Middleware and Pipeline features in Laravel

 3r33333. 3r3-31. Middleware and Pipeline features in Laravel
 3r33333.
 3r33333. Laravel - this is truly a large and complex system that tries to solve most of the everyday tasks of a web developer in the most elegant way and to collect as many tools as possible and, which is very important, with the most human interface possible.
 3r33333.
 3r33333. And today we will talk about one of these tools, and more precisely about its use and implementation by the programmer. The lack of complete documentation, as well as the lack of Russian-language articles and a very small number of foreign articles - pushed me to the decision to uncover a certain veil of secrecy about this interesting possibility of the framework and the choice of this topic as my first article on Habré.
 3r33333. Middleware - the developer encounters these classes rather quickly on the way of mastering Laravel, even at the “Basics” reading stage of the official documentation item, and this is not surprising - Middleware is one of the main and most important building blocks of the whole system.
 3r33333.
 3r33333. Examples of standard Yuz-cases of this component in Laravel are: 3r-3348. EncryptCookies /RedirectIfAuthenticated /VerifyCsrfToken [/i] , and in the example of a custom implementation, middleware localization of the application can be cited (setting the required localization based on certain request data), before sending the request further.
 3r33333.
 3r33333. 3r33333. Deeper into the abyss
 3r33333.

Give up hope, everyone entering here is


 3r33333. Well, now that the main points are over - we can go deep into a terrible place for many people - alpha and omega, the beginning and the end - in 3r3347. source Laravel
. Those who reached immediately close the article - do not rush. In fact, in the source code of this framework there is almost nothing really complicated from the conceptual side - the creators obviously try not only to create a clear and convenient interface for working with their brainchild, but they also try very hard to do the same directly at the source code level, which cannot not happy.
 3r33333.
 3r33333. I will try to explain as simply and as easily as possible the concept of the work of 3r33348. Middleware [/i] and Pipeline at the level of code and logic, and I will try not to go deep into it - where it is not necessary within the framework of the article. So, if in the comments there are people who know all the lines of the source code by heart - I will ask to refrain from criticizing my superficial narrative. But any recommendations and corrections of inaccuracies are welcome.
 3r33333.
 3r33333.

Middleware - on the other side of the barricades


 3r33333. I believe that learning anything is always easier when good examples are provided. Therefore, explore this mysterious beast under the name 3r3333348. Pipeline [/i] I offer you and me together. If there really are such brave men - then before further reading we will need to install an empty Laravel version 5.7 project - the version is due only to the fact that it is the last one at the time of writing, all of the above should be identical at least to version 5.4. Those who just want to know the essence and conclusions of the article - you can safely skip this part.
 3r33333.
 3r33333. What could be better than studying the behavior of any component, except for not studying the behavior already embedded in the system? Maybe something can be, but we will manage without unnecessary complications and begin our analysis from the standard Middleware - namely, from the simplest and most understandable of the whole gang - 3r-3229. RedirectIfAuthenticated [/b] :
 3r33333.
 3r33333.
RedirectIfAuthenticated.php [/b]
3r33354. 3r33333. class RedirectIfAuthenticated
{
/** Perform actions on incoming request
* Handle an incoming request. 3r33333. *
* @param IlluminateHttpRequest $ request
* @param Closure $ next
* @param string | null $ guard
* @return mixed
* /
public function handle ($ request, Closure $ next, $ guard = null)
{
if (Auth :: guard ($ guard) -> check ()) {3r3333390. return redirect ('/'); 3r33333.}
3r33333. return $ next ($ request); 3r33333.}
}
3r33333. 3r33333.
 3r33333. 3r33386. 3r33386.
 3r33333. In any classical middleware class, there is a main method that directly and must process the request, and transfer the processing to the next one in the chain - in our case, it is the 3r33348 method. handle [/i] . In this particular class, processing the request is quite simple - “if the user is authorized, then redirect him to the main page and, thus, stop the execution of the chain”.
 3r33333.
 3r33333. If we look at the registration of this Middleware in 3r33333. app /Http /Kernel.php [/i] then we will see that it is registered in 'route middleware'. In order for us to find out how the system works with this middleware - let's move on to the class from which our app /Http /Kernel is inherited - and it is inherited from the class IlluminateFoundationHttpKernel . At this stage, we are directly opening the gate to r3r3122. hell [/s] the source code of our framework, or rather, to the most important and its main part, to the core of work with HTTP.
 3r33333.
 3r33333. The definition and implementation of our middleware in the kernel constructor is as follows:
 3r33333.
 3r33333.
IlluminateFoundationHttpKernel (Application $ app, Router $ router) [/b]
3r33354. 3r33333. /** Create a new HTTP Kernel class object. 3r33333. * Create a new HTTP kernel instance. 3r33333. *
* @param IlluminateContractsFoundationApplication $ app
* @param IlluminateRoutingRouter $ router
* @return void
* /
public function __construct (Application $ app, Router $ router)
{
$ this-> app = $ app; 3r33333. $ this-> router = $ router; 3r33333. $ router-> middlewarePriority = $ this-> middlewarePriority; 3r33333. foreach ($ this-> middlewareGroups as $ key => $ middleware) {
$ router-> middlewareGroup ($ key, $ middleware); 3r33333.}
foreach ($ this-> routeMiddleware as $ key => $ middleware) {
$ router-> aliasMiddleware ($ key, $ middleware); 3r33333.}
}
3r33333. 3r33333.
 3r33333. 3r33386. 3r33386.
 3r33333. The code is quite simple and clear - for each middleware in the array, we register it with an alias /index in our router. The methods aliasMiddleware and middlewareGroups of our Route class are simply adding middleware to one of the arrays of the router object. But this is not in the context of the article, so let's skip this point and move on.
 3r33333.
 3r33333. What we are really interested in is the method. sendRequestThroughRoute , literally translated as Send an Inquiry Through the Route :
 3r33333.
 3r33333.
IlluminateFoundationHttpKernel :: sendRequestThroughRouter ($ request) [/b]
3r33354. 3r33333. /** Send specific request through middleware /roter. 3r33333. * Send the given request through the middleware /router. 3r33333. *
* @param IlluminateHttpRequest $ request
* @return IlluminateHttpResponse
* /
protected function sendRequestThroughRouter ($ request)
{
//* missing part of the code *
return (new Pipeline ($ this-> app))
-> send ($ request)
-> through ($ this-> app-> shouldSkipMiddleware ()?[]: $ this-> middleware) 3r33333. -> then ($ this-> dispatchToRouter ()); 3r33333.}
3r33333. 3r33333.
 3r33333. 3r33386. 3r33386.
 3r33333. As a parameter, this method receives a request. At this point, we should look again at our code. RedirectIfAuthenticated . In method handle of our middleware, we also receive a request, we will need this note a little later.
 3r33333.
 3r33333. The code above has a very clear and readable interface - 3r33348. “Pipeline”, which sends a request through each of the registered middleware, and then “sends” it to the router 3r-3349. . Charming and wonderful. I think at this stage we will not try to decompose this section of the code further, I will only briefly describe the role of this section in the entire system: 3r37777.  3r33333.
 3r33333. Before the request gets into your controller, a lot of actions take place, starting from simple parsing of the url itself and ending with the initialization of the class. Request . Middleware in this chain action is also involved. The middleware classes themselves implement the (almost) design pattern Chain of Responsibility or Chain of Responsibility Thus, each specific class of midleware is only a link in this chain.
 3r33333.
 3r33333. Above, we have not just returned to our initially considered class RedirectIfAuthenticated . The request "circulates" along the chain, including it passes through all the middleware required for the route. This moment will help us with working with our own links of our own chain, more on that later.
 3r33333.
 3r33333.

Pipeline - sewage of our application


 3r33333. One of the examples of the implementation of 3r33348. Pipeline [/i] we saw above. But the purpose of the article was not only to explain the operation of this component at the level of integration with Laravel, but also to explain the basic principle of working with this class in our own code.
 3r33333.
 3r33333. The class itself can be found by its full definition with namespace: 3r-3245. IlluminatePipelinePipeline
 3r33333. This component can be quite a lot of applications, depending on the specific task that you need to solve, but one of the most obvious motivations is the requirement to create your own chain of request handlers that does not interfere with the entire system processes and is determined solely at the level of your business logic. Also, the class interface has a sufficient level of abstraction and has enough functionality to implement various types of queues.
 3r33333.
 3r33333.

An example implementation in Laravel


 3r33333. We implement the most simple and remote from the reality chain of requests. As data, we will use the string "HELLO WORLD", and with the help of two handlers, we will form the string "Hello User" from it. The code is intentionally simplified.
 3r33333.
 3r33333. Before the direct implementation of our own "Pipe", we need to identify the elements of this pipe. Elements are written by analogy with middleware:
 3r33333.
 3r33333.
Defining handlers [/b]
StrToLowerAction.php:
 3r33333. 3r33354. 3r33333. use Closure; 3r33333. class StrToLowerAction
{
/**
* Handle an incoming request. 3r33333. *
* @param string $ content
* @param Closure $ next
* @return mixed
* /
public function handle (string $ content, Closure $ next)
{
$ content = strtolower ($ content); 3r33333. return $ next ($ content); 3r33333.}
}
3r33333. 3r33333.
 3r33333. SetUserAction.php:
 3r33333.
 3r33333. 3r33354. 3r33333. use Closure; 3r33333. class SetUserAction
{
/**
* Handle an incoming request. 3r33333. *
* @param string $ content
* @param Closure $ next
* @return mixed
* /
public function handle (string $ content, Closure $ next)
{
$ content = ucwords (str_replace ('world', 'user')); 3r33333. return $ next ($ content); 3r33333.}
}
3r33333. 3r33333.
 3r33333. 3r33386. 3r33386.
 3r33333. Then we create a “pipeline”, determine what data we want to send for it, determine through which collection of handlers we want to send this data, and also define a callback that receives our data passed through the entire chain as an argument. In the case when the data along the chain we remain unchanged - the part with the callback can be omitted:
 3r33333.
 3r33333. 3r33354. 3r33333. $ pipes =[
StrToLowerAction::class,
SetUserNameAction::class
]; 3r33333. 3r33333. $ data = 'Hello world'; 3r33333. 3r33333. $ finalData = app (Pipeline :: class)
-> send ($ data) //Data that we want to pass through
handlers. -> through ($ pipes) //Collection of processors 3r33333. -> then (function ($ changedData) {
return $ changedData; //Returns the data passed through the 3r33333 chain.}); 3r33333. 3r33333. var_dump ($ finalData); //The returned data is written to the $ finalData variable
3r33333. 3r33333.
 3r33333. Also, if you have a desire or need to define your own method in handlers, the Pipeline interface provides a special method 3r33348. via ('method_name') [/i] , then the processing chain can be written this way:
 3r33333.
 3r33333. 3r33354. 3r33333. $ finalData = app (Pipeline :: class)
-> send ($ data) 3r33333. -> through ($ pipes)
-> via ('handle') //There can be any method name here, you must guarantee its presence in all handlers 3r33333. -> then (function ($ changedData) {3r33333. return $ changedData;
}); 3r33333. 3r33333. 3r33333.
 3r33333. Directly, the data that we pass through processors can be absolutely anything, as well as interaction with them. Type hinting and setting the type of the object returned in the chain will help to avoid data integrity errors.
 3r33333.
 3r33333. 3r33333. Conclusion
 3r33333. Laravel provides a large number of built-in classes, and the flexibility of many of them allows us to develop something complicated with sufficient simplicity. This article examined the possibility of creating simple queues for requests based on the Pipeline class built into Laravel. Implementations of this class in the final code can be completely different, and the flexibility of this tool allows you to get rid of many unnecessary actions when building certain algorithms.
 3r33333.
 3r33333. How specifically to use this feature of the framework depends on the tasks assigned to you. 3r33386. 3r33333. 3r33333. 3r33333. 3r33383. ! function (e) {function t (t, n) {if (! (n in e)) {for (var r, a = e.document, i = a.scripts, o = i.length; o-- ;) if (-1! == i[o].src.indexOf (t)) {r = i[o]; break} if (! r) {r = a.createElement ("script"), r.type = "text /jаvascript", r.async =! ? r.defer =! ? r.src = t, r.charset = "UTF-8"; var d = function () {var e = a.getElementsByTagName ("script")[0]; e.parentNode.insertBefore (r, e)}; "[object Opera]" == e.opera? a.addEventListener? a.addEventListener ("DOMContentLoaded", d,! 1): e.attachEvent ("onload", d ): d ()}}} t ("//mediator.mail.ru/script/2820404/"""_mediator") () ();
3r33333. 3r33386. 3r33333. 3r33333. 3r33333. 3r33333.
+ 0 -

Add comment