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 écrisUser::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) |
|---|---|
users | User |
posts | Post |
password_reset_tokens | PasswordResetToken |
flight_bookings | FlightBooking |
⚠️ 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éthode | Type 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 ✅
- Toujours définir
$fillableou$guardedpour la sécurité (mass assignment) - Utiliser les scopes pour les requêtes réutilisables :
User::active()->admins()->get() - Préférer
findOrFail()plutôt quefind()+ vérification manuelle - Utiliser l'eager loading pour éviter le problème N+1 :
User::with('posts')->get() - Nommer les tables en snake_case pluriel et les clés étrangères
model_id - Toujours écrire
down()dans les migrations pour pouvoir rollback
Pièges fréquents 🚨
| Piège | Solution |
|---|---|
❌ Oublier $fillable | Impossible de faire create() — erreur MassAssignmentException |
| ❌ Problème N+1 | Utilise with() pour charger les relations en une seule requête |
| ❌ Ne pas caster les dates | Laravel convertit automatiquement les timestamps en Carbon |
❌ Ignorer down() dans les migrations | Impossible de rollback proprement |
| ❌ Faire confiance aux entrées utilisateur | Toujours valider avec $request->validate() avant create() |
Exercices pour toi 🎯
- Migration : Crée une migration
create_categories_tableavecid,name(string 100),slug(string unique),timestamps. - Modèle : Crée le modèle
Categorycorrespondant. Définis$fillable. - Requêtes : Écris une requête Eloquent pour récupérer les 5 dernières catégories triées par nom.
- Relation : Ajoute une relation
hasManydeCategoryversPost. AjouteforeignId('category_id')dans la migration des posts. - 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/8class User extends Model { protected $fillable = ['name', 'email']; }— pas de SQL direct —- 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).