Laravel Pipeline

8/11/2021 Laravel

# Description:

Laravel pipeline: design pattern xử lí object theo từng bước cho đến khi kết thúc. Khá giống với Chain of Responsibility pattern (opens new window). Hiểu đơn giản là bạn cung cấp 1 object và object để đi qua từng pipe để handle.

Vd dễ thấy nhất là laravel middleware, Request được xử lí bởi từng middleware cho đến khi pass toàn bộ.

Xem chi tiết class Pipeline tại Illuminate\Pipeline\Pipeline::class

# Laravel Pipeline class:

Các method của Pipeline

  1. Send($passable): Cung cấp đối tượng cho pipeline.

    $std = new StdClass()
    $std->name = 'foo';
    
    app(Pipeline::class)
    ->send($std);
    
    1
    2
    3
    4
    5
  2. though(array $pipes): Các pipe handle object.

    app(Pipeline::class)
    ->send($std)
    ->though([
        PipeFirstHandle::class
    ]);
    
    1
    2
    3
    4
    5

    Để biết cấu trúc của pipe, ta có thể xem tại method carry(). pipe có thể là 1 callback function

    $std = new \stdClass();
    $std->name = "foo";
    
    $pipeline = app(Pipeline::class)
        ->send($std)
        ->through([
            function ($ob, \Closure $next) {
                $ob->name = "bar";
                return $next($ob);
            }
        ])
        ->thenReturn();
    dd($pipeline);
    /*
    output: 
    {#274 ▼
      +"name": "bar"
    }
    */
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    Hoặc là 1 class: Laravel sẽ reflect class, khởi tạo object pipe và call method mặc định là handle() - có thể chỉ định 1 method khác qua method via();

    $std = new \stdClass();
    $std->name = "foo";
    $pipeline = app(Pipeline::class)
        ->send($std)
        ->through([
            PipeFirstHandle::class
        ])
        ->thenReturn();
    dd($pipeline);
    
    /* PipeFirstHandle::class */
    class PipeFirstHandle
    {
        public function handle($ob, \Closure $next)
        {
            $ob->name = "bar";
            return $next($ob);
        }
    }
    
    
    /*
    output: 
    {#274 ▼
      +"name": "bar"
    }
    */
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
  3. thenResult(): Run pipeline và trả về result.

  4. then($callback): Run pipeline với 1 callback cuối cùng. vd: Nếu pass qua các pipe rồi thì insert và db.

# Hub:

Nếu để ý ta có thể thấy là có 1 class Hub cũng nằng trong folder Illuminate\Pipeline và đã được binding trong Service Container với contract Illuminate\Contracts\Pipeline\Hub

Hub này mục đích để define nhiều pipeline và gán alias rồi có thể sử dụng cho nhiều đối tượng khác nhau.

// Vì Hub đã được binding trong Service Container nên có thể resolved HubContract để sử dụng
$pipelineHub = resolve(HubContract::class);

// define 1 pipeline 
$pipelineHub->pipeline('fooPipeline', function ($pipeline, $object) {
     return $pipeline
         ->send($object)
         ->through([
             Active::class
         ])
         ->thenReturn();
 });

// sử dụng pipe đã define bằng alias
$std = new \stdClass();
$std->name = "foo";
dd($pipelineHub->pipe($std, 'fooPipeline'));
/*
{#274 ▼
  +"name": "bar"
}
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22