X

Laravel 5.0 – Commandes et gestionnaires


Comme vous m’avez probablement lu sur Twitter, j’ai fait une pause sur le blog des nouvelles fonctionnalités de Laravel 5 pour essayer de donner à Taylor un peu d’espace pour se développer avec un peu moins de « ce commit Git s’est produit ; donc c’est comme ça que ce sera pour toujours ! Mais il a annoncé une nouvelle fonctionnalité aujourd’hui, donc je considère que celle-ci est assez susceptible de rester.

Le nouvel ensemble de fonctionnalités concerne les commandes, qui existent déjà dans Laravel, mais qui reçoivent beaucoup d’amour dans Laravel 5.0.

J’utiliserai des exemples dans ce billet de blog d’une nouvelle application sur laquelle je travaille appelée SaveMyProposals, qui permet aux conférenciers d’enregistrer des propositions de conversation.

Qu’est-ce qu’une commande ? Un gestionnaire de commandes ? Un bus de commande ?

Vous pouvez en savoir plus sur le concept de commande, de gestionnaire de commandes et de bus de commandes auprès de Shawn McCool, mais essentiellement :

UN commande est un simple objet censé être un message. Il contient seul les informations dont vous avez besoin pour faire quelque chose. Notre exemple ici sera “Duplicate Talk Command”, qui est une commande imaginaire que notre système (un contrôleur ou une commande Artisan, probablement) enverra chaque fois qu’un utilisateur a choisi de dupliquer une proposition de conversation. La commande duplicate talk aura toutes les propriétés dont nous avons besoin pour dupliquer une conversation, probablement un objet Talk sérialisé ou un TalkId.

UN gestionnaire de commandes est une classe chargée de faire quelque chose en réponse à la commande. Une commande peut être passée par un ou plusieurs gestionnaires ; chacun extrait des informations importantes de la commande et fait quelque chose en réponse.

UN bus de commande est le système qui vous permet de répartir (créer et envoyer) des commandes, qui fait correspondre les commandes à leurs gestionnaires et qui fait que tout joue ensemble. Souvent, les gens écrivent leurs propres bus de commande, mais Laravel en fournit un prêt à l’emploi, nous n’avons donc pas à nous en soucier dans cet article.

Utilisation des commandes dans Laravel

Avant d’entrer dans toute la structure de l’utilisation des commandes dans Laravel 5, regardons à quoi ressemblera le cas d’utilisation final. Imaginez qu’un utilisateur visite un itinéraire quelque chose comme savemyproposals.com/talks/12345/duplicatequi les achemine vers TalkController@duplicate(12345).

Nous aurons une méthode de contrôleur pour le gérer:

// Http\Controllers\TalkController
...
    public function duplicate($talkId)
    {
        $talk = Talk::findOrFail($talkId);

        $this->dispatch(new DuplicateTalkCommand($talk));

        // Depending on implementation, this could also just be:
        // $this->dispatch(new DuplicateTalkCommand($talkId));
    }

Ensuite, nous aurons une commande :

// Commands\DuplicateTalkCommand
...

class DuplicateTalkCommand extends Command
{
    public $talk;

    public function __construct(Talk $talk)
    {
        $this->talk = $talk;
    }
}

Et un gestionnaire de commandes :

// Handlers\Commands\DuplicateTalkCommandHandler
...

class DuplicateTalkCommandHandler
{
    public function handle(DuplicateTalkCommand $command)
    {
        // Do something with $command
        dd($command);
    }
}

Comme vous pouvez le voir, notre contrôleur crée un DuplicateTalkCommand avec les informations nécessaires, le répartit à l’aide du répartiteur de bus de commande intégré, puis il est géré (automatiquement) par son gestionnaire.

Architecture

OK, regardons d’abord où se trouvent ces commandes et ces gestionnaires, puis comment nous les générons.

Dossiers

Il y a deux nouveaux dossiers dans app/: Commands et Handlerset Handlers contient deux sous-dossiers : Commands et Events (ce qui nous montre que nous pouvons également nous réjouir de la gestion des événements.)

    app/
        Commands/
        Handlers/
            Commands/
            Events/

Comme vous pouvez le deviner, les commandes vont dans le app/Commands dossier et les gestionnaires de commandes vont dans le dossier app/Handlers/Commands/ dossier—avec exactement le même nom que leur commande, mais avec Handler ajouté à la fin.

Artisan

Heureusement, vous n’avez pas à le faire vous-même. Il existe un nouveau générateur Artisan qui simplifiera la création de votre propre commande :

$ php artisan make:command DuplicateTalkCommand

Par défaut, cela crée une commande d’auto-gestion qui n’est pas poussée dans la file d’attente. Passe ça le --handler drapeau pour générer un gestionnaire, et le --queued drapeau pour le mettre en file d’attente.

Cela génère deux fichiers : une commande (app\Commands\DuplicateTalkCommand.php) et un gestionnaire (app\Handlers\Commands\DuplicateTalkCommandHandler.php) (si vous avez réussi le --handler drapeau), et le Handler’s handle La méthode est générée automatiquement typée pour sa commande appariée.

Flux de travail de base

Ainsi, pour créer un nouveau DuplicateTalkCommandvous feriez ce qui suit :

  1. php artisan make:command DuplicateTalkCommand
  2. Modifier DuplicateTalkCommand et lui donner une propriété publique de $talk et configurez-le pour qu’il soit injecté via le constructeur
  3. Modifier DuplicateTalkCommandHandler et écrire son handle() méthode pour faire ce que vous voulez réellement, en utilisant probablement un référentiel ou une autre couche d’accès à la base de données pour dupliquer la conversation et enregistrer le doublon.
  4. Envoyez la commande, probablement dans votre contrôleur ou une commande Artisan.

C’est ça! Vous utilisez maintenant des commandes dans Laravel 5.0 ! À partir de maintenant, tout ne sont que des détails minutieux sur les files d’attente, les traits, les interfaces et d’autres considérations et astuces spéciales.

Files d’attente

Commandes de mise en file d’attente

Si vous voulez qu’une commande soit mise en file d’attente chaque fois que vous l’envoyez (au lieu de fonctionner de manière synchrone), tout ce que vous avez à faire est de lui faire implémenter le ShouldBeQueued interface. Laravel lira cela comme un signal pour le mettre en file d’attente, et il sera poussé dans la file d’attente que vous utilisez au lieu de l’exécuter en ligne.

...
class DuplicateTalkCommand extends Command implements ShouldBeQueued
{

Cela signifie qu’il est désormais encore plus facile que jamais d’intégrer des files d’attente dans votre flux de travail normal.

Caractéristique InteractsWithQueue

L’ajout de ce trait à votre commande vous donnera toutes les fonctionnalités de votre commande auxquelles vous êtes habitué dans les commandes de file d’attente traditionnelles : $command->release(), $command->delete(), $command->attempts()etc.

...
class DuplicateTalkCommand extends Command implements ShouldBeQueued, InteractsWithQueue
{

Trait SerializesModels

Si vous transmettez un modèle Eloquent en tant que propriété, comme je l’ai fait dans l’exemple ci-dessus, et vous souhaitez mettre vos commandes en file d’attente (au lieu de simplement les laisser s’exécuter de manière synchrone), cela peut vous causer des problèmes en raison de la sérialisation des modèles Eloquent. Mais il y a un trait que vous pouvez ajouter à la commande nommée SerializesModels qui atténuera tous ces problèmes. Utilisez-le simplement en haut de votre commande :

...
class DuplicateTalkCommand extends Command implements ShouldBeQueued
{
    use SerializesModels;

Le répartiteur

Trait DispatchesCommands

Vous remarquerez que, dans l’exemple ci-dessus, nous avons pu simplement utiliser $this->dispatch() dans le contrôleur. Ce est magie du contrôleur, mais c’est de la magie accessible via un DispatchesCommands trait, que vous pouvez appliquer à autre chose qu’un contrôleur.

Donc, si vous voulez qu’une classe de service, par exemple, puisse utiliser $this->dispatch() dans ses méthodes, il suffit d’utiliser le DispatchesCommands trait sur votre classe de service et vous êtes prêt à partir.

Injection du bus

Si vous préférez être plus direct et clair avec votre utilisation du bus, au lieu d’utiliser le trait, vous pouvez en fait injecter le bus dans votre constructeur ou votre méthode. Juste injecter Illuminate\Contracts\Bus\Dispatcher et vous aurez un bus prêt à partir.

    ...
    public function __construct(\Illuminate\Contracts\Bus\Dispatcher $bus)
    {
        $this->bus = $bus;
    }

    public function doSomething()
    {
        $this->bus->dispatch(new Command);
    }

dispatchFrom(command::class, $request ou n’importe quel tableauAccessible)

On a déjà vu ça $bus->dispatch(new Command(params...)) est le moyen le plus simple d’envoyer une commande. Mais parfois, la liste des paramètres d’une nouvelle commande peut devenir de plus en plus longue, par exemple, lorsque votre commande gère une demande de formulaire.

...
class CreateTalkCommand extends Command
{
    public function __construct($title, $description, $outline, $organizer_notes, $length, $type, $level)
    {

Maintenir l’appel d’instanciation pour cela pourrait devenir fou.

    $this->dispatch(new CreateTalkCommand($input['title'], $input['description'], $input['outline'], $input['organizer_notes'], $input['length'], $input['type'], $input['level']));

Hum, regarde ça. Souvent, nous ne faisons que transmettre des propriétés avec la même clé, accessibles à partir d’un tableau ou d’un objet Request, n’est-ce pas ? Heureusement, il existe une solution de contournement pour résoudre ce problème très facile:

    $this->dispatchFrom(‘NameOfCommand’, $objectThatImplementsPHPArrayAccessible);

C’est ça! Donc tu peux faire ça :

    $this->dispatchFrom(CreateTalkCommand::class, $input);

… ou même ceci :

    public function doSomethingInController(Request $request)
    {
        $this->dispatchFrom(CreateTalkCommand::class, $request);

Laravel mappera automatiquement les clés sur ce tableau ou arrayAccessible objet aux mêmes noms de propriété dans votre constructeur de commande.

Commandes d’auto-assistance

Si vous préférez éviter les tracas d’une commande et d’un CommandHandler, vous pouvez rendre une commande “auto-gérée”, ce qui signifie simplement qu’il n’y a qu’un seul gestionnaire pour elle, et que ce gestionnaire est la commande elle-même. Ajoutez simplement un handle() méthode sur cette commande, et faites en sorte que la commande implémente la SelfHandling interface:

...
class DuplicateTalkCommand extends Command implements SelfHandling
{
...
    public function handle()
    {
        // Do stuff with $this->talk
    }

Remarques diverses

  • Les gestionnaires de commandes sont résolus hors du conteneur IOC, ce qui signifie que vous pouvez injecter des référentiels, des classes de service ou tout autre élément dans le constructeur de votre gestionnaire de commandes afin de les utiliser dans le handle() méthode.
  • Presque tous ces traits et interfaces vivent dans le Illuminate\Contracts\Bus ou Illuminate\Contracts\Queue espaces de noms. Par exemple Illuminate\Contracts\Bus\SelfHandling.
  • Si votre commande est en file d’attente, vous n’avez pas à effectuer $command->delete() à la fin de votre gestionnaire. Tant que votre gestionnaire ne génère aucune exception, Laravel supposera qu’il s’est terminé correctement et supprimera l’élément de la file d’attente.

Ouf.

C’était beaucoup. Si j’ai raté quelque chose ou si je n’ai pas été particulièrement clair, faites-le moi savoir – il y a beaucoup à couvrir ici, et je suis en vacances, donc je le fais par à-coups. Mais j’espère que cela vous donne une bonne idée de la façon dont tout cela va fonctionner – et comme je l’ai dit, la vidéo de Taylor sur Laracasts couvre tout cela et plus encore, et il y a beaucoup plus à venir.