linusshops / pipeline
一个逐步处理的简单工具。
v1.0.0
2023-07-31 18:55 UTC
Requires (Dev)
- phpunit/phpunit: ^9.5
This package is auto-updated.
Last update: 2024-08-31 00:33:35 UTC
README
基于Laravel的管道,这个小巨人可以在任何对象上逐步处理。更多文档请参阅https://laravel.net.cn/docs/10.x/helpers#pipeline。
目录
安装
此软件包可以通过Composer安装。
composer require linusshops/pipeline
测试
可以使用PHPUnit运行测试。安装后运行:
vendor/bin/phpunit
概述
想象一下,你的系统中发生了一个事件,比如上传了一个CSV文件,并触发了某些操作,
- 文件被验证(例如大小、扩展名、MIME类型等)
- 文件被处理(例如解析并提取到关系型数据库中)
- 文件被归档(例如移动到远程共享中)
- 需要发送通知(例如向业务用户和最终用户发送电子邮件)
你可能有一些对象负责这些步骤中的每一个,
- 一个
$validator
- 一个
$processor
- 一个
$archiver
- 一个
$notifier
很好。在一个传统的面向对象应用中,调用代码可能如下所示,
$validator->validate($file); $processor->process($file); $mover->move($file); $notifier->notify($file);
很好。这没有错。这种逻辑可以存在于任何地方,但在传统的MVC应用中,它可能存在于控制器、模型或这些的辅助类中。
那么,管道是如何做到这一点的呢?
(new Pipeline()) ->send($file) ->through([ $validator, $processor, $archiver, $notifier ]) ->thenReturn();
每个管道将接收到文件对象,可以自由地对其进行任何操作。完成工作后,它可以调用下一个管道,也可以不调用。让我们看看一些示例。
管道可以是类
class Validate { public function handle(File $file, Closure $next) { // ... business logic ... return $next($file); } } class Process { public function handle(File $file, Closure $next) { // ... business logic ... return $next($file); } } class Archive { public function handle(File $file, Closure $next) { // ... business logic ... return $next($file); } } class Notify { public function handle(File $file, Closure $next) { // ... business logic ... return $next($file); } } $pipes = [ new Validate(), new Process(), new Archive(), new Notify() ]; (new Pipeline()) ->send(new File()) // Start with a file ->through($pipes) // validate, process, archive, notify ->thenReturn();
管道可以是闭包
$pipes = [ // Multiply by 10 function ($input, $next) { // Modify the input $input = $input * 10; // Run the next pipe with the modified input return $next($input); }, // Divide by 5 function ($input, $next) { // Modify the input $input = $input / 5; // Run the next pipe with the modified input return $next($input); }, // Add 1 function ($input, $next) { // Modify the input $input = $input + 1; // Run the next pipe with the modified input return $next($input); }, ]; $output = (new Pipeline()) ->send(10) // Start with 10 ->through($pipes) // Multiply by 10, divide by 5, add 1 ->thenReturn(); // Output: 21
管道可以中止进一步处理
$pipes = [ fn($input, $next) => $next($input . 'A'), function ($input, $next) { // Abort further processing by returning the current $input. // The important part is that we don't call `$next($input)`. // We can return anything, false, null, $input etc. as long as it doesn't // Run the next pipe with the modified input. if ($input === 'A') { return $input; } // The remainder of this, as well as the next pipe, // will not execute. $input .= 'B'; return $next($input); }, fn($input, $next) => $next($input . 'C'), ]; $output = (new Pipeline()) ->send('') // Start with an empty string ->through($pipes) // Append A. Immediately stop further processing and return A. ->thenReturn(); // Output: A
管道可以将自己移动到管道末尾
$pipes = [ function ($input, $next) { // Immediately run the next pipes and get their results. $result = $next($input); $result .= 'A'; return $result; }, fn($input, $next) => $next($input . 'B'), fn($input, $next) => $next($input . 'C'), ]; $output = (new Pipeline()) ->send('') // Start with an empty string ->through($pipes) // The first pipe immediately calls the next pipe, so we move on to B, then C, then finally A is run at the end. ->thenReturn(); // Output: BCA