Model
Les Modèles dans Laravel
À quoi servent les modèles?
Dans le cadre du développement d'applications avec Laravel, les modèles jouent un rôle crucial en tant que partie intégrante de l'architecture MVC (Modèle-Vue-Contrôleur). Le modèle est responsable de la gestion des données et des règles métier de l'application. Il communique directement avec la base de données et sert d'intermédiaire entre la base de données et les contrôleurs.
- Représentation des données : Chaque modèle représente une table dans la base de données, où chaque instance du modèle correspond à une ligne de cette table.
- Abstraction des opérations : Les modèles permettent d'effectuer des opérations CRUD (Création, Lecture, Mise à jour, Suppression) sans avoir à écrire de requêtes SQL complexes.
Utilisation d'Eloquent au lieu de PDO
Laravel propose une approche simplifiée pour interagir avec les bases de données grâce à Eloquent, son ORM (Object-Relational Mapping) intégré. Eloquent permet aux développeurs de travailler avec les données de la base comme s'ils manipulaient des objets PHP.
- Abstraction et simplicité : Eloquent élimine le besoin d'écrire des requêtes SQL manuellement, ce qui réduit la complexité du code et les erreurs potentielles.
- Opérations intuitives : Avec Eloquent, les opérations de base de données sont effectuées via des méthodes simples et expressives.
Comprendre l'ORM
Un ORM est un outil qui crée un "pont" entre les objets du langage de programmation (dans notre cas, PHP) et les tables de base de données. Il permet aux développeurs de manipuler les données de la base de données en utilisant des classes et des objets, au lieu de requêtes SQL.
- Eloquent : C'est l'ORM par défaut de Laravel qui suit le pattern Active Record, permettant à chaque modèle de correspondre à une table dans la base de données et à chaque instance de modèle à une ligne dans cette table.
Exemples de code avec un modèle User
Voici un exemple simple de la façon dont un modèle User peut être utilisé dans Laravel :
// Création d'un nouvel utilisateur
$user = new User();
$user->name = 'John Doe';
$user->email = 'john@example.com';
$user->save();
// Lecture d'un utilisateur par son id
$user = User::find(1);
// Mise à jour d'un utilisateur
$user->email = 'john.doe@example.com';
$user->save();
// Suppression d'un utilisateur
$user->delete();
Ces opérations montrent comment les modèles Eloquent permettent de manipuler les données de manière élégante et efficace.
Création d'un modèle avec Artisan
Laravel utilise une interface en ligne de commande appelée Artisan qui facilite diverses tâches de développement, y compris la création de modèles. Pour créer un modèle, vous utilisez la commande suivante :
php artisan make:model Animal
Cette commande crée un fichier de modèle dans le répertoire app/Models.
Exemple de fichier de modèle créé
Voici à quoi ressemble le fichier de modèle Animal créé par la commande Artisan :
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Animal extends Model
{
use HasFactory;
// Vous pouvez ajouter des propriétés et des méthodes personnalisées ici
}
Explication du contenu du modèle
- Namespace : Le modèle appartient à l'espace de noms
App\Models, ce qui aide à organiser le code de l'application. - Base Class : Le modèle
Animalétend la classeModelde Eloquent, ce qui lui permet d'hériter de toutes les fonctionnalités ORM d'Eloquent. - Trait
HasFactory: Ce trait est utilisé pour intégrer la capacité de générer des instances de modèle via des factories, utile pour les tests ou le seeding de votre base de données.
Personnalisation du modèle
Vous pouvez personnaliser le modèle en ajoutant des propriétés telles que $fillable ou $guarded pour gérer l'assignation en masse, ou en définissant des relations comme belongsTo ou hasMany pour décrire les relations entre les tables.
class Animal extends Model
{
use HasFactory;
protected $fillable = ['name', 'type', 'age'];
public function owner()
{
return $this->belongsTo(Owner::class);
}
}
Dans cet exemple, le modèle Animal peut maintenant avoir des champs name, type, et age sécurisés pour l'assignation en masse, et il est lié à un modèle Owner via une relation belongsTo.
Cette structure vous permet de commencer à travailler avec des données de manière orientée objet très rapidement et efficacement dans votre application Laravel.
Système de migration
Le système de migration de Laravel permet de définir la structure de la base de données de l'application en PHP, ce qui facilite le partage et la mise à jour de la structure de la base de données.
- Création de migrations : Laravel utilise des migrations pour créer et modifier les tables de la base de données.
- Exemple de migration pour la table
users:
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('users');
}
}
Cette migration crée une nouvelle table users avec quelques champs de base comme id, name, email et timestamps.
Méthodes Eloquent utiles
Sélection de données
Eloquent fournit une variété de méthodes pour récupérer et filtrer les données de manière simple et expressive. Par exemple, pour récupérer tous les utilisateurs d'une base de données, on peut simplement utiliser :
$users = User::all();
Pour des requêtes plus spécifiques, Eloquent permet d'utiliser des méthodes comme find, where, orderBy, permettant une grande flexibilité. Par exemple, pour trouver un utilisateur par son email :
$user = User::where('email', 'john@example.com')->first();
Mise à jour de données
La mise à jour des données est également simplifiée avec Eloquent. Après avoir récupéré un modèle, on peut modifier ses propriétés et le sauvegarder :
$user = User::find(1);
$user->email = 'new.email@example.com';
$user->save();
Pour les mises à jour en masse, Laravel fournit la méthode update qui peut être utilisée directement sur une requête :
User::where('active', 0)->update(['active' => 1]);
Suppression de données
La suppression de modèles est tout aussi intuitive. Pour supprimer un modèle, il suffit d'appeler la méthode delete sur une instance de modèle :
$user = User::find(1);
$user->delete();
Pour une suppression en masse, on peut combiner where avec delete :
User::where('active', 0)->delete();
Relations entre les modèles
Types de relations
Laravel Eloquent supporte plusieurs types de relations qui reflètent les relations entre les tables dans une base de données. Comprendre ces relations est essentiel pour modéliser efficacement les interactions entre les données.
- hasOne : Utilisée pour indiquer qu'un modèle possède une relation un-à-un avec un autre modèle. Par exemple, un utilisateur a une adresse.
- belongsTo : L'inverse de
hasOneethasMany. Elle est utilisée pour indiquer qu'un modèle appartient à un autre modèle. Par exemple, une adresse appartient à un utilisateur. - hasMany : Indique qu'un modèle possède une relation un-à-plusieurs avec un autre modèle. Par exemple, un utilisateur peut avoir plusieurs articles de blog.
- belongsToMany : Utilisée pour les relations plusieurs-à-plusieurs. Par exemple, un utilisateur peut avoir plusieurs rôles et un rôle peut être attribué à plusieurs utilisateurs.
hasOne
Création dans la base de données avec migrations Laravel
Pour définir une relation hasOne entre User et Profile, voici comment vous pourriez structurer vos migrations :
// Migration pour la table 'users'
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});
// Migration pour la table 'profiles'
Schema::create('profiles', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->text('bio');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
On peut aussi écrire la migration de la table profiles comme ceci :
Schema::create('profiles', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->string('bio');
$table->timestamps();
});
On peut ajouter la clé étrangère ainsi que les contraintes d’intégrité en une seule ligne.
Définition de la relation dans la classe User
Pour exploiter la relation hasOne, ajoutez la méthode suivante dans la classe User :
public function profile() {
return $this->hasOne(Profile::class);
}
Simple : Récupération des données d’un utilisateur et de son profil.
$user = User::with('profile')->find(1);
Ajout d'un profil à un utilisateur : Pour créer un nouveau profil et l'associer à un utilisateur existant.
$user = User::find(1);
$profile = new Profile(['bio' => 'Développeur Laravel passionné']);
$user->profile()->save($profile);
Avancée : Chargement des données d'un utilisateur avec une condition spécifique sur le profil.
$user = User::whereHas('profile', function($query) {
$query->where('bio', 'like', '%Laravel%');
})->first();
Création d’un utilisateur et de son profil simultanément : Pour créer un utilisateur et son profil :
$user = User::create(['name' => 'Thibault', 'email' => 'thibault@example.com']);
$profile = new Profile(['bio' => 'Enseignant en informatique et développeur Laravel']);
$user->profile()->save($profile);
belongsTo
C’est la relation inverse de hasOne définie dans le modèle User.
Le profil appartient à l’utilisateur.
Création dans la base de données avec migrations Laravel
La clé étrangère est déjà définie dans la migration de la table profiles.
Définition de la relation dans la classe Profile
Pour exploiter la relation belongsTo, ajoutez la méthode suivante dans la classe Profile :
public function user() {
return $this->belongsTo(User::class);
}
Requêtes avec belongsTo et Manipulation de Données
Simple : Récupérer l'utilisateur associé à un profil.
$profile = Profile::find(1);
$user = $profile->user;
Ajout d'un nouvel objet à la relation : Créer un nouvel utilisateur et l'associer à un profil.
$user = new User(['name' => 'Nouvel Utilisateur', 'email' => 'email@example.com']);
$profile = Profile::find(1);
$profile->user()->associate($user);
$profile->save();
Avancée : Charger tous les profils avec leur utilisateur respectif, en filtrant les utilisateurs selon un critère.
$profiles = Profile::whereHas('user', function($query) {
$query->where('name', 'like', '%John%');
})->get();
Création d'un profil et association à un utilisateur existant : Supposons que nous avons un utilisateur existant auquel nous voulons associer un nouveau profil.
$user = User::find(1); // Supposons que cet utilisateur est déjà existant
$profile = new Profile(['bio' => 'Biographie du profil', 'birthdate' => '2000-01-01']);
$user->profile()->save($profile);
hasMany
Création dans la base de données avec migrations Laravel
La relation hasMany relie un modèle à plusieurs instances d'un autre modèle. Par exemple, un utilisateur (User) peut avoir plusieurs posts (Post). Voici comment créer une migration pour la table posts :
// Migration pour la table 'posts'
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->string('title');
$table->text('body');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
Définition des relations dans les modèles
Pour compléter les migrations et établir les relations entre nos modèles User et Post dans Laravel, voici comment procéder :
Dans le modèle User, ajoutez la méthode suivante pour définir la relation hasMany avec le modèle Post :
// Dans le modèle User
public function posts() {
return $this->hasMany(Post::class);
}
Cette méthode signifie qu'un utilisateur peut avoir plusieurs posts. Elle utilise la méthode hasMany de Eloquent, le système de gestion de base de données ORM de Laravel, pour établir cette relation.
D'autre part, dans le modèle Post, vous devez définir la relation inverse belongsTo pour spécifier qu'un post appartient à un utilisateur :
// Dans le modèle Post
public function user() {
return $this->belongsTo(User::class);
}
Cette méthode établit qu'un post est associé à un seul utilisateur. En utilisant belongsTo, on crée une relation inverse, permettant de naviguer de chaque post à son utilisateur propriétaire.
Requêtes avec hasMany
Simple : Récupérer tous les posts d'un utilisateur.
$user = User::find(1);
$posts = $user->posts;
Ajout d'un nouvel objet à la relation : Créer un nouveau post et l'associer à un utilisateur.
$post = new Post(['title' => 'Mon nouveau Post', 'body' => 'Contenu du post']);
$user = User::find(1);
$user->posts()->save($post);
Avancée : Récupérer tous les utilisateurs avec leurs posts, en filtrant les posts selon un critère.
$users = User::with(['posts' => function($query) {
$query->where('title', 'like', '%Laravel%');
}])->get();
Création de plusieurs posts et association à un utilisateur existant : Pour créer plusieurs posts et les associer à un utilisateur existant, on peut procéder comme suit :
$user = User::find(1); // Supposons que cet utilisateur est déjà existant
$posts = [
new Post(['title' => 'Post 1', 'body' => 'Contenu du post 1']),
new Post(['title' => 'Post 2', 'body' => 'Contenu du post 2'])
];
$user->posts()->saveMany($posts);
Gestion des relations many-to-many avec Laravel
Mise en place dans la base de données
La relation belongsToMany, illustrée par la liaison entre User et Role, nécessite une table intermédiaire, souvent nommée table pivot. Voici comment la créer grâce aux migrations Laravel :
// Création de la table pivot 'role_user'
Schema::create('role_user', function (Blueprint $table) {
$table->unsignedBigInteger('user_id'); // Clé étrangère pour User
$table->unsignedBigInteger('role_id'); // Clé étrangère pour Role
// Déclaration des contraintes de clé étrangère
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
// Définition de la clé primaire composée
$table->primary(['user_id', 'role_id']);
});
Création et association de modèles
Pour exploiter pleinement la relation belongsToMany, il faut définir cette relation dans les modèles concernés. Voici comment procéder :
- Modèle User : Intégrer une méthode
rolesretournant la relation.
public function roles() {
return $this->belongsToMany(Role::class);
}
- Modèle Role : Ajouter une méthode similaire pour accéder aux utilisateurs associés.
public function users() {
return $this->belongsToMany(User::class);
}
Pour associer un rôle à un utilisateur (et inversement), Laravel offre des méthodes telles que attach, detach et sync permettant de manipuler facilement les enregistrements dans la table pivot.
- Attachement d'un rôle à un utilisateur :
$user->roles()->attach($roleId); // Associe un rôle par son ID
- Détachement d'un rôle :
$user->roles()->detach($roleId); // Supprime l'association avec le rôle
- Synchronisation des rôles :
$user->roles()->sync([$roleId1, $roleId2]); // Synchronise les rôles, en supprimant les anciennes associations et en ajoutant les nouvelles.
Ces méthodes facilitent la gestion dynamique des relations entre les modèles dans des applications complexes.
Exploitation de la relation belongsToMany
- Basique : Pour récupérer les rôles associés à un utilisateur donné :
$user = User::find(1); // Trouve l'utilisateur avec l'ID 1
$roles = $user->roles; // Récupère les rôles liés
- Avancé : Pour trouver les utilisateurs ayant le rôle "Admin" :
$users = User::whereHas('roles', function($query) {
$query->where('name', 'Admin'); // Filtre sur le nom du rôle
})->get();
Relations polymorphiques
Création dans la base de données avec migrations Laravel
Les relations polymorphiques permettent à un modèle d'appartenir à plus d'un autre modèle sur une seule association. Par exemple, si vous avez des Comment pouvant appartenir à Post ou Video, vous structurerez vos migrations comme suit :
// Migration pour la table 'comments'
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->string('body');
$table->morphs('commentable'); // Cela crée 'commentable_type' et 'commentable_id'
$table->timestamps();
});
Notation dans les modèles
Vous devez définir la relation dans les modèles Comment, Post, et Video :
// Dans le modèle Comment
class Comment extends Model
{
public function commentable()
{
return $this->morphTo();
}
}
// Dans les modèles Post et Video
class Post extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
class Video extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
Requêtes avec relations polymorphiques
Simple : Accéder aux commentaires d'un post.
$post = Post::find(1);
$comments = $post->comments;
Avancée : Charger tous les commentaires pour un type spécifique de "commentable" avec un critère spécifique.
$comments = Comment::where('commentable_type', Post::class)
->where('body', 'like', '%Laravel%')
->get();
Exemples de requêtes avancées
Pour approfondir l'utilisation des relations dans Laravel, voici quelques exemples de requêtes avancées qui montrent la puissance et la flexibilité d'Eloquent.
Filtrage avec des relations profondes
Imaginons que vous voulez récupérer tous les utilisateurs qui ont posté des commentaires contenant le mot "Laravel" sur des posts. Vous pouvez utiliser la méthode whereHas de manière imbriquée :
$users = User::whereHas('posts.comments', function ($query) {
$query->where('body', 'like', '%Laravel%');
})->get();
Factories et Seeders
Pour tester efficacement une application, il est souvent nécessaire de générer des données factices. Laravel fournit deux outils puissants pour cela : les Factories et les Seeders.
Factories
Les Factories permettent de définir des modèles de données pour générer automatiquement des enregistrements de test. Elles utilisent la bibliothèque Faker pour générer des données réalistes.
Création d'une Factory
Pour créer une factory pour le modèle User, utilisez la commande Artisan :
php artisan make:factory UserFactory --model=User
Cela crée un fichier dans database/factories où vous pouvez définir la structure de vos données factices :
use Faker\Generator as Faker;
$factory->define(User::class, function (Faker $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'password' => bcrypt('secret'), // ou Hash::make('secret')
];
});
Utilisation d'une Factory
Pour générer une instance de modèle sans la persister en base de données :
$user = User::factory()->make();
Pour créer une instance de modèle et la persister :
$user = User::factory()->create();
Seeders
Les Seeders permettent de peupler votre base de données avec des données de test en utilisant les factories ou en insérant directement des données.
Création d'un Seeder
Pour créer un seeder pour le modèle User, utilisez la commande Artisan :
php artisan make:seeder UsersTableSeeder
Dans le fichier créé dans database/seeders, vous pouvez définir comment peupler votre table :
use Illuminate\Database\Seeder;
class UsersTableSeeder extends Seeder
{
public function run()
{
// Créer 10 utilisateurs
User::factory(10)->create();
}
}
Exécution des Seeders
Pour exécuter un seeder particulier :
php artisan db:seed --class=UsersTableSeeder
Ou pour exécuter tous les seeders définis :
php artisan db:seed
Pour détruire la base de données, la recréer et la peupler :
php artisan migrate:fresh --seed
Exemples de requêtes avancées avec Factories et Seeders
Relations avec Factories
Si vous avez des relations entre vos modèles, par exemple, un utilisateur ayant plusieurs posts, vous pouvez définir cela dans vos factories :
$factory->define(Post::class, function (Faker $faker) {
return [
'user_id' => User::factory(),
'title' => $faker->sentence,
'body' => $faker->paragraph,
];
});
Et dans vos seeders, vous pouvez utiliser les relations pour créer des données cohérentes :
$user = User::factory()->create();
$posts = Post::factory(5)->create(['user_id' => $user->id]);
Ces outils offrent une flexibilité incroyable pour générer des données de test, permettant de tester efficacement votre application dans divers scénarios.
Bonnes pratiques avec Eloquent
L'utilisation efficace d'Eloquent dans Laravel n'est pas seulement une question de savoir comment créer des modèles ou exécuter des requêtes. Il est également crucial d'adopter de bonnes pratiques pour assurer la sécurité, les performances et la maintenabilité de votre application. Voici quelques conseils essentiels :
Gestion de l'Assignation Massive
L'Assignation Massive représente une méthode efficace pour attribuer simultanément plusieurs propriétés à un modèle. Toutefois, elle requiert une manipulation prudente pour éviter les vulnérabilités de sécurité.
Configuration des Attributs Autorisés
Afin de se prémunir contre les risques d'assignation massive indésirable, il est crucial de spécifier clairement quels attributs peuvent être assignés de cette manière dans le modèle. Ceci se fait comme suit :
protected $fillable = ['nom', 'email', 'mot_de_passe'];
Exemples d'Utilisation
Création d'un nouvel enregistrement :
Lors de l'utilisation de la méthode create, Laravel permet d'insérer un nouvel enregistrement dans la base de données en utilisant les attributs définis comme fillable.
$user = User::create([
'nom' => 'Jean Dupont',
'email' => 'jean.dupont@example.com',
'mot_de_passe' => Hash::make('password123'),
]);
Mise à jour d'un enregistrement existant :
Pour la mise à jour, la méthode update permet de modifier plusieurs attributs d'un modèle existant en une seule fois, en respectant toujours les règles définies par fillable.
$user->update([
'nom' => 'Jean Dupont',
'email' => 'jean.nouveau@example.com',
]);
Ces exemples démontrent comment manipuler l'assignation massive de manière sécurisée, en prenant soin de définir précisément quels attributs peuvent être assignés ou modifiés de cette façon.
Conventions de nommage
Respecter les conventions de nommage de Laravel facilite la compréhension du code par d'autres développeurs et par vous-même dans le futur.
- Modèles : Singulier avec la première lettre en majuscule (ex.
User,Post). - Tables de base de données : Pluriel et en minuscules (ex.
users,posts). - Clés étrangères : Nom du modèle en singulier suivi de
_id(ex.user_id,post_id).
Sécurisation des requêtes
Eloquent protège contre les injections SQL grâce à la préparation des requêtes. Cependant, il est toujours important d'être vigilant, surtout lorsque vous utilisez des requêtes SQL brutes ou des clauses whereRaw.
Validation des entrées
Toujours valider les données entrantes, surtout si elles sont utilisées dans des requêtes SQL, pour éviter toute manipulation malveillante :
$request->validate([
'email' => 'required|email',
'password' => 'required|min:8',
]);
Optimisation des requêtes : Éliminer les N+1 queries
Les N+1 queries désignent une inefficacité courante en base de données, spécialement avec les ORM comme Eloquent. Imaginez que vous avez une liste d'utilisateurs et que pour chaque utilisateur, vous souhaitez récupérer ses publications. Sans optimisation, vous lancez une première requête pour obtenir tous les utilisateurs, puis, pour chaque utilisateur, une requête supplémentaire pour ses publications. Si vous avez 10 utilisateurs, cela équivaut à 1 (pour les utilisateurs) + 10 (une pour chaque utilisateur pour ses publications) = 11 requêtes au total.
Solution : Le eager loading
Pour contourner ce piège, Laravel propose le eager loading avec la méthode with, permettant de récupérer toutes les données en une fois :
// Requête inefficace : N+1 queries
$users = User::all();
foreach ($users as $user) {
$posts = $user->posts; // Une requête par utilisateur
}
// Requête optimisée avec chargement eager
$users = User::with('posts')->get(); // Une seule requête pour tout récupérer
Cette approche réduit considérablement le nombre de requêtes, améliorant la performance de votre application.
Conclusion
En adoptant ces bonnes pratiques, vous maximiserez les avantages d'utiliser Eloquent dans vos projets Laravel. Cela contribuera non seulement à la sécurité et aux performances de vos applications mais aussi à leur maintenabilité sur le long terme.
Création complète avec Artisan
Laravel permet de simplifier le processus de développement en offrant la possibilité de créer simultanément un modèle, sa migration associée, un seeder, une factory, et un contrôleur de type resource. Cette fonctionnalité est particulièrement utile pour mettre en place rapidement les composants nécessaires à une partie de votre application.
Commande d'exemple
Pour créer tous ces éléments en une seule commande, vous pouvez utiliser la commande make:model d'Artisan avec plusieurs options :
php artisan make:model Animal --all
Ou, si vous préférez spécifier chaque composant individuellement :
php artisan make:model Animal -mfsr
Options disponibles
Voici les options que vous pouvez utiliser avec la commande make:model :
-m,--migration: Crée une migration pour le modèle.-f,--factory: Crée une factory pour le modèle.-s,--seed: Crée un seeder pour le modèle.-c,--controller: Crée un contrôleur pour le modèle.-r,--resource: Indique que le contrôleur généré doit être un contrôleur de ressource, qui inclut des méthodes pour les opérations CRUD (Create, Read, Update, Delete).--api: Similaire à--resource, mais le contrôleur sera optimisé pour une API, excluant les méthodes qui ne sont pas nécessaires comme celles pour afficher des vues.
Ces outils combinés offrent une base solide pour développer une partie fonctionnelle de votre application, en respectant les conventions de Laravel et en accélérant le processus de développement initial.
