该篇属于《Laravel底层核心技术实战揭秘》这一课程《laravel底层核心概念解析》这一章的扩展阅读。考虑到学员们的基础差异,为了避免视频当中过于详细而连篇累牍,故将一些laravel底层实现相关的PHP知识点以文章形式呈现,供大家预习和随时查阅。

基本上,使用laravel pipelines你可以将一个实例对象(object)在多个类之间传递,就像流水顺着管道依次流淌一般,最终呢,层层传递,你就得到了从头至尾一系列执行操作的“最终”结果。

当然,laravel里pipeline(管道、通道)相关的,最直接的例子就是Middleware了。借助这种通道方式,Middleware可以很方便地层层“过滤”(filter)进入你程序中的request。

下面是一个基本的Middleware例子:

<?php
namespace App\Http\Middleware;
use Closure;
class TestMiddleware
{
    
    public function handle($request, Closure $next)
    {
        // 这里添加自己的逻辑
        return $next($request);
    }
}

middleware就像是我们的request请求流经的“管道”,这期间可以执行必要的操作,比如判断当前请求是一个http请求呢,还是一个json请求呢,或者也可以检查一下用户的权限,或许这个时候你就会想到我们laratrust(或Entrust)插件一系列经典的Middleware。

《Laravel底层核心技术实战揭秘》课程里,涉及到service provider的加载注册原理和源码解读的时候,我们一起看过Illuminate\Foundation\Http\Kernel这个class,期间呢详细分析了sendRequestThroughRouter这个方法:

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());
}

注意return后面的那段,看字面意思都很好理解:

一个new Pipeline发送了(send)request请求,通过了(through)我们的Middleware,然后(then)提交给了路由。

例子:实现一个需要依次处理多个任务的类(class)逻辑

假设呢你在写一个论坛功能,用户呢可以创建话题、评论话题,但是呢,你的客户要求你实现一个能自动替换删除某些内容、符号的功能,数据处理了以后呢才能写入数据库,比如说具体的需求列表是这样的:

  1. 删除那些没有href的空的链接
  2. 将一些敏感词汇替换成星号*
  3. 删掉用户提交可能危险的script tag

很可能最后呢,你需要实现下面的这个处理过程:

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

我们需要的是,将需要处理的内容依次传递到这三个class中,上一个class的执行结果要传给下一个class来继续处理,那么类似的需求,我们就可以使用pipeline来实现:

<?php

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

每一个数据处理的class,都应该有一个handle方法来执行具体的逻辑,所以这个时候让它们都扩展或实现某个特定的interface比较好:

<?php
namespace App;

use Closure;

interface Pipe
{
    public function handle($content, Closure $next);
}

然后呢假设我们过滤敏感词的class就可以这样写:

<?php
namespace App;

use Closure;

class RemoveBadWords implements Pipe
{
    public function handle($content, Closure $next)
    {
        //具体的替换逻辑,更新掉$content
        return  $next($content);
    }
}

这里handle方法接收两个参数,第一个是你要在管道中传递的对象(passable object),第二个呢是一个Closure,也即是匿名函数,它指代你管道中的下一步,当前这一步的逻辑执行完了,就可以把结果传给下一步。

你也可以不用handle这个方法名,但这个时候你需要通过via方法来指定自定义方法的名字:

app(Pipeline::class)
 ->send($content)
 ->through($pipes)
 ->via(‘customMethodName’) // <---- 在这儿指定
 ->then(function ($content) {
     return Post::create(['content' => $content]);
 });

原文出处://medium.com/@jeffochoa/understanding-laravel-pipelines-a7191f75c351