Architecture MVC et les contrôleurs en Laravel - Letecode

Contrôleurs

Dernière mise à jour : 17/06/2022

Dans ce chapitre, nous verrons les concepts du modèle MVC et l'utilisation de contrôleurs en Laravel

Avant de parler des contrôleurs, parlons de l'architecture MVC (Model View Controller).

MVC (Model View Controller)

On peut difficilement parler d'un framework sans évoquer le patron Modèle-Vue-Contrôleur. Pour certains il s'agit de la clé de voûte de toute application rigoureuse, pour d'autres c'est une contrainte qui empêche d'organiser judicieusement son code. De quoi s'agit-il ? Voici un petit schéma pour y voir clair :

Model MVC

C'est un modèle d'organisation du code :

  • le Modèle est chargé de gérer les données
  • la Vue est chargée de la mise en forme pour l'utilisateur
  • le Contrôleur est chargé de gérer l'ensemble

En général on résume en disant que le modèle gère la base de données, la vue produit les pages HTML et le contrôleur fait tout le reste. Dans Laravel :

  • le modèle correspond à une table d'une base de données. C'est une classe qui hérite de la classe Model qui permet une gestion simple et efficace des manipulations de données et l'établissement automatisé de relations entre tables. Les modèles sont placés dans App/Models/.
  • le contrôleur se décline en deux catégories : contrôleur classique et contrôleur de ressource. Ils sont placés dans App/Http/Controllers/.
  • la vue est soit un simple fichier avec du code HTML, soit un fichier utilisant le système de template Blade de Laravel. Elles sont placés dans resources/views/.

Laravel propose ce modèle mais ne l'impose pas. Nous verrons d'ailleurs qu'il est parfois judicieux de s'en éloigner parce qu'il y a des tas de choses qu'on arrive pas à caser dans ce modèle. Par exemple si je dois envoyer des emails où vais-je placer mon code ? En général ce qui se produit est l'inflation des contrôleurs auxquels on demande des choses pour lesquelles ils ne sont pas faits.

Contrôleurs

Au lieu de définir toute votre logique de gestion des requêtes comme des closures dans vos fichiers de routage, vous pouvez organiser ce comportement à l'aide de classes "Controller". Les contrôleurs peuvent regrouper la logique de gestion des requêtes associées dans une seule classe. Par exemple, une classe UserController peut gérer toutes les demandes entrantes liées aux utilisateurs, y compris l'affichage, la création, la mise à jour et la suppression d'utilisateurs. Par défaut, les contrôleurs sont stockés dans le  répertoire app/Http/Controllers.

Pour créer un contrôleur, taper ligne de commande suivante :

php artisan make:controller IndexController 

Vous le trouverez sur app/Http/Controllers/IndexController avec ce contenu : 

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;

class IndexController extends Controlle
{
    //
}

Contrôleurs de base

Examinons un exemple de contrôleur de base. Notez que le contrôleur étend la classe Controller de base incluse avec Laravel dans App\Http\Controllers\Controller :

<?php
namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\User;
 
class UserController extends Controller
{
    /**
     * Afficher le profile d'un utilisateur.
     */
    public function show($id)
    {
        return view('user.profile', [
            'user' => User::findOrFail($id)
        ]);
    }
}

 

Vous pouvez définir une route vers cette méthode de contrôleur comme suit :

 

use App\Http\Controllers\UserController;
 
Route::get('/user/{id}', [UserController::class, 'show']);

Lorsqu'une requête entrante correspond à l'URI de route spécifié, la méthode show de la classe App\Http\Controllers\UserController est appelée et les paramètres de route sont transmis à la méthode.

Les contrôleurs ne sont pas obligés d'étendre une classe de base. Cependant, vous n'aurez pas accès à des fonctionnalités pratiques telles que les méthodes middleware et authorize.

Contrôleurs à simple action

Si une action de contrôleur est particulièrement complexe, vous trouverez peut-être pratique de dédier une classe de contrôleur entière à cette action unique. Pour ce faire, vous pouvez définir une seule méthode __invoke dans le contrôleur :




<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\User;
 class ProvisionServer extends Controller
{
    /**
     * @return \Illuminate\Http\Response
     */
    public function __invoke()
    {
        // ...
    }
}

Lors de l'enregistrement de routes pour des contrôleurs à action unique, vous n'avez pas besoin de spécifier une méthode de contrôleur. Au lieu de cela, vous pouvez simplement transmettre le nom du contrôleur au routeur :




use App\Http\Controllers\ProvisionServer;

Route::post('/server', ProvisionServer::class);



Vous pouvez générer un contrôleur invocable en utilisant l'option --invokable de la commande Artisan make:controller:

php artisan make:controller ProvisionServer --invokable

 

Middleware du contrôleur

Un middleware peut être affecté aux routes du contrôleur dans vos fichiers de routes :

Route::get('profile', [UserController::class, 'show'])->middleware('auth');

Ou, vous trouverez peut-être pratique de spécifier un middleware dans le constructeur de votre contrôleur. En utilisant la méthode middleware dans le constructeur de votre contrôleur, vous pouvez affecter un middleware aux actions du contrôleur :

 

class UserController extends Controller
{
    /**
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
        $this->middleware('log')->only('index');
        $this->middleware('subscribed')->except('store');
    }
}

Les contrôleurs vous permettent également d'enregistrer un middleware à l'aide d'une closure. Cela fournit un moyen pratique de définir un middleware en ligne pour un seul contrôleur sans définir une classe complète de middleware :

 

$this->middleware(function ($request, $next) {
    return $next($request);
});

Contrôleurs ressources

Si vous considérez chaque modèle Eloquent de votre application comme une "ressource", il est courant d'effectuer les mêmes ensembles d'actions sur chaque ressource de votre application. Par exemple, imaginez que votre application contient un modèle Photo et un modèle Movie. Il est probable que les utilisateurs puissent créer, lire, mettre à jour ou supprimer ces ressources.

En raison de ce cas d'utilisation courant, le routage des ressources Laravel attribue les routes typiques de création, lecture, mise à jour et suppression ("CRUD") à un contrôleur avec une seule ligne de code. Pour commencer, nous pouvons utiliser l'option --resource de la commande Artisan  make:controller pour créer rapidement un contrôleur pour gérer ces actions :

php artisan make:controller PhotoController --resource

Cette commande générera un contrôleur à app/Http/Controllers/PhotoController.php. Le contrôleur contiendra une méthode pour chacune des opérations de ressources disponibles. Ensuite, vous pouvez enregistrer une route ressources qui pointe vers le contrôleur :

 

use App\Http\Controllers\PhotoController;
 
Route::resource('photos', PhotoController::class);

 

Cette déclaration de route unique crée plusieurs routes pour gérer diverses actions sur la ressource. Le contrôleur généré aura déjà des méthodes crud pour chacune de ces actions. N'oubliez pas que vous pouvez toujours obtenir un aperçu rapide des routes de votre application en exécutant la commande Artisan route:list.

Vous pouvez même enregistrer plusieurs contrôleurs de ressources à la fois en passant un tableau à la méthode resources:

 

Route::resources([
    'photos' => PhotoController::class,
    'posts' => PostController::class,
]);

 

Actions gérées par le contrôleur de ressources

Verbe URI Action Nom de la route
GET /photos index photos.index
GET /photos/create créer photos.create
POST /photos Enregistrer photos.store
GET /photos/{photo} Afficher photos.show
GET /photos/{photo}/edit Modifier photos.edit
PUT/PATCH /photos/{photo} mettre à jour photos.update
DELETE /photos/{photo} supprimer photos.destroy

 

Personnalisation du comportement du modèle manquant

En règle générale, une réponse HTTP 404 est générée si un modèle de ressource implicitement lié n'est pas trouvé. Cependant, vous pouvez personnaliser ce comportement en appelant la méthode missing lors de la définition de votre route de ressource. La méthode missing accepte une fermeture qui sera invoquée si un modèle lié implicitement ne peut être trouvé pour aucune des routes de la ressource :

 

use App\Http\Controllers\PhotoController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
 
Route::resource('photos', PhotoController::class)
        ->missing(function (Request $request) {
            return Redirect::route('photos.index');
        });

 

Spécification du modèle de ressource

Si vous utilisez la liaison de modèle de route et que vous souhaitez que les méthodes du contrôleur de ressources indiquent le type d'une instance de modèle, vous pouvez utiliser l'option --model lors de la génération du contrôleur :

php artisan make:controller PhotoController --model=Photo --resource

Génération de requêtes de formulaire

Vous pouvez fournir l'option --requests lors de la génération d'un contrôleur de ressources pour demander à Artisan de générer des classes de requête de formulaire pour les méthodes de stockage et de mise à jour du contrôleur :

php artisan make:controller PhotoController --model=Photo --resource --requests

Routes de ressources partielles

Lors de la déclaration d'une route de ressources, vous pouvez spécifier un sous-ensemble d'actions que le contrôleur doit gérer au lieu de l'ensemble complet d'actions par défaut :

use App\Http\Controllers\PhotoController;
 
Route::resource('photos', PhotoController::class)->only([
    'index', 'show'
]);
 
Route::resource('photos', PhotoController::class)->except([
    'create', 'store', 'update', 'destroy'
]);

 

Routes ressources d'API

Lors de la déclaration des routes ressources qui seront consommées par les API, vous souhaiterez généralement exclure les routes qui présentent des modèles HTML tels que create et edit. Pour plus de commodité, vous pouvez utiliser la méthode  apiResource pour exclure automatiquement ces deux routes :

 

use App\Http\Controllers\PhotoController;
 
Route::apiResource('photos', PhotoController::class);

 

Vous pouvez enregistrer plusieurs contrôleurs de ressources d'API à la fois en passant un tableau à la méthode apiResource:

 

use App\Http\Controllers\PhotoController;
use App\Http\Controllers\PostController;
 
Route::apiResources([
    'photos' => PhotoController::class,
    'posts' => PostController::class,
]);

Pour générer rapidement un contrôleur de ressources d'API qui n'inclut pas les méthodes create ou edit, utilisez le commutateur --api lors de l'exécution de la commande : make:controller

php artisan make:controller PhotoController --api

Ressources imbriquées

Parfois, vous devrez peut-être définir des routes vers une ressource imbriquée. Par exemple, une ressource photo peut avoir plusieurs commentaires qui peuvent être joints à la photo. Pour imbriquer les contrôleurs de ressources, vous pouvez utiliser la notation "dot" dans votre déclaration de route :

 

use App\Http\Controllers\PhotoCommentController;
 
Route::resource('photos.comments', PhotoCommentController::class);

 

Cette route enregistrera une ressource imbriquée accessible avec des URI comme suit :

/photos/{photo}/comments/{comment}

Étendue des ressources imbriquées

La fonctionnalité de liaison de modèle implicite de Laravel peut automatiquement étendre les liaisons imbriquées de sorte que le modèle enfant résolu soit confirmé comme appartenant au modèle parent. En utilisant la méthode scoped lors de la définition de votre ressource imbriquée, vous pouvez activer la portée automatique et indiquer à Laravel par quel champ la ressource enfant doit être récupérée. Pour plus d'informations sur la façon d'accomplir cela, veuillez consulter la documentation sur la portée des routes de ressources .

Nommer les routes de ressources

Par défaut, toutes les actions du contrôleur ressource ont un nom de route ; cependant, vous pouvez remplacer ces noms en transmettant un tableau names avec les noms de route souhaités :

use App\Http\Controllers\PhotoController;
 
Route::resource('photos', PhotoController::class)->names([
    'create' => 'photos.build'
]);

Consultez la documentation officielle de Laravel sur les Contrôleurs.

Pous notre blog, nous avons besoin des contrêleurs suivants :

  • PostController : pour gérer le CRUD de nos publications,
  • CategoryController: pour gérer les actions sur les catégories
  • TagController : pour les actions sur les tags.

Au prochain chapitre nous allons voir comment travailler avec les Vues.