0 XP
?
API REST et Backend avec Laravel/Modèles Eloquent et base de données
Intermédiaire60 min40 XP

Objectifs de cette leçon

  • Manipuler les modèles avec Eloquent ORM
  • Effectuer des requêtes simples et complexes
  • Comprendre les relations entre modèles

Modèles Eloquent et base de données 🗄️

Qu'est-ce qu'un ORM ?

Un ORM (Object-Relational Mapping) est un outil qui permet d'interagir avec une base de données relationnelle en utilisant des objets PHP, sans écrire de SQL.

💡 Au lieu d'écrire SELECT * FROM users, tu écris User::all(). Laravel traduit automatiquement ton PHP en SQL optimisé.

Eloquent est l'ORM de Laravel. Il est :

  • Actif : chaque modèle correspond à une table
  • Expressif : le code PHP est lisible et naturel
  • Sécurisé : protège contre les injections SQL

Définition d'un modèle 🗄️

php artisan make:model User
<?php

namespace AppModels;

use IlluminateDatabaseEloquentModel;

class User extends Model
{
    // 👇 Champs modifiables en masse (mass assignment)
    protected $fillable = ['name', 'email', 'password'];

    // 👇 Champs cachés dans les réponses JSON
    protected $hidden = ['password', 'remember_token'];

    // 👇 Conversion automatique des types
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

Convention de nommage

Table (BDD)Modèle (PHP)
usersUser
postsPost
password_reset_tokensPasswordResetToken
flight_bookingsFlightBooking

⚠️ Laravel déduit automatiquement le nom de la table à partir du nom du modèle (snake_case, pluriel). Tu peux le surcharger avec protected $table = 'ma_table';.


Les migrations — versionner la BDD 📋

Créer une migration

php artisan make:migration create_posts_table

Structure d'une migration

<?php

use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title', 255);
            $table->text('body');
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->timestamps(); // created_at + updated_at
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

Types de colonnes disponibles

MéthodeType SQL
$table->id()BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY
$table->string('name', 100)VARCHAR(100)
$table->text('body')TEXT
$table->integer('age')INT
$table->boolean('is_admin')TINYINT(1)
$table->foreignId('user_id')BIGINT UNSIGNED (clé étrangère)
$table->timestamps()created_at + updated_at TIMESTAMP
$table->softDeletes()deleted_at TIMESTAMP NULL

Exécuter les migrations

php artisan migrate            # Applique toutes les migrations
php artisan migrate:rollback   # Annule la dernière lot
php artisan migrate:fresh      # Supprime tout et réapplique

Opérations CRUD avec Eloquent

CREATE — Créer un enregistrement ➕

// Méthode 1 : create() (mass assignment)
$user = User::create([
    'name' => 'Alice',
    'email' => 'alice@example.com',
    'password' => bcrypt('secret'),
]);

// Méthode 2 : new + save
$user = new User();
$user->name = 'Bob';
$user->email = 'bob@example.com';
$user->password = bcrypt('secret');
$user->save();

READ — Lire des enregistrements 📖

// Tous les utilisateurs
$users = User::all();                    // SELECT * FROM users

// Un utilisateur par ID
$user = User::find(1);                   // SELECT * FROM users WHERE id = 1

// Avec condition
$users = User::where('age', '>', 18)->get();       // SELECT * FROM users WHERE age > 18

// Premier résultat
$user = User::where('email', 'alice@example.com')->first();

// Ordonné et limité
$users = User::orderBy('name')->take(10)->get();   // SELECT * FROM users ORDER BY name LIMIT 10

// Pagination
$users = User::paginate(15);

UPDATE — Modifier un enregistrement ✏️

// Méthode 1 : update()
$user = User::find(1);
$user->update(['name' => 'Alex']);       // UPDATE users SET name = 'Alex' WHERE id = 1

// Méthode 2 : assignation + save()
$user = User::find(1);
$user->name = 'Alex';
$user->save();

DELETE — Supprimer un enregistrement 🗑️

// Suppression définitive
$user = User::find(1);
$user->delete();                         // DELETE FROM users WHERE id = 1

// Suppression par condition
User::where('age', '<', 13)->delete();

Le Query Builder 🔍

Parfois, tu as besoin de requêtes plus complexes. Laravel propose un query builder fluide :

use IlluminateSupportFacadesDB;

$users = DB::table('users')
    ->select('users.*', 'posts.title as last_post')
    ->leftJoin('posts', 'users.id', '=', 'posts.user_id')
    ->where('users.active', true)
    ->whereIn('users.role', ['admin', 'editor'])
    ->orderBy('users.name')
    ->paginate(20);

💡 Le query builder est aussi sécurisé qu'Eloquent. Utilise-le pour les requêtes complexes qui ne correspondent pas à un modèle.


Relations entre modèles 🔗

Eloquent gère les relations sans écrire de JOINs.

hasMany (1 → N)

class User extends Model
{
    public function posts()
    {
        return $this->hasMany(Post::class);
        // SELECT * FROM posts WHERE user_id = ?
    }
}

// Utilisation
$posts = $user->posts;    // Collection des posts

belongsTo (N → 1)

class Post extends Model
{
    public function author()
    {
        return $this->belongsTo(User::class, 'user_id');
        // SELECT * FROM users WHERE id = ?
    }
}

// Utilisation
$author = $post->author;  // Un seul utilisateur

belongsToMany (N ↔ N)

class User extends Model
{
    public function roles()
    {
        return $this->belongsToMany(Role::class);
        // SELECT * FROM roles
        // INNER JOIN role_user ON roles.id = role_user.role_id
        // WHERE role_user.user_id = ?
    }
}

Bonnes pratiques ✅

  1. Toujours définir $fillable ou $guarded pour la sécurité (mass assignment)
  2. Utiliser les scopes pour les requêtes réutilisables : User::active()->admins()->get()
  3. Préférer findOrFail() plutôt que find() + vérification manuelle
  4. Utiliser l'eager loading pour éviter le problème N+1 : User::with('posts')->get()
  5. Nommer les tables en snake_case pluriel et les clés étrangères model_id
  6. Toujours écrire down() dans les migrations pour pouvoir rollback

Pièges fréquents 🚨

PiègeSolution
❌ Oublier $fillableImpossible de faire create() — erreur MassAssignmentException
❌ Problème N+1Utilise with() pour charger les relations en une seule requête
❌ Ne pas caster les datesLaravel convertit automatiquement les timestamps en Carbon
❌ Ignorer down() dans les migrationsImpossible de rollback proprement
❌ Faire confiance aux entrées utilisateurToujours valider avec $request->validate() avant create()

Exercices pour toi 🎯

  1. Migration : Crée une migration create_categories_table avec id, name (string 100), slug (string unique), timestamps.
  2. Modèle : Crée le modèle Category correspondant. Définis $fillable.
  3. Requêtes : Écris une requête Eloquent pour récupérer les 5 dernières catégories triées par nom.
  4. Relation : Ajoute une relation hasMany de Category vers Post. Ajoute foreignId('category_id') dans la migration des posts.
  5. Défi : Ouvre le simulateur ci-dessous. Clique sur chaque opération pour voir le PHP et le SQL généré côte à côte.

⚡ Simulateur — Eloquent ORM

Étape 1/8
📝 Pseudo-code
1# Eloquent ORM — interagir avec la BDD en PHP
2class User extends Model {} # ⬅ définition du modèle
3Schema::create('users', function(Blueprint $table) { # ⬅ migration
4 $table->id(); $table->string('name'); $table->timestamps();
5});
6User::create(['name' => 'Alice', 'email' => 'alice@ex.com']) # ⬅ CREATE
7User::all() # ⬅ READ : tous les users
8User::where('age', '>', 18)->get() # ⬅ READ : avec condition
9$user = User::find(1); $user->update(['name' => 'Alex']) # ⬅ UPDATE
10$user->delete() # ⬅ DELETE
11$user->posts; // Relation hasMany # ⬅ RELATIONS
🗄️ Eloquent ORM → SQL
📌 Opérations Eloquent
🗄️ Modèle📋 Migration CREATE📖 READ🔍 WHERE✏️ UPDATE🗑️ DELETE🔗 Relations
Définition du modèle
🐘 PHP / Eloquent
class User extends Model { protected $fillable = ['name', 'email']; }
⬇ traduit en ⬇
🗄️ SQL généré
— pas de SQL direct —
Chaque table de la BDD a un modèle Eloquent correspondant. Par convention, la table 'users' est associée au modèle User.
💡 Points clés d'Eloquent
  • Chaque table = un modèle PHP qui hérite de Model
  • Les migrations versionnent le schéma de la BDD
  • create() insère, update() modifie, delete() supprime
  • Le query builder protège contre les injections SQL
  • Les relations (hasMany, belongsTo...) lient les tables

Un modèle Eloquent hérite de la classe Model de Laravel. Le nom de la table est déduit automatiquement (snake_case, pluriel).