Laravel est un système vraiment grand et complexe qui essaie de résoudre la plupart des tâches quotidiennes d'un développeur Web de la manière la plus élégante et de collecter autant d'outils que possible et, ce qui est très important, avec autant d'interface humaine que possible.
Et aujourd'hui, nous allons nous concentrer sur l'un de ces outils, ou plutôt sur son utilisation et sa mise en œuvre par le programmeur. Le manque de documentation complète, ainsi que le manque d'articles en russe et un très petit nombre d'articles étrangers, m'ont poussé à décider de révéler un certain voile de secret sur cette caractéristique intéressante du cadre et à choisir ce sujet comme mon premier article sur Habré.
Middleware
Cet article suppose que le lecteur est déjà familiarisé avec l'utilisation de base de cette fonctionnalité du framework, donc je ne m'attarderai pas sur ce point pendant longtemps.
Prêt à l'emploi, Laravel nous fournit une fonctionnalité assez puissante pour filtrer les requêtes HTTP entrantes vers notre application. Nous parlons du
middleware bien-aimé (ou pas) de tout le monde - le développeur rencontre ces classes assez rapidement sur le chemin de la maîtrise de Laravel, tout en lisant la section «Les bases» de la documentation officielle, et cela n'est pas surprenant - le middleware est l'un des principaux et des plus importants briques sur la base desquelles l'ensemble du système est construit.
Des exemples de cas utilisateur standard de ce composant dans Laravel sont:
EncryptCookies / RedirectIfAuthenticated / VerifyCsrfToken , et comme exemple d'implémentation personnalisée, vous pouvez citer la localisation de l'application middleware (en définissant la localisation requise en fonction de certaines données de demande) avant de transférer la demande.
Plus profondément dans l'abîme
Abandonnez tout espoir de venir ici
Eh bien, maintenant que les points principaux sont terminés - nous pouvons plonger dans un endroit terrible pour beaucoup - en alpha et oméga, le début et la fin - dans les
sources de Laravel . Ceux qui ont tendu la main pour clore l'article immédiatement - prenez votre temps. En fait, dans le code source de ce framework, il n'y a presque rien de vraiment compliqué du côté conceptuel - les créateurs essaient clairement non seulement de créer une interface claire et pratique pour travailler avec leur idée originale, mais ils essaient également de faire la même chose directement au niveau du code source, ce qui ne peut pas ne pas plaire.
J'essaierai d'expliquer le concept de la façon dont
Middleware et
Pipeline fonctionnent au niveau du code et de la logique aussi simple et accessible que possible, et j'essaierai de ne pas entrer dans les détails - là où ce n'est pas nécessaire dans le cadre de l'article. Donc s'il y a des gens dans les commentaires qui connaissent par cœur toutes les lignes sources, je vous demande de ne pas critiquer ma narration superficielle. Mais toutes recommandations et corrections d'inexactitudes ne sont que les bienvenues.
Middleware - à travers les barricades
Je crois qu'apprendre n'importe quoi est toujours plus facile quand de bons exemples sont fournis. Par conséquent, je vous invite, moi et moi, à étudier cette mystérieuse bête sous le nom de
Pipeline . Si de tels hommes courageux existent vraiment, alors avant de poursuivre la lecture, nous devrons installer une version 5.7 du projet Laravel vide - la version n'est due qu'au fait qu'elle est la dernière au moment de la rédaction, tout ce qui précède devrait être identique au moins à la version 5.4. Ceux qui veulent simplement connaître l'essence et les conclusions de l'article peuvent sauter cette partie en toute sécurité.
Quoi de mieux que d'étudier le comportement d'un composant, si ce n'est d'étudier le comportement déjà intégré au système? Peut-être que quelque chose peut le faire, mais nous ferons sans complications inutiles et commencerons notre analyse avec le middleware standard - à savoir, avec le plus simple et le plus compréhensible de tout le gang -
RedirectIfAuthenticated :
RedirectIfAuthenticated.phpclass RedirectIfAuthenticated { public function handle($request, Closure $next, $guard = null) { if (Auth::guard($guard)->check()) { return redirect('/'); } return $next($request); } }
Dans toute classe de middleware classique, il existe une méthode principale qui doit traiter directement la demande et passer le traitement à la suivante de la chaîne - dans notre cas, c'est la méthode du
handle . Dans cette classe particulière, le traitement de la demande est assez simple - "si l'utilisateur est autorisé, alors redirigez-le vers la page principale et, ainsi, mettez fin à la chaîne."
Si nous regardons l'enregistrement de ce middleware dans
app / Http / Kernel.php , nous verrons qu'il est enregistré dans le 'middleware de route'. Afin de découvrir comment le système fonctionne avec ce middleware, allons à la classe dont hérite notre
application / Http / Kernel - et elle hérite de la classe
Illuminate \ Foundation \ Http \ Kernel . À ce stade, nous ouvrirons directement les portes de l'
enfer du code source de notre framework, ou plutôt, de la partie la plus importante et principale de celui-ci - au cœur du travail avec HTTP. Soit dit en passant, peu importe - Laravel est basé sur de nombreux composants de
Symfony , en particulier dans cette partie - sur
HttpFoundation et
HttpKernel .
La définition et l'implémentation de notre middleware dans le constructeur du noyau sont les suivantes:
Illuminate \ Foundation \ Http \ Kernel (Application $ app, Router $ router) public function __construct(Application $app, Router $router) { $this->app = $app; $this->router = $router; $router->middlewarePriority = $this->middlewarePriority; foreach ($this->middlewareGroups as $key => $middleware) { $router->middlewareGroup($key, $middleware); } foreach ($this->routeMiddleware as $key => $middleware) { $router->aliasMiddleware($key, $middleware); } }
Le code est assez simple et direct - pour chaque middleware du tableau, nous l'enregistrons avec un alias / index dans notre routeur. Les méthodes aliasMiddleware et middlewareGroups de notre classe Route ajoutent simplement un middleware à l'un des tableaux de l'objet routeur. Mais cela n'est pas inclus dans le contexte de l'article, nous allons donc sauter ce moment et passer à autre chose.
Ce qui nous intéresse vraiment, c'est la méthode
sendRequestThroughRoute , qui traduit littéralement comment
envoyer une demande via Route :
Illuminate \ Foundation \ Http \ Kernel :: sendRequestThroughRouter ($ request) protected function sendRequestThroughRouter($request) {
En paramètre, cette méthode reçoit une requête. À ce stade, nous devrions revoir notre code
RedirectIfAuthenticated . Nous recevons également une requête dans la méthode
handle de notre middleware, nous aurons besoin de cette note un peu plus tard.
Le code ci-dessus a une interface très claire et lisible -
«Pipeline», qui envoie une demande via chacun des middlewares enregistrés, puis la «transfère» au routeur . Charmant et merveilleux. Je pense qu'à ce stade, nous n'essaierons pas de décomposer davantage cette section de code, je ne décrirai que brièvement le rôle de cette section dans l'ensemble du système:
Avant qu'une requête ne pénètre dans votre contrôleur, de nombreuses actions passent, de la simple analyse de l'url elle-même à l'initialisation de la classe
Request . L'intergiciel est également impliqué dans cette
chaîne d' actions. Les classes de middleware elles-mêmes implémentent le (presque) modèle de conception de la
chaîne de responsabilité ,
ou chaîne de responsabilité , de sorte que chaque classe de middleware particulière n'est qu'un maillon de la chaîne.
Ci-dessus, nous ne sommes pas seulement revenus à notre classe
RedirectIfAuthenticated initialement considérée. La demande "circule" le long de la chaîne, y compris celle qui passe par tout ce qui est requis pour la route du middleware. Ce moment nous aidera à travailler avec nos propres liens dans notre propre chaîne, plus à ce sujet plus tard.
Pipeline - Assainissement de notre application
Un exemple de la mise en œuvre de
Pipeline que nous avons vu ci-dessus. Mais le but de l'article n'était pas seulement d'expliquer le fonctionnement de ce composant au niveau de l'intégration avec Laravel, mais aussi d'expliquer le principe de base de travailler avec cette classe dans notre propre code.
La classe elle-même peut être trouvée par sa définition complète avec namespace:
Illuminate \ Pipeline \ Pipeline
Il peut y avoir beaucoup d'applications pour ce composant, selon la tâche spécifique que vous devez résoudre, mais l'une des motivations les plus évidentes est la nécessité de créer votre propre chaîne de gestionnaires de demandes, qui n'interfère pas avec les processus de l'ensemble du système et est déterminée exclusivement au niveau de votre logique métier. De plus, l'interface de classe a un niveau d'abstraction suffisant et dispose de fonctionnalités suffisantes pour implémenter différents types de files d'attente.
Exemple d'implémentation dans Laravel
Nous mettons en œuvre la chaîne de requête la plus simple et la plus éloignée de la réalité. Nous utiliserons la chaîne "HELLO WORLD" comme données, et avec l'aide de deux gestionnaires, nous en formerons la chaîne "Hello User". Le code est intentionnellement simplifié.
Avant la mise en œuvre immédiate de notre propre «Pipe», nous devons identifier les éléments de cette pipe. Les éléments sont écrits par analogie avec le middleware:
Définition des gestionnairesStrToLowerAction.php:
use Closure; class StrToLowerAction { public function handle(string $content, Closure $next) { $content = strtolower($content); return $next($content); } }
SetUserAction.php:
use Closure; class SetUserAction { public function handle(string $content, Closure $next) { $content = ucwords(str_replace('world', 'user', $content)); return $next($content); } }
Ensuite, nous créons un "pipeline", déterminons le type de données que nous voulons envoyer dessus, déterminons à travers quelle collection de processeurs nous voulons envoyer ces données, et définissons également un rappel, qui reçoit en argument nos données transmises à travers toute la chaîne. Dans le cas où les données de la chaîne restent inchangées, la partie avec rappel peut être omise:
$pipes = [ StrToLowerAction::class, SetUserNameAction::class ]; $data = 'Hello world'; $finalData = app(Pipeline::class) ->send($data)
De plus, si vous avez un désir ou besoin de définir votre propre méthode dans les gestionnaires, l'interface Pipeline fournit une méthode spéciale
via ('method_name') , alors le traitement en chaîne peut être écrit de cette façon:
$finalData = app(Pipeline::class) ->send($data) ->through($pipes) ->via('handle')
Directement, les données que nous transmettons aux processeurs peuvent être absolument n'importe quoi, ainsi que l'interaction avec eux. La saisie d'indices et la définition du type de l'objet renvoyé dans la chaîne aideront à éviter les erreurs d'intégrité des données.
Conclusion
Laravel fournit un grand nombre de classes intégrées, et la flexibilité de beaucoup d'entre elles nous permet de développer quelque chose de complexe avec une simplicité suffisante. Cet article a examiné la possibilité de créer des files d'attente simples pour les demandes basées sur la classe Pipeline intégrée à Laravel. Les implémentations de cette classe dans le code final peuvent être complètement différentes, et la flexibilité de cet outil vous permet de vous débarrasser de nombreuses actions inutiles lors de la construction de certains algorithmes.
La façon d'utiliser spécifiquement cette fonctionnalité du cadre dépend des tâches qui vous sont assignées.