PHP code example of padosoft / laravel-flow

1. Go to this page and download the library: Download padosoft/laravel-flow 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/ */

    

padosoft / laravel-flow example snippets


use Padosoft\LaravelFlow\Facades\Flow;

Flow::define('promotion.create')
    ->withInput(['brand', 'discount_pct', 'starts_at', 'ends_at'])
    ->step('validate', ValidatePromotionInput::class)
    ->step('simulate', SimulatePromotionImpact::class)
        ->withDryRun(true)
    ->step('persist', PersistPromotion::class)
        ->compensateWith(ReversePromotion::class)
    ->register();

$run    = Flow::execute('promotion.create', $input);   // real execution
$dryRun = Flow::dryRun ('promotion.create', $input);   // simulate, no writes

use Padosoft\LaravelFlow\Facades\Flow;
use Padosoft\LaravelFlow\FlowContext;
use Padosoft\LaravelFlow\FlowStepHandler;
use Padosoft\LaravelFlow\FlowStepResult;

// 1. Define a handler.
class ValidatePromotionInput implements FlowStepHandler
{
    public function execute(FlowContext $context): FlowStepResult
    {
        if ($context->input['discount_pct'] > 90) {
            return FlowStepResult::failed(new \DomainException('Discount > 90%'));
        }

        return FlowStepResult::success(['validated_at' => now()->toIso8601String()]);
    }
}

// 2. Register a flow.
Flow::define('promotion.create')
    ->withInput(['brand', 'discount_pct'])
    ->step('validate', ValidatePromotionInput::class)
    ->register();

// 3. Execute.
$run = Flow::execute('promotion.create', ['brand' => 'acme', 'discount_pct' => 25]);

if ($run->status === \Padosoft\LaravelFlow\FlowRun::STATUS_SUCCEEDED) {
    // Done.
} else {
    // $run->failedStep tells you which step blew up.
    // $run->compensated tells you whether rollback ran.
}

use Padosoft\LaravelFlow\FlowExecutionOptions;

$run = Flow::execute(
    'promotion.create',
    $input,
    FlowExecutionOptions::make(
        correlationId: 'checkout-2026-0001',
        idempotencyKey: 'tenant-42:promotion-abc',
    ),
);

use Padosoft\LaravelFlow\FlowExecutionOptions;

Flow::dispatch(
    'promotion.create',
    $input,
    FlowExecutionOptions::make(
        correlationId: 'checkout-2026-0001',
        idempotencyKey: 'tenant-42:promotion-abc',
    ),
);

Flow::define('promotion.publish')
    ->step('prepare', PreparePromotion::class)
        ->compensateWith(UndoPreparedPromotion::class)
    ->approvalGate('manager')
    ->step('publish', PublishPromotion::class)
    ->register();

// Enable laravel-flow.persistence.enabled before executing so a persisted approval token is issued.
$pausedRun = Flow::execute('promotion.publish', $input);
$token = $pausedRun->approvalTokens['manager']->plainTextToken;

$resumedRun = Flow::resume($token, ['decision' => 'approved'], ['user_id' => 123]);
// To reject instead of approving that pending token:
// $rejectedRun = Flow::reject($token, ['reason' => 'duplicate'], ['user_id' => 123]);

php artisan flow:deliver-webhooks

// Tune throughput and pacing for transient errors.
php artisan flow:deliver-webhooks --batch=10 --sleep-ms=250

class PersistPromotion implements FlowStepHandler { /* writes a DB row */ }
class ReversePromotion implements FlowCompensator  { /* deletes the row */ }

Flow::define('promotion.create')
    ->withInput(['brand', 'discount_pct'])
    ->step('validate', ValidatePromotionInput::class)
    ->step('persist', PersistPromotion::class)
        ->compensateWith(ReversePromotion::class)
    ->step('publish', PublishPromotionToCDN::class)  // imagine this fails
    ->register();

$run = Flow::execute('promotion.create', $input);

// If 'publish' fails:
//   $run->status      === FlowRun::STATUS_FAILED
//   $run->failedStep  === 'publish'
//   $run->compensated === true   // ReversePromotion ran

class SimulatePromotionImpact implements FlowStepHandler
{
    public function execute(FlowContext $context): FlowStepResult
    {
        $impact = [
            'expected_users_reached' => 12_400,
            'projected_revenue_eur'  => 18_900.00,
        ];

        return FlowStepResult::success(output: [], businessImpact: $impact);
    }
}

Flow::define('promotion.create')
    ->step('simulate', SimulatePromotionImpact::class)
        ->withDryRun(true)
    ->step('persist', PersistPromotion::class)        // skipped in dry mode
    ->register();

$dryRun = Flow::dryRun('promotion.create', $input);

$businessImpact = $dryRun->stepResults['simulate']->businessImpact;
// ['expected_users_reached' => 12400, 'projected_revenue_eur' => 18900.00]

use Illuminate\Support\Facades\Event;
use Padosoft\LaravelFlow\Events\FlowStepStarted;
use Padosoft\LaravelFlow\Events\FlowStepCompleted;
use Padosoft\LaravelFlow\Events\FlowStepFailed;
use Padosoft\LaravelFlow\Events\FlowCompensated;

Event::listen(function (FlowStepStarted $e) {
    logger()->info('flow.step.started', [
        'flow_run_id'     => $e->flowRunId,
        'definition_name' => $e->definitionName,
        'step_name'       => $e->stepName,
        'dry_run'         => $e->dryRun,
    ]);
});

Event::listen(function (FlowStepFailed $e) {
    // Wire to Sentry / Datadog / your audit table.
});

// config/laravel-flow.php
$queueLockSeconds = env('LARAVEL_FLOW_QUEUE_LOCK_SECONDS', 3600);
$queueLockRetrySeconds = env('LARAVEL_FLOW_QUEUE_LOCK_RETRY_SECONDS', 30);
$queueTries = env('LARAVEL_FLOW_QUEUE_TRIES', null);
$queueBackoffSeconds = env('LARAVEL_FLOW_QUEUE_BACKOFF_SECONDS', null);
$approvalTokenTtlMinutes = env('LARAVEL_FLOW_APPROVAL_TOKEN_TTL_MINUTES', 1440);
$webhookTimeoutSeconds = env('LARAVEL_FLOW_WEBHOOK_TIMEOUT_SECONDS', 5);
$webhookRetryBaseDelaySeconds = env('LARAVEL_FLOW_WEBHOOK_RETRY_BASE_DELAY_SECONDS', 30);
$webhookMaxAttempts = env('LARAVEL_FLOW_WEBHOOK_MAX_ATTEMPTS', 3);

return [
    'default_storage'        => env('LARAVEL_FLOW_STORAGE', null),
    'persistence'            => [
        'enabled'   => env('LARAVEL_FLOW_PERSISTENCE_ENABLED', false),
        'redaction' => [
            'enabled'     => env('LARAVEL_FLOW_REDACTION_ENABLED', true),
            'replacement' => env('LARAVEL_FLOW_REDACTION_REPLACEMENT', '[redacted]'),
            'keys'        => ['api_key', 'authorization', 'password', 'secret', 'token'],
        ],
        'retention' => [
            'days' => env('LARAVEL_FLOW_RETENTION_DAYS', null),
        ],
    ],
    'queue'                  => [
        'lock_store'   => env('LARAVEL_FLOW_QUEUE_LOCK_STORE', null),
        'lock_seconds' => is_numeric($queueLockSeconds) && (int) $queueLockSeconds >= 1
            ? (int) $queueLockSeconds
            : 3600,
        'lock_retry_seconds' => is_numeric($queueLockRetrySeconds) && (int) $queueLockRetrySeconds >= 1
            ? (int) $queueLockRetrySeconds
            : 30,
        'tries' => $queueTries,
        'backoff_seconds' => $queueBackoffSeconds, // set LARAVEL_FLOW_QUEUE_BACKOFF_SECONDS=5,30,120 for a retry schedule
    ],
    'approval'               => [
        'token_ttl_minutes' => is_numeric($approvalTokenTtlMinutes) && (int) $approvalTokenTtlMinutes >= 1
            ? (int) $approvalTokenTtlMinutes
            : 1440,
    ],
    'webhook'               => [
        'enabled' => env('LARAVEL_FLOW_WEBHOOK_ENABLED', false),
        'url' => env('LARAVEL_FLOW_WEBHOOK_URL', ''),
        'secret' => env('LARAVEL_FLOW_WEBHOOK_SECRET', null),
        'retry_base_delay_seconds' => is_numeric($webhookRetryBaseDelaySeconds) && (int) $webhookRetryBaseDelaySeconds > 0
            ? (int) $webhookRetryBaseDelaySeconds
            : 30,
        'max_attempts' => is_numeric($webhookMaxAttempts) && (int) $webhookMaxAttempts > 0
            ? (int) $webhookMaxAttempts
            : 3,
        'timeout_seconds' => is_numeric($webhookTimeoutSeconds) && (int) $webhookTimeoutSeconds >= 1
            ? (int) $webhookTimeoutSeconds
            : 5,
    ],
    'audit_trail_enabled'    => env('LARAVEL_FLOW_AUDIT_ENABLED', true), // events; DB audit rows 
bash
php artisan vendor:publish --tag=laravel-flow-config
bash
php artisan vendor:publish --tag=laravel-flow-migrations
php artisan migrate
bash
php artisan flow:prune --days=90 --dry-run
php artisan flow:prune --days=90
bash
php artisan flow:replay 00000000-0000-4000-8000-000000000001