shampine / sequence
一个现代的管道包
Requires
- php: ^8.1|^8.2
- league/pipeline: ^1.0
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
README
一个框架无关的管道包,用于处理基于The PHP League Pipeline Package的完整请求。
Laravel 示例 - https://github.com/shampine/sequence-demo
教程 - https://medium.com/gosteady/day-5-sequence-how-to-guide-56c0af1b2303
原因
使用管道模式,开发者可以快速开发、回收流程和测试一切。
使用管道为 MVC 框架带来的好处包括
- 苗条的、一致的控制器
- 能够在不同的管道之间共享流程
- 简单地将服务或仓库类注入到流程中,以保持代码整洁
- 轻松测试单个流程
- 清晰、一致的 API 响应
- 消除在堆栈内部尝试/捕获异常的需要
安装
composer require shampine/sequence
使用
以下示例使用 Laravel 惯例,但此包是无框架的。
请参阅这三个文件,以获取详细的使用示例和 phpunit 测试中的实时演示。
有效载荷
这是活动工作区。有效载荷在通过每个阶段时被修改。任何需要从一个阶段到另一个阶段的数据都需要在有效载荷上设置,然后从有效载荷中检索。
当定义您的有效载荷时,您可以可选地定义一个 $allowlist
和 $overrides
。
$allowlist = ['email']; // Only hydrate `email` from post/patch $overrides = ['email_address' => 'email']; // Allows `email_address` to be hydrated as `email` $payload = new \Sample\SamplePayload($allowlist, $overrides);
允许列表将限制用户提供的输入将填充到有效载荷中。重写参数允许将不同的外部键映射到内部键。例如,如果帖子包含 email_address
,但在有效载荷中方法被调用为 setEmail
。映射 ['email_address' => 'email']
将正确地对齐填充。
管道组合
管道可以在 $pipelines
属性中存储多个命名的闭包。这将允许将类似的管道分组在一起,例如 GET、POST、PATCH、DELETE 管道。您可以通过类构造函数或闭包构造函数将属性传递到管道中。
服务、仓库和其他依赖注入参数最好通过类构造函数设置。而标志和其他阶段相关属性可以通过使用 ->process($pipelineName, $payload, ...$argments)
注入。
此示例管道在构造函数中注入了服务,但在 ->process()
上的 $arguments 参数通过两个布尔标志传递。
class SamplePipeline extends AbstractPipeline { public const SAMPLE_PIPELINE = 'SamplePipeline'; public function __construct(?SampleUseService $sampleUseService = null) { $this->pipelines = [ self::SAMPLE_PIPELINE => static function( bool $validationFailure = false, bool $sequenceFailure = false ) use ($sampleUseService) { return (new Pipeline) ->pipe(new ValidationExceptionProcess($validationFailure, $sampleUseService)) ->pipe(new SequenceExceptionProcess($sequenceFailure)) ->pipe(new HydrateResponseProcess(SampleResponsePayload::class)); } ]; $this->excludeWhenEmpty = [ 'empty_value', ]; $this->excludeWhenNull = [ 'null_value', ]; } }
$excludeWhenEmpty
或 $excludeWhenNull
属性将检查任何根或数据键的值是否为 empty()
或 === null
。如果是这样,它们将被排除在最终数组之外,所有键都应该使用 snake_case
。
响应
响应是最终的输出容器,应该在管道的最终阶段进行填充。类上的所有属性都可以有 getter,如果没有 getter,则属性将被神奇地访问。
public function __construct(SamplePayload $payload) { $this->setSampleAbout('This is an about statement.'); }
在格式化过程中,将使用 getSampleAbout
编译最终数组,该数组将作为 json 返回。
控制器示例(Laravel)
使用依赖注入在控制器中实例化管道。
class SampleController { public function __construct(SamplePipeline $samplePipeline) { $this-samplePipeline = $samplePipeline; } }
GET
public function get(Request $request) { $payload = new SamplePayload(); $response = $this->samplePipeline->process(SamplePipeline::SAMPLE_PIPELINE, $payload)->format(); return response()->json($response, $response['http_code']); }
POST
public function post(Request $request) { $payload = (new SamplePayload())->hydratePost($request->all()); $response = $this->samplePipeline->process(SamplePipeline::SAMPLE_PIPELINE, $payload)->format(); return response()->json($response, $response['http_code']); }
PATCH
Patch 负载数据需要 PatchInterface
和 PatchTrait
。有效载荷将包含解析请求要修补的内容的方法 ->getPatch()
,以及有效载荷是否为修补请求 ->isPatch()
。
public function patch(Request $request) { $payload = (new SamplePayload())->hydratePatch($request->all()); $response = $this->samplePipeline->process(SamplePipeline::SAMPLE_PIPELINE, $payload)->format(); return response()->json($response, $response['http_code']); }
异常
包括两个异常,ValidationException 和 SequenceException。这两个异常都会被捕获并转换为 JSON 格式。您可以通过扩展这些类来定义特定的异常。它们与正常的有效负载以相同的方式被捕获和渲染,以便于返回 JSON。
class FetchUser extends AbstractProcess { public function process($payload) { $user = $this->userService->getUserRepository()->fetchUserById($payload->getId()); if ($user === null) { throw new SequenceException(1000, 'User not found', 400); } $payload->setUser($user); return $payload; } }
返回空的用户
{ "error_code": 1000, "status_code": 400, "data": null, "message": "User not found", "error_messages": null }
任何标准的 PHP 异常都将以正常的方式导致应用程序崩溃。在扩展 ValidationException 或 SequenceException 的异常类构造函数中应该存在日志记录。
测试
要运行所有测试,请运行 ./tests/run.sh
。
这将执行
- phpstan 级别 8
- phpunit 与代码覆盖率(期望 100% 覆盖率)
许可
MIT