PHP code example of wundii / flowcrafter

1. Go to this page and download the library: Download wundii/flowcrafter library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

wundii / flowcrafter example snippets


use Wundii\Flowcrafter\AbstractMessage;
use Wundii\Flowcrafter\Interface\MessageDataInterface;
use Wundii\Flowcrafter\Interface\MessageInitInterface;
use Wundii\Flowcrafter\Interface\MessageReturnInterface;

readonly class OrderInit extends AbstractMessage implements MessageInitInterface
{
    public function __construct(private string $sku) {}
    public function getSku(): string { return $this->sku; }
}

readonly class OrderValidated extends AbstractMessage implements MessageDataInterface
{
    public function __construct(private string $sku, private int $quantity) {}
    public function getSku(): string { return $this->sku; }
    public function getQuantity(): int { return $this->quantity; }
}

readonly class OrderCompleted extends AbstractMessage implements MessageReturnInterface
{
    public function __construct(private string $summary) {}
    public function getSummary(): string { return $this->summary; }
}

use Wundii\Flowcrafter\Interface\MessageDataInterface;
use Wundii\Flowcrafter\Interface\MessageReturnInterface;
use Wundii\Flowcrafter\Interface\StepInterface;

class ValidateStep implements StepInterface          // Init → Data
{
    public function __construct(private readonly OrderInit $init) {}

    /** @return class-string[] */
    public function returnTypes(): array { return [OrderValidated::class]; }

    public function process(): MessageDataInterface
    {
        return new OrderValidated($this->init->getSku(), quantity: 1);
    }
}

class CompleteOrderStep implements StepInterface     // Data → Return (beendet den Flow)
{
    public function __construct(private readonly OrderValidated $validated) {}

    /** @return class-string[] */
    public function returnTypes(): array { return [OrderCompleted::class]; }

    public function process(): MessageReturnInterface
    {
        return new OrderCompleted(sprintf(
            'Order %s x%d completed',
            $this->validated->getSku(),
            $this->validated->getQuantity(),
        ));
    }
}

class AuditStep implements StepInterface             // Data → bool (FlowResult)
{
    public function __construct(private readonly OrderValidated $validated) {}

    /** @return class-string[] */
    public function returnTypes(): array { return []; }

    public function process(): bool
    {
        return $this->validated->getQuantity() > 0;
    }
}

use Wundii\Flowcrafter\Attribute\FlowGroup;
use Wundii\Flowcrafter\FlowBuilder;
use Wundii\Flowcrafter\FlowSchema;
use Wundii\Flowcrafter\Interface\FlowInterface;

#[FlowGroup('Order Management')]   // optionale UI-Gruppierung, ohne Einfluss auf den Schema-Hash
class OrderFlow implements FlowInterface
{
    public static function schema(): FlowSchema
    {
        $builder = new FlowBuilder('flow.order.v1', OrderInit::class, OrderCompleted::class);
        $builder->addStep(ValidateStep::class);
        $builder->addStep(CompleteOrderStep::class, retries: 3, delay: 500);
        $builder->addStep(AuditStep::class);
        return $builder->build();
    }
}

use Wundii\Flowcrafter\FlowRunner;

$flowRunner = new FlowRunner(
    type: 'flow.order.v1',
    flowSource: OrderFlow::class,
    flowSubject: 'sku-42',   // optionaler Geschäfts-Key zur späteren Suche
    storage: $storage,       // aus $flowcrafterConfig->getStorage()
);

$result = $flowRunner->run(new OrderInit('sku-42'));   // MessageReturnInterface|bool

$queue = $flowcrafterConfig->getQueue();

$queue->appendObserveItem(
    type: 'flow.order.v1',
    flowSource: OrderFlow::class,
    flowHash: null,                 // null = neuer Flow, sonst Re-Run einer bestehenden Instanz
    messageSource: OrderInit::class,
    message: (new OrderInit('sku-42'))->jsonSerialize(),
    flowSubject: 'sku-42',
);

use Wundii\Flowcrafter\Attribute\FlowSchedule;
use Wundii\Flowcrafter\Schedule\AbstractSchedule;

#[FlowSchedule('0 */6 * * *', name: 'order-cleanup', group: 'Maintenance')]
class OrderCleanupSchedule extends AbstractSchedule
{
    public function process(): void
    {
        $this->enqueue(OrderFlow::class, new OrderInit('scheduled-cleanup'));
        // oder synchron: $this->run(...)
    }
}

$builder->addStep(ExternalApiStep::class, retries: 3);             // 3 Versuche, 200 ms Delay (default)
$builder->addStep(SlowServiceStep::class, retries: 5, delay: 500); // 5 Versuche, 500 ms Delay

use Wundii\Flowcrafter\Attribute\FlowProjection;
use Wundii\Flowcrafter\Attribute\FlowProjectionMessage;
use Wundii\Flowcrafter\FlowMessageReadonly;
use Wundii\Flowcrafter\Interface\ProjectionHandlerInterface;

#[FlowProjection(['flow.order.v1'])]
class OrderProjection implements ProjectionHandlerInterface
{
    #[FlowProjectionMessage(OrderCompleted::class)]
    public function onCompleted(FlowMessageReadonly $message): void
    {
        // Read Model aktualisieren, Benachrichtigung verschicken, ...
    }
}

use Wundii\Flowcrafter\DependencyInjection\DependencyRegistry;
use Wundii\Flowcrafter\Env;
use Psr\Container\ContainerInterface;

$registry = (new DependencyRegistry())
    ->bind(HttpClientInterface::class, new CurlHttpClient())  // Interface → Instanz
    ->instance(new MyLogger())                                // Instanz ohne Interface
    ->autowireNamespace('App\\Service')                       // ganze Namespaces autowiren
    ->factory(ApiClient::class,                               // lazy, für rohe Skalare
        fn(ContainerInterface $c) => new ApiClient($c->get(HttpClientInterface::class), Env::string('API_KEY')),
    );

$flowRunner = new FlowRunner(
    type: 'flow.order.v1',
    flowSource: OrderFlow::class,
    storage: $storage,
    dependencyRegistry: $registry,
);

use Wundii\Flowcrafter\Testing\FlowTestCase;

final class OrderFlowTest extends FlowTestCase
{
    public function testHappyPath(): void
    {
        $this->runFlow(
            flowType: 'flow.order.v1',
            flowSource: OrderFlow::class,
            initMessage: new OrderInit('sku-42'),
        );

        $this->assertFlowOk();
        $this->assertStepExecuted(CompleteOrderStep::class);
        $this->assertFlowBoolResult(true);   // AuditStep lieferte true

        $return = $this->assertFlowReturned(OrderCompleted::class);
        $this->assertSame('Order sku-42 x1 completed', $return->getSummary());
    }
}