doganddev
Accueil Blog Boutique

Bonnes pratiques Laravel : modèles, contrôleurs, requêtes et sécurité

DOG&DEV · 25/01/2025

Failles & Patches Gestion de Projet IT
Bonnes pratiques Laravel : modèles, contrôleurs, requêtes et sécurité

Bonnes pratiques Laravel : modèles, contrôleurs, requêtes et sécurité

Adopter les bonnes pratiques Laravel permet de garder un code lisible, maintenable et aligné avec l’écosystème. Ce guide synthétise des recommandations largement partagées : fat models, skinny controllers, principe de responsabilité unique, scopes, FormRequest, Http::pool / batch, transactions, traduction et tests.

Prérequis

  • Projet Laravel (10, 11 ou 12)
  • Bases en Eloquent, contrôleurs et Blade

1. Fat models, skinny controllers

Déplacer la logique métier et les requêtes dans les modèles (ou des services) pour alléger les contrôleurs.

// ❌ Logique dans le contrôleur
$clients = Client::verified()
    ->with(['orders' => fn ($q) => $q->where('created_at', '>', now()->subDays(7))])
    ->get();

// ✅ Méthode ou scope dans le modèle
$clients = $client->getVerifiedWithRecentOrders();

Le contrôleur appelle une méthode claire ; le détail des requêtes et des relations reste dans le modèle.

2. Principe de responsabilité unique (SRP)

Une classe = une responsabilité. Pour la validation, le logging, les mises à jour métier, extraire des FormRequest, Services, Actions ou Logger dédiés.

// ❌ Tout dans le contrôleur
public function update(Request $request) {
    $validated = $request->validate([...]);
    foreach ($request->tasks as $task) { /* log */ }
    $this->project->updateTasks($validated);
    return redirect()->route('projects.index');
}

// ✅ Délégation
public function update(UpdateProjectRequest $request, ProjectService $projectService, TaskLogger $logger) {
    $logger->logTasks($request->tasks);
    $projectService->updateTasks($request->validated());
    return redirect()->route('projects.index');
}

3. Relations et création

Utiliser les relations Eloquent plutôt que d’assigner manuellement les clés étrangères :

// ❌
$article->category_id = $category->id;
$article->title = $request->input('title');
$article->save();

// ✅
$category->articles()->create($request->safe()->only(['title', 'content', 'verified']));

4. Transactions pour les opérations atomiques

Tout ce qui touche plusieurs écritures liées doit être dans une transaction :

DB::transaction(function () use ($request) {
    $order = Order::create($request->validated());
    Payment::create(['order_id' => $order->id]);
});

5. Eager loading pour éviter le N+1

Ne pas lancer de requêtes dans les boucles Blade. Charger les relations en amont :

// ❌ N+1
@foreach (User::all() as $user)
    {{ $user->profile->name }}
@endforeach

// ✅
$users = User::with('profile')->get();
@foreach ($users as $user)
    {{ $user->profile->name }}
@endforeach

6. Chunk pour les gros volumes

Pour des milliers d’enregistrements, utiliser chunk() (ou chunkById) pour limiter la mémoire :

User::chunk(500, function ($users) {
    foreach ($users as $user) {
        // traitement
    }
});

7. Constantes au lieu de valeurs en dur

Remplacer les magic strings par des constantes ou des enums :

// ❌
return $user->type === 'admin';

// ✅
return $user->type === UserType::ADMIN;

8. Traduction des chaînes

Préparer l’i18n dès le début avec __() :

return back()->with('message', __('Votre article a bien été ajouté.'));

9. Injection de dépendances

Préférer l’injection (constructeur ou méthode) à new pour les services :

public function __construct(protected UserService $userService) {}

public function store(StoreUserRequest $request) {
    $this->userService->create($request->validated());
}

10. Configuration au lieu de env() direct

Lire la config via config() plutôt qu’env() dans le code applicatif :

// ❌
$apiKey = env('API_KEY');

// ✅ config/services.php : 'api_key' => env('API_KEY'),
$apiKey = config('services.api_key');

11. Dates en datetime / date

Caster les colonnes de date pour manipuler des objets Carbon :

protected $casts = ['ordered_at' => 'datetime'];
// Blade : {{ $object->ordered_at->format('d/m/Y') }}

12. Http::pool et Http::batch

Pour plusieurs appels API indépendants, utiliser Http::pool (ou Http::batch avec callbacks) au lieu d’appels séquentiels :

$responses = Http::pool(fn (Pool $pool) => [
    $pool->as('github')->get('https://api.github.com/users/laravel'),
    $pool->as('weather')->get('https://api.weather.com/v3/today'),
]);

13. Documenter avec parcimonie

Éviter les blocs de commentaires massifs. Préférer des noms explicites (méthodes, variables) ; réserver les commentaires aux règles métier ou cas complexes.

14. Tests

Couvrir au minimum les routes critiques, les FormRequest et les services ; les tests servent de sécurité pour le refactoring et la régression.

Dépannage

Symptôme Piste Correctif
N+1, requêtes excessives Boucles sur des relations non chargées with(), withCount() et analyse des requêtes (Debugbar, Telescope)
Contrôleur surchargé Trop de responsabilités Extraire en Service, Action, FormRequest
Données incohérentes Plusieurs écritures sans transaction Envelopper dans DB::transaction()

Bonnes pratiques

  • FormRequest pour toute entrée utilisateur ; Policy / Gate pour l’autorisation.
  • Scopes (locaux et globaux) pour réutiliser des contraintes de requêtes.
  • Laravel Pint pour uniformiser le style de code en équipe.

Ressources


Cet article s’inscrit dans notre série de guides technique et développement web. Pour un serveur ou une application sur-mesure, contact.

Commentaires (0)

Laisser un commentaire