zaengle/pipeline

在 Laravel Pipelines 之上的便捷功能

v4.0.0 2024-03-18 13:09 UTC

This package is auto-updated.

Last update: 2024-09-18 14:20:42 UTC


README

Tests Latest Version on Packagist Total Downloads MIT Licensed

apollo launch control

Zaengle Pipeline

在项目中使用 Laravel Pipelines 来处理复杂的数据流后,我们注意到一些模式出现

  • 数据库事务
  • 管道接口
  • 响应和异常处理

这个包在 Laravel Pipeline 上增加了便捷功能,并将它们整合到一个可重用的位置。

注意 - 更多详细示例请参阅 "示例" 目录。

安装

composer require zaengle/pipeline

测试

phpunit

基本类示例

管道是将数据、逻辑和响应/异常拆分为三个独立元素的一种常见模式。 Zaengle Pipeline 将这些部分抽象为有用的类,并为底层模式提供了一些结构。例如,让我们探索一下虚构的用户注册的管道可能是什么样子

<?php

use App\RegisterTraveler;
use App\Pipes\CreateUser;
use App\Pipes\HandleMailingList;
use Zaengle\Pipeline\Pipeline;

class FicticiousRegisterController {
    public function __invoke() 
    {
        $traveler = (new RegisterTraveler())->setRequest(request()->all());
        
        $pipes = [
            CreateUser::class,
            HandleMailingList::class,
            // any other items that need to happen during registration...
        ];
    
        $response = app(Pipeline::class)->pipe($traveler, $pipes, $useTransactions = true);
    
        if ($response->passed()) {
            return response()->json('Welcome!');
        } else {
            return response()->json('Your registration failed with the following error: ' . $response->getMessage());
        }
    }
}

分解

创建旅行者

使用 Zaengle Pipeline 的第一步是创建一个数据旅行者类。 注意:以下示例中使用的 setRequest() 方法是人为设计的。

$traveler = (new RegisterTraveler())->setRequest(request()->all());

Zaengle\Pipeline\Contracts\AbstractTravelerZaengle\Pipeline\Pipeline 类提供了额外的用于方法。

<?php
use Zaengle\Pipeline\Contracts\AbstractTraveler;

class RegisterTraveler extends AbstractTraveler {
}

$traveler 中可以设置任何所需的数据,并且它将在任何管道中可用。

<?php
use Zaengle\Pipeline\Contracts\AbstractTraveler;

class RegisterTraveler extends AbstractTraveler {

  private $request;

  private $user;
    // custom methods
    public function setRequest($request)
    {
        $this->request = $request;
        return $this;
    }

    public function getRequest()
    {
        return $this->request;
    }

    public function setUser($user)
    {
        $this->user = $user;
        return $this;
    }

    public function getUser()
    {
        return $this->user;
    }
}

管道

将业务逻辑分离到合适的 "管道" 中,每个管道都应该实现 Zaengle\Pipeline\Contracts\PipeInterface

<?php

use App\User;
use Zaengle\Pipeline\Contracts\PipeInterface;

class CreateUser implements PipeInterface {
    public function handle(RegisterTraveler|AbstractTraveler $traveler, \Closure $next): RegisterTraveler
    {
        $traveler->setUser(
            User::create([
                'email' => $traveler->getRequest()->email,
                'password' => $traveler()->getRequest()->password,
            ])
        );
        
        return $next($traveler);
    }
}
<?php

use App\MailingService;
use Zaengle\Pipeline\Contracts\PipeInterface;

class HandleMailingList implements PipeInterface
{
    public function handle(RegisterTraveler|AbstractTraveler $traveler, \Closure $next): RegisterTraveler
    {
        if ($traveler->getRequest()->subscribe) {
            MailingService::subscribe($traveler->getUser()->email);
            
            $traveler->getUser()->update([
                'subscriber' => true,
            ]);
        }
        
        return $next($traveler);
    }
}

主管道

一旦你有了数据和管道,就可以通过 Zaengle\Pipeline\Pipeline->pipe() 方法发送它们。

pipe() 接受三个参数,其中两个是必需的。第一个参数应该是你的 $traveler,第二个是管道数组,第三个是可选参数,告诉 Pipeline 是否使用事务。

// use Zaengle\Pipeline\Pipeline;

$response = app(Pipeline::class)->pipe($traveler, $pipes, $useTransactions = true);

结果

$traveler 通过数据管道发送后,你可以访问一个 ->passed() 方法,该方法指示管道是否成功完成。

$response = app(Pipeline::class)->pipe($traveler, $pipes, $useTransactions = true);

if ($response->passed()) {
    // Handle pass
    dump($response->getMessage());
} else {
    // Handle fail
    // $response->getException();
    // $response->getMessage();
    // $response->getStatus();
}

AbstractTraveler 允许你访问以下便捷方法

$response->passed()

一个布尔值,指示旅行者是否成功通过所有管道而没有任何异常。

$response->getStatus()

你可以使用 ->setStatus() 设置的字符串,或者自动设置为 'ok' 或 'fail'。

$response->getException()

要终止过程,你可以抛出一个异常,管道将在响应中捕获它。它还将设置状态和信息,让你可以访问 $response->getMessage()

$response->setMessage()

在异常情况下,将自动设置 $message 属性。否则,你可以在管道执行的任何时刻设置它。

$response->getMessage()

管道完成后可用的字符串。

测试策略

测试管道时,应该测试整体管道,以确保给定的输入与预期输出相匹配。

<?php
use TestCase;

class FicticiousRegistrationTest extends TestCase 
{
    /** @test */
    public function it_registers_a_user()
    {
        $userStub = factory(User::class)->make();

        $response = $this->postJson('ficticious-endpoint', [
            'email' => $userStub->email,
            'password' => 'password',
            'subscribe' => true,
        ]);
        
        $response->assertJsonFragment('Welcome!');
    
        $this->assertDatabaseHas('users', [
            'email' => $userStub->email,
            'subscribed' => true,
        ]);  
    }
}

你还可以测试单个管道,例如这样

<?php

use TestCase;
use App\User;
use App\RegisterTraveler;
use App\Pipes\CreateUser;

class CreateUserTest extends TestCase {
    /** @test */
    public function it_creates_a_user()
    {
        $traveler = (new RegisterTraveler)->setRequest(['email' => 'test', 'password' => 'password']);
        
        (new CreateUser)->handle($traveler, function () {});
        
        $this->assertInstanceOf(User::class, $traveler->getUser());
    }
}

许可

MIT 许可证 (MIT)。有关更多信息,请参阅 许可文件

致谢