Un guide pratique basé sur une vraie application : BeeNote, app de suivi de ruchers
🎯 Qu’est-ce qu’un Modèle Laravel ?
Un modèle Eloquent est votre pont intelligent entre votre code PHP et votre base de données. Pensez-y comme à un traducteur qui :
- Transforme vos lignes SQL en objets PHP manipulables
- Gère automatiquement les relations entre vos entités
- Offre des méthodes pour valider, sauvegarder et rechercher vos données
Analogie simple : Si votre base de données est une bibliothèque, le modèle est le bibliothécaire qui sait exactement où trouver chaque livre et comment les organiser.
🏗️ Structure de Base d’un Modèle
Exemple : Modèle Rucher
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Rucher extends Model
{
use HasFactory;
// 🔒 Sécurité : Champs modifiables en masse
protected $fillable = [
'nom',
'localisation',
'latitude',
'longitude',
'description'
];
// 🔗 Relations : Un rucher appartient à un utilisateur
public function user()
{
return $this->belongsTo(User::class);
}
// 🔗 Relations : Un rucher a plusieurs ruches
public function ruches()
{
return $this->hasMany(Ruche::class);
}
}
🛡️ Mass Assignment et Sécurité
Le Problème
Sans protection, un pirate pourrait envoyer :
fetch('/ruchers', {
method: 'POST',
body: JSON.stringify({
nom: "Mon Rucher",
user_id: 999, // 🚨 Voler le rucher d'un autre !
created_at: "2020-01-01" // 🚨 Falsifier la date
})
})
La Solution : $fillable
protected $fillable = [
'nom', // ✅ L'utilisateur peut le définir
'localisation', // ✅ L'utilisateur peut le définir
'latitude', // ✅ L'utilisateur peut le définir
'longitude', // ✅ L'utilisateur peut le définir
'description', // ✅ L'utilisateur peut le définir
];
// ❌ ABSENTS de $fillable = PROTÉGÉS :
// 'user_id' -> Défini par l'authentification
// 'id' -> Défini par la base de données
// 'created_at' -> Défini automatiquement
Utilisation Sécurisée
// ✅ SÉCURISÉ
Rucher::create([
'nom' => $request->nom,
'localisation' => $request->localisation,
'user_id' => auth()->id(), // 🔒 Utilisateur connecté
]);
// ✅ ENCORE PLUS ÉLÉGANT
auth()->user()->ruchers()->create($request->only(['nom', 'localisation', ...]));
🔗 Relations Eloquent Maîtrisées
Relations de Base
belongsTo (Appartient à)
// Une ruche appartient à un rucher
public function rucher()
{
return $this->belongsTo(Rucher::class);
}
// Usage
$ruche = Ruche::find(1);
$rucher = $ruche->rucher; // Récupère le rucher parent
hasMany (A plusieurs)
// Un rucher a plusieurs ruches
public function ruches()
{
return $this->hasMany(Ruche::class);
}
// Usage
$rucher = Rucher::find(1);
$ruches = $rucher->ruches; // Collection de ruches
Relations Avancées
hasOneThrough (Relation indirecte)
// Une ruche → son propriétaire (via le rucher)
public function user()
{
return $this->hasOneThrough(User::class, Rucher::class);
}
// SQL généré : une seule requête avec JOIN !
// SELECT users.* FROM users
// INNER JOIN ruchers ON users.id = ruchers.user_id
// WHERE ruchers.id = ruches.rucher_id
Relations Polymorphiques
// Modèle Photo : une photo peut appartenir à N'IMPORTE QUOI
public function photoable()
{
return $this->morphTo();
}
// Modèle Visite : une visite peut avoir plusieurs photos
public function photos()
{
return $this->morphMany(Photo::class, 'photoable');
}
🎨 Relations Polymorphiques en Détail
Le Problème Résolu
Sans polymorphique (approche naïve) :
photos_visites: photo_id, visite_id
photos_ruches: photo_id, ruche_id
photos_recoltes: photo_id, recolte_id
-- Une table par relation !
Avec polymorphique :
photos: id, photoable_type, photoable_id, chemin, titre
-- UNE SEULE table !
Exemple Concret
Données dans la table photos
:
id | photoable_type | photoable_id | chemin | titre |
---|---|---|---|---|
1 | App\Models\Visite | 5 | visite_123.jpg | Cadres de couvain |
2 | App\Models\Ruche | 12 | ruche_456.jpg | Ma ruche bleue |
3 | App\Models\Recolte | 8 | miel_789.jpg | Récolte d’acacia |
Utilisation Magique
// Ajouter une photo à une visite
$visite = Visite::find(5);
$visite->photos()->create([
'chemin' => 'nouvelle_photo.jpg',
'titre' => 'État de la ruche'
]);
// Laravel ajoute automatiquement :
// photoable_type = 'App\Models\Visite'
// photoable_id = 5
// Récupérer l'objet parent d'une photo
$photo = Photo::find(2);
$ruche = $photo->photoable; // Retourne l'objet Ruche !
echo $ruche->nom; // "Ma ruche bleue"
🏭 Traits et Factories
Traits : Boîtes à Outils Réutilisables
class User extends Authenticatable
{
use HasApiTokens; // Méthodes pour gérer les tokens API
use HasFactory; // Méthodes pour créer des faux users
use HasProfilePhoto; // Méthodes pour gérer les photos
use Notifiable; // Méthodes pour les notifications
}
HasFactory : Créateur de Fausses Données
// Factory = recette pour créer des faux ruchers
// database/factories/RucherFactory.php
return [
'nom' => fake()->randomElement(['Rucher des Acacias', 'Rucher du Pré Vert']),
'localisation' => fake()->city() . ', France',
'latitude' => fake()->latitude(46, 48),
'longitude' => fake()->longitude(-1, 1),
];
// Utilisation
$rucher = Rucher::factory()->create(); // Un rucher fake
$ruchers = Rucher::factory()->count(10)->create(); // 10 ruchers fake
📊 Architecture de Données Réelle
Exemple : Application BeeNote
User (apiculteur)
└── Ruchers (propriétés)
└── Ruches (colonies d'abeilles)
├── Visites (inspections)
├── Récoltes (miel récolté)
├── Traitements (soins)
└── Photos (documentation)
Modèle Complexe : Visite (47 champs)
class Visite extends Model
{
protected $fillable = [
// Informations temporelles
'date_visite', 'heure_debut', 'heure_fin', 'duree_visite',
// Météo & conditions
'meteo', 'temperature', 'humidite', 'force_vent',
// État de la colonie (20+ champs d'observation apicole)
'humeur_colonie', 'activite_entree', 'estimation_population',
'reine_vue', 'ponte_observee', 'reserves_miel', 'varroas_observes',
// Actions réalisées
'nourrissement_effectue', 'traitement_applique', 'hausse_ajoutee',
// Notes
'notes_generales', 'actions_prevues', 'prochaine_visite_prevue'
];
// Relations
public function user() { return $this->belongsTo(User::class); }
public function ruche() { return $this->belongsTo(Ruche::class); }
public function photos() { return $this->morphMany(Photo::class, 'photoable'); }
public function traitements() { return $this->hasMany(Traitement::class); }
public function taches() { return $this->hasMany(Tache::class); }
}
🎯 Bonnes Pratiques
1. Sécurité First
- ✅ Toujours définir
$fillable
- ✅ Jamais de
user_id
dans$fillable
- ✅ Utiliser
auth()->id()
dans les contrôleurs
2. Relations Logiques
- ✅
belongsTo
pour les foreign keys - ✅
hasMany
pour les relations inversées - ✅ Noms au pluriel pour
hasMany
:ruches()
pasruche()
3. Architecture Cohérente
- ✅ Une seule source de vérité (éviter les
user_id
redondants) - ✅ Relations polymorphiques pour les entités transversales
- ✅ Traits pour les fonctionnalités réutilisables
4. Performance
- ✅
hasOneThrough
pour éviter les requêtes multiples - ✅ Index de base de données sur les foreign keys
- ✅ Eager loading avec
with()
pour éviter N+1
🚀 Workflow de Développement
1. Créer un Modèle
# Modèle + Migration + Factory
php artisan make:model Rucher -mf
# Modèle seul (si migration existe)
php artisan make:model Rucher -f
2. Définir la Structure
// 1. Sécurité
protected $fillable = [...];
// 2. Relations
public function user() { return $this->belongsTo(User::class); }
public function ruches() { return $this->hasMany(Ruche::class); }
3. Tester avec Tinker
php artisan tinker
# Créer des données de test
$user = User::factory()->create();
$rucher = $user->ruchers()->create(['nom' => 'Test']);
$ruche = $rucher->ruches()->create(['nom' => 'Ruche 1']);
# Tester les relations
$ruche->rucher->user->name; // Navigation dans les relations
🎓 Points Clés à Retenir
- Modèle = Pont intelligent entre code et base de données
$fillable
= Sécurité contre les attaques mass assignment- Relations = Puissance d’Eloquent pour naviguer entre entités
- Polymorphiques = Flexibilité pour les entités transversales
- Factories = Productivité pour générer des données de test
- Architecture = Cohérence pour une application maintenable
🌟 Conclusion
Les modèles Laravel ne sont pas juste des classes qui parlent à la base de données. Ils sont le cœur métier de votre application, où vous encodez la logique de votre domaine.
Avec BeeNote, nous avons vu comment modéliser un domaine complexe (apiculture) avec des relations riches, de la sécurité robuste, et une architecture évolutive.
Prochaine étape : Maîtriser les contrôleurs pour orchestrer ces modèles dans des interfaces utilisateur ! 🚀
Article basé sur l’apprentissage pratique de la stack VILT (Vue, Inertia, Laravel, Tailwind) dans le cadre du projet BeeNote.