blog posts

What is pipeline in laravel ?

In some cases, your project may need to make several changes before moving. You may sometimes make these changes with a certain logic and order. In this article, we will talk about the pipeline in Laravel and show you how to make a series of corrections and operations before performing the main operation in Laravel.

What is a pipeline in Laravel?

To clarify, the concept of Pipeline can be defined as follows.

The pipeline accepts a passable object, sends some value by the developer, applies regions through a class called pipettes, when the last pipe is executed and finished. , Only at this time (then) returns the final result to you.

The full class is as follows.

<? php 
namespace  Illuminate \ Pipeline ;
use  Closure ;
use  RuntimeException ;
use  Illuminate \ Http \ Request ;
use  Illuminate \ Contracts \ Container \ Container ;
use  Illuminate \ Contracts \ Support \ Responsable ;
use  Illuminate \ Contracts \ Pipeline \ Pipeline  as  PipelineContract;
class  Pipeline  implements  PipelineContract
 {
     / **
     * The container implementation.
     *
     * @var \ Illuminate \ Contracts \ Container \ Container
     * / 
    protected  $ container ;
    / **
     * The object being passed through the pipeline.
     *
     * @var mixed
     * / 
    protected  $ passable ;
    / **
     * The array of class pipes.
     *
     * @var array
     * / 
    protected  $ pipes = [];
    / **
     * The method to call on each pipe.
     *
     * @var string
     * / 
    protected  $ method = 'handle' ;
    / **
     * Create a new instance class.
     *
     * @param   \ Illuminate \ Contracts \ Container \ Container | null $ container
     * @return void
     * / 
    public  function  __construct ( Container $ container = null )
     {
         $ this -> container = $ container ;
    }
    / **
     * Set the object being sent through the pipeline.
     *
     * @param   mixed $ passable
     * @return $ this
     * / 
    public  function  send ( $ passable )
     {
         $ this -> passable = $ passable ;
        return  $ this ;
    }
    / **
     * Set the array of pipes.
     *
     * @param   array | mixed $ pipes
     * @return $ this
     * / 
    public  function  through ( $ pipes )
     {
         $ this -> pipes = is_array ( $ pipes )? $ pipes : func_get_args ();
        return  $ this ;
    }
    / **
     * Set the method to call on the pipes.
     *
     * @param   string $ method
     * @return $ this
     * / 
    public  function  via ( $ method )
     {
         $ this -> method = $ method ;
        return  $ this ;
    }
    / **
     * Run the pipeline with a final destination callback.
     *
     * @param   \ Closure $ destination
     * @return mixed
     * / 
    public  function  then ( Closure  $ destination )
     {
         $ pipeline = array_reduce (
            array_reverse ( $ this -> pipes), $ this -> carry (), $ this -> prepareDestination ( $ destination )
        );
        return  $ pipeline ( $ this -> passable);
    }
    / **
     * Run the pipeline and return the result.
     *
     * @return mixed
     * / 
    public  function  thenReturn ()
     {
         return  $ this -> then ( function ( $ passable ) {
             return  $ passable ;
        });
    }
    / **
     * Get the final piece of the Closure onion.
     *
     * @param   \ Closure $ destination
     * @return \ Closure
     * / 
    protected  function  prepareDestination ( Closure  $ destination )
     {
         return  function ( $ passable ) use ( $ destination ) {
             return  $ destination ( $ passable );
        };
    }
    / **
     * Get a Closure that represents a slice of the onion application.
     *
     * @return \ Closure
     * / 
    protected  function  carry ()
     {
         return  function ( $ stack , $ pipe ) {
             return  function ( $ passable ) use ( $ stack , $ pipe ) {
                 if (is_callable ( $ pipe )) {
                     // If the pipe is an instance of a Closure, we will just call it directly but 
                    // otherwise we'll resolve the pipes out of the container and call it with 
                    // the appropriate method and arguments, returning the results back out.
                    return  $ pipe ( $ passable , $ stack );if elseif (! is_object ( 
                $ pipe )) {
                    [ $ name , $ parameters ] = $ this -> parsePipeString ( $ pipe );
                    // If the pipe is a string we will parse the string and resolve the class out 
                    // of the dependency injection container. We can then build a callable and 
                    // execute the pipe function giving in the parameters that are required. 
                    $ pipe = $ this -> getContainer () -> make ( $ name );
                    $ parameters = array_merge ([ $ passable , $ stack ], $ parameters );
                } else {
                     // If the pipe is already an object we'll just make a callable and pass it to 
                    // the pipe as-is. There is no need to do any extra parsing and formatting 
                    // since the object we're given was already a fully instantiated object. 
                    $ parameters = [ $ passable , $ stack ];
                }
                $ response = method_exists ( $ pipe , $ this -> method)
                                ? $ pipe -> { $ this -> method} (... $ parameters )
                                : $ pipe (... $ parameters );
                return  $ response  instanceof Responsable
                            ? $ response -> toResponse ( $ this -> getContainer () -> make (Request :: class))
                            : $ response ;
            };
        };
    }
    / **
     * Parse full pipe string to get name and parameters.
     *
     * @param   string $ pipe
     * @return array
     * / 
    protected  function  parsePipeString ( $ pipe )
     {
        [ $ name , $ parameters ] = array_pad (explode ( ':' , $ pipe , 2 ), 2 , []);
        if (is_string ( $ parameters ))
             parameters $ parameters = explode ( ',' , $ parameters );
        }
        return [ $ name , $ parameters ];
    }
    / **
     * Get the container instance.
     *
     * @return \ Illuminate \ Contracts \ Container \ Container
     *
     * @throws \ RuntimeException
     * / 
    protected  function  getContainer ()
     {
         if (! $ this -> container) {
             throw  new  RuntimeException ( 'A container instance has not been passed to the Pipeline.' );
        }
        return  $ this -> container;
    }
}

 

The Pipeline class provides general methods that we can work with.

  • The send method will be the values ​​sent to the Pipe class.
  • And the through the method, which accepts the Pipe class or classes.
  • The via the method, the input of which will be a method of class Pipe and the code in this method will be executed and is optional.
  • Finally, the then method will run, which accepts a function and executes the Pipes.

Pipeline needs Service Container (Read the Service Container article to understand more about the concept.) Because Pipeline uses Service Container to resolve this issue when an object is passed to it.

One of the most common examples of operations is Middleware operations, which are involved in filtering user requests. Middleware‌, for example, checks whether or not it needs authentication in Routes and checks whether or not it needs to log in and authenticate before performing any operation when the user sends a request to the Route.

Looking at the Illuminate \ Foundation \ Http \ Kernel class, you will learn how to run Middleware‌.

The following method is how to run Middleware‌.

<? php 
    / **
     * Send the given request through the middleware / router.
     *
     * @param   \ Illuminate \ Http \ Request $ request
     * @return \ Illuminate \ Http \ Response
     * / 
    protected  function  sendRequestThroughRouter ( $ request )
     {
         $ this -> app-> instance ( 'request' , $ request );
        Facade :: clearResolvedInstance ( 'request' );
        $ this -> bootstrap ();
        return ( new Pipeline ( $ this -> app))
                    -> send ( $ request )
                    -> through ( $ this -> app-> shouldSkipMiddleware ()? []: $ this -> middleware)
                    -> then ( $ this -> dispatchToRouter ());
    }

 

Start with Pipeline in Laravel

In the first step of creating a pipeline, we need to create an instance of it. We can use the app helper function and send the Pipeline class to it to do this.

$ result = app (\ Illuminate \ Pipeline \ Pipeline :: class);

Now we can apply the pipeline methods mentioned above, which I will explain below.

Send an object to Pipeline in Laravel

As mentioned in the list of general pipeline methods, the send method can accept a good object and send it to the desired pipe class. The input of the send method can be an instance of an object. For example, it can be an array, a string, a collection, etc.

$ result = app (\ Illuminate \ Pipeline \ Pipeline :: class)
    -> send ( 'this should be correctly formatted' );

Remember that the object is sent with the address or so-called reference, and you do not need to save it in a new variable. This is true of models, collections, and anything that can be prototyped.

Pipe class

Using the through a method in the Pipeline class, we can specify to which class this object should be sent so that the desired operation can be performed on it. This method is implemented as follows.

$ result = app (\ Illuminate \ Pipeline \ Pipeline :: class)
    -> send ('this should be correctly formatted')
    -> through (
        function ( $ passable , $ next ) {
           return  $ next (ucfirst ( $ passable ));
        },
        AddPunctuation :: class,
        new RemoveDoubleSpacing (),
        InvokableClassToRemoveDuplicatedWords :: class
     );

The through method is the most important part of the Pipeline. In its input, we can send an array of acceptable items of this method or even define a function in the input. The input of this function is called Pipe.

Pipes can be as follows:

  • A class created through Service Containers.
  • Closure or invokable class
  • An example of an object

In the Pipe class, there must be a method called to handle that will execute the code of this method. But we can change this method if necessary. In the following, we will examine this issue.

Pipe class custom method

There is a method called via in the Pipeline class, which we mentioned above. This method is not mandatory, and the pipeline operation is performed in the handle method in the Pipe class by default.

We can rename this method and assign it to another method in the Pipe class.

For example, you may have used the handle method to perform another operation, and now you want to perform another operation in the same class.

$ result = app (\ Illuminate \ Pipeline \ Pipeline :: class)
    -> send ( 'this should be correctly formatted' )
    -> through ([
        ...
    ])
    -> via ( 'modifyString' );

Pipeline results in Laravel

The last step is to run the Pipeline, which is run via the then or thenReturn method. The Pipeline itself is in Sleep mode until this method is executed.

The then method receives a packet (Closure), and the function input in this method is the result of the last pipe executed. For example, when we put text in two Pipes to remove ugly words and delete script tags, the input of the function in the then method will be the result of the last Pipe, that is, text without ugly words and script tags.

You can also use the thenReturn method. This method returns only the last result of the Pipeline Pipes and completes the Pipeline.

This method does not take input.

$ result = app (\ Illuminate \ Pipeline \ Pipeline :: class)
    -> send ( 'this should be correctly formatted' )
    -> through (...)
    -> via ( 'modifyString' )
    -> thenReturn ();

 

<? php
app (Pipeline :: class)
    -> send ( $ content )
    -> through ( $ pipes )
    -> then ( function ( $ content ) {
         return Post :: create ([ 'content' => $ content ]);
    });

Implementing Pipeline in Laravel with a simple example

Suppose we are setting up a forum, and we want the following three things to be done when posting.

  • Link tags should be written normally
  • Write bad words as *.
  • Delete the script tag completely from the text.

We need to define three classes to perform each of the above operations to do this. So our Pipes‌ will be as follows.

$ pipes = [
    RemoveBadWords :: class
    ReplaceLinkTags :: class
    RemoveScriptTags :: class
];

Each of the above classes will do one of the operations mentioned.

Next, the method that is supposed to record and save the desired content must first call a Pipeline to perform the above operations on the text of the content, and then the content can be saved. For example, the registration code will be as follows.

<? php 
public  function  create ( Request $ request )
 {
     $ pipes = [
        RemoveBadWords :: class,
        ReplaceLinkTags :: class,
        RemoveScriptTags :: class
    ];
    $ post = app (Pipeline :: class)
        -> send ( $ request -> content)
        -> through ( $ pipes )
        -> then ( function ( $ content ) {
             return Post :: create ([ 'content' => 'content' ]);
        });
    // return any type of response 
}

In the post variable, the function’s return value called in the then method will always be.

An array of pipettes can be sent to the input to the Pipeline class and the through method.

The Pipeline class performs operations on the pipes at the input of the through method through a method called handle. Each Pipe class needs to have a method called handle to execute its code in the Pipeline.

It would be good to create an interface for Pipe classes that makes the handle method mandatory.

<? php 
namespace  App ;
use  Closure ;
interface  Pipe
 {
     public  function  handle ( $ content , Closure  $ next ) ;
}

After that, the classes we define need to follow this interface, and implements must not be forgotten.🙂

For example, the RemoveBadWord class would look like this:

<? php 
namespace  App ;
use  Closure ;
class  RemoveBadWords  implements  Pipe
 {
     public  function  handle ( $ content , Closure  $ next )
     {
         // Here you perform the task and return the updated $ content 
        // to the next pipe 
        return   $ next ( $ content );
    }
}

The handle method will have two inputs. The first input must be a good object, and the second is a packet to which the object is sent after the pipe is piped.

Now you can write all your Pipes as you wish.

Besides the handle method, you can also use another method in the Pipe class.

<? php
app (Pipeline :: class)
    -> send ( $ content )
    -> through ( $ pipes )
    -> via ('customMethodName') // <---- This one :) 
    -> then ( function ( $ content ) {
       return Post :: create ([ 'content' => $ content ]);
    });

Via the method, you can enter the name of the method you want in the Pipe class to operate it.

Conclusion:

In this article, we talked about a feature in Laravel called Pipeline, which few people know about and has not even been documented. With Pipeline, you can modify items if you need to. For example, you need to make a series of corrections to a text before saving it and then saving it. The pipeline allows you to. We examined all the methods of the Pipeline class and said that you could set up a Pipeline by creating one Pipeline class and several Pipe classes, and then we explained the use of the Pipeline by giving an example. If you have any questions or experiences about Pipeline at Laravel, please share them with us at SunLearn.