baron / pipeline

递归管道构造

dev-master 2024-03-19 08:00 UTC

This package is auto-updated.

Last update: 2024-09-19 08:59:21 UTC


README

使用这个库,我们可以将长执行分离成多个任务。管道使你能够更好地满足单一职责的要求。这导致更好的可维护性和可测试性。

如何使用

  • 管道将有效载荷及其自身传递给任务。

public function __invoke(PayloadInterface $payload, PipelineInterface $pipeline): PayloadInterface;

  • 第一个任务调用下一个任务,调用下一个任务,调用下一个任务...

$payloadAfterAllSteps = $pipeline->handle($payload);

  • 就像在 PSR-7(请求对象)中一样,你现在能够通过添加和从有效载荷对象获取信息来处理所有信息
$value = $genericPayload->getValue();
$value++;
$genericPayload->setValue($value);
步骤详解
  1. 创建任务(s)
  2. 初始化管道
  3. 填充有效载荷
  4. 执行管道
  5. 评估管道

使用管道服务

你可以简单地从你的DI(需要PSR-11容器服务管理器)创建管道。这只需要两行代码(+配置)

PipelineService 类允许你将任务作为服务散列传递。

// .. In your factory
$tasksFromEnvConfig = $config->getTasks() // somewhere in your config: [Task1::class, Task2::class, Task3::class];
$pipeline = (new PipelineService)->createPsr11($serviceManager, $tasksFromEnvConfig);

使用 mbaron/Pipeline,你可以快速创建复杂任务

// Configuration of BiPRO Request (= German XML Request Standard)
$tasklist = [
    CheckServiceAvailabilityTask::class,
    [ // do Request
        ErrorHandlerTask::class, // catch execution of submission even on error
        [
            PrepareDataTask::class,
            ValidateDataTask::class,
            DoGetOfferRequestTask::class
        ],
        // .. some additional things like set quote, upload documents etc.
    ],
    [ // do something additionally
        LogResultLocalyTask::class,
        LogResultInDWTask::class,
    ]
];

最佳实践

  • 使用 RecursivePipeline 创建“子管道”=> 动态任务
  • 由于结构,每个任务都有一个“处理下一个步骤之前”和“处理下一个步骤之后”
  • 通过抛出异常或返回未处理的下一个步骤的有效载荷来中断管道

示例

手动初始化管道

当然,最好总是使用DI而不是new

// Create Tasks in order of execution
$tasks = [
    new ExceptionTask(),
    new ErrorBagTask(),
    new OpenStreamTask(),
    new ReadValuesTask(),
    new ValidateValuesTask(),
    new RequireDocumentsTask(),
    new HandleImportTask(),
];

// Create Pipeline
$pipeline = new \mbaron\Pipeline\Pipeline($tasks);

// Create Payload
$payload = new ImportPayload();

// Setup Payload
$payload->setEnvironment($env); // <= here you also could use $pipeline->setOptions(...)
$payload->setConfig($config);

// Do the thing the pipeline is constructed for
$payload = $pipeline->handle($payload);
// Evaluate result
$payload->getMessage();
简单的执行某些操作的任务

使用管道对象传递配置和处理任务之间的数据

public function __invoke(PayloadInterface $payload, PipelineInterface $pipeline): PayloadInterface {
    // there is something, so we need to continue with our tasks
    if ($payload->getValue() > 0) {
        return $pipeline->handle($payload);
    }
    
    // we do not allow negative values => Completely interrupt process
    if ($payload->getValue() < 0) {
        throw new \Exception("Negative Values are not allowed here");
    }

    // Value is 0, let's pretend there is nothing more to do. => Interrupt from here and go back up the callstack
    return $payload;
}
异常处理任务

在管道中处理(可能只是某些)异常

public function __invoke(PayloadInterface $payload, PipelineInterface $pipeline): PayloadInterface {
    try {
        // handle next steps BEFORE doing something
        $payload = $pipeline->handle($payload);
    } catch (\Throwable $e) {
        $this->logger->logException($e, ['context' => $payload]);
    }
    return $payload;
}
错误包

使用错误包处理多个错误

public function __invoke(PayloadInterface $payload, PipelineInterface $pipeline): PayloadInterface {
    // Do something before handle next steps
    $payload->setErrorBag($this->errorBag);

    // Pipeline will add errors to error bag
    $payload = $pipeline->handle($payload);

    // "On the way back" and after handling, we check if something is inside the error bag
    if ($payload->getErrorBag()->hasErrors()) {
        $payload->setMessage($payload->getErrorBag()->toString());
    }
    
    return $payload;
}