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