PHP code example of pstoute / laravel-workflow-conductor

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

    

pstoute / laravel-workflow-conductor example snippets


// config/workflow-conductor.php

'database' => [
    'table_prefix' => 'automation_',   // Match your existing table prefix
    'skip_migrations' => true,          // Skip package migrations
],

// Override the default models with your extended versions
'models' => [
    'workflow' => App\Models\Automation::class,
    'trigger' => App\Models\AutomationTrigger::class,
    'condition' => App\Models\AutomationCondition::class,
    'action' => App\Models\AutomationAction::class,
    'execution' => App\Models\AutomationExecution::class,
    'execution_log' => App\Models\AutomationActionResult::class,
],

use Pstoute\WorkflowConductor\Models\Workflow;

class Automation extends Workflow
{
    public function getTable(): string
    {
        return 'automations';
    }

    // Add your custom columns, relationships, etc.
}

use Pstoute\WorkflowConductor\WorkflowConductorServiceProvider;

class WorkflowConductorProvider extends WorkflowConductorServiceProvider
{
    public function boot(): void
    {
        // Publish config
        $this->publishes([
            base_path('vendor/pstoute/laravel-workflow-conductor/config/workflow-conductor.php')
                => config_path('workflow-conductor.php'),
        ], 'workflow-conductor-config');

        // Skip migrations - the app has its own tables
        // Load routes and register built-in extensions
        $this->loadRoutesFrom(base_path('vendor/pstoute/laravel-workflow-conductor/routes/webhooks.php'));
        $this->registerBuiltInTriggers();
        $this->registerBuiltInConditions();
        $this->registerBuiltInActions();
    }
}

use Pstoute\WorkflowConductor\Facades\Conductor;

// Create a welcome email workflow
$workflow = Conductor::create()
    ->name('Welcome Email')
    ->trigger('model.created', ['model' => App\Models\User::class])
    ->when('email_verified_at', 'is_not_null')
    ->sendEmail(
        '{{ model.email }}',
        'Welcome to {{ config.app.name }}!',
        'Hello {{ model.name }}!'
    )
    ->save();

use Pstoute\WorkflowConductor\Traits\HasWorkflows;

class User extends Model
{
    use HasWorkflows;
}

use Pstoute\WorkflowConductor\Facades\Conductor;
use Pstoute\WorkflowConductor\Data\WorkflowContext;

$context = new WorkflowContext([
    'order' => $order,
    'user' => $order->user,
]);

// Execute synchronously
$result = Conductor::execute($workflowId, $context);

// Execute asynchronously
Conductor::executeAsync($workflowId, $context);

return [
    'execution' => [
        'default_mode' => 'async', // sync or async
        'queue' => 'workflows',
        'max_retries' => 3,
        'timeout' => 300,
    ],

    'logging' => [
        'enabled' => true,
        'retention_days' => 30,
    ],

    'rate_limits' => [
        'enabled' => true,
        'max_executions_per_minute' => 100,
    ],
];

Conductor::create()
    ->trigger('model.created', ['model' => App\Models\User::class])
    // ...

Conductor::create()
    ->trigger('scheduled', [
        'cron' => '0 9 * * *', // Every day at 9 AM
        'timezone' => 'America/New_York',
    ])
    // ...

$workflow = Conductor::create()
    ->trigger('webhook', [
        'webhook_id' => 'your-unique-webhook-id',
    ])
    // ...
    ->save();

// Webhook URL: https://yourapp.com/workflows/webhooks/your-unique-webhook-id

Conductor::create()
    ->trigger('manual', [
        'allowed_users' => [1, 2, 3], // Optional: restrict to specific users
        '

Conductor::create()
    ->when('status', 'equals', 'active')
    ->when('total', 'greater_than', 100)
    ->orWhen('type', 'equals', 'premium')
    // ...

Conductor::create()
    ->condition('date', [
        'field' => 'created_at',
        'operator' => 'is_today',
    ])
    // ...

Conductor::create()
    ->condition('relation', [
        'relation' => 'orders',
        'operator' => 'count_greater',
        'value' => 5,
    ])
    // ...

Conductor::create()
    ->sendEmail(
        '{{ model.email }}',
        'Order Confirmation #{{ model.order_number }}',
        '<p>Thank you for your order!</p>'
    )
    // Or with template
    ->action('send_email', [
        'to' => '{{ model.email }}',
        'subject' => 'Welcome!',
        'template' => 'emails.welcome',
        'data' => ['user' => '{{ model }}'],
    ])

Conductor::create()
    ->action('send_notification', [
        'notification' => App\Notifications\OrderShipped::class,
        'notifiable' => 'model.user',
    ])

Conductor::create()
    ->webhook('https://api.example.com/webhook', [
        'event' => 'order.created',
        'order_id' => '{{ model.id }}',
    ])

Conductor::create()
    ->slack(
        ':money_bag: New order #{{ model.number }} for ${{ model.total | number_format:2 }}',
        '#sales'
    )

Conductor::create()
    ->createModel(App\Models\Task::class, [
        'user_id' => '{{ model.id }}',
        'title' => 'Follow up with {{ model.name }}',
    ])
    ->updateModel(['status' => 'processed'])

Conductor::create()
    ->delay(3, 'days')
    ->sendEmail(/* ... */)

Conductor::create()
    ->action('http_request', [
        'url' => 'https://api.example.com/users',
        'method' => 'POST',
        'body' => ['email' => '{{ model.email }}'],
        'auth' => [
            'type' => 'bearer',
            'token' => '{{ env.API_TOKEN }}',
        ],
    ])

'subject' => 'Hello {{ user.name }}!'
'amount' => '{{ order.total | number_format:2 }}'

use Pstoute\WorkflowConductor\Events\WorkflowCompleted;

Event::listen(WorkflowCompleted::class, function ($event) {
    Log::info("Workflow {$event->workflow->name} completed");
});



namespace App\Workflows\Triggers;

use Pstoute\WorkflowConductor\Contracts\TriggerInterface;
use Pstoute\WorkflowConductor\Data\WorkflowContext;

class PaymentReceivedTrigger implements TriggerInterface
{
    public function getIdentifier(): string
    {
        return 'payment.received';
    }

    public function getName(): string
    {
        return 'Payment Received';
    }

    public function getDescription(): string
    {
        return 'Triggered when a payment is received';
    }

    public function getConfigurationSchema(): array
    {
        return [
            'type' => 'object',
            'properties' => [
                'min_amount' => [
                    'type' => 'number',
                    'description' => 'Minimum payment amount to trigger',
                ],
                'currency' => [
                    'type' => 'string',
                    'description' => 'Currency code (e.g., USD)',
                ],
            ],
        ];
    }

    public function shouldTrigger(WorkflowContext $context, array $triggerConfig): bool
    {
        $payment = $context->get('payment');

        if (!$payment) {
            return false;
        }

        // Check minimum amount
        $minAmount = $triggerConfig['min_amount'] ?? 0;
        if ($payment->amount < $minAmount) {
            return false;
        }

        // Check currency
        $currency = $triggerConfig['currency'] ?? null;
        if ($currency && $payment->currency !== $currency) {
            return false;
        }

        return true;
    }

    public function getAvailableData(): array
    {
        return [
            'payment' => 'The payment object',
            'payment.amount' => 'Payment amount',
            'payment.currency' => 'Payment currency',
            'payment.user' => 'The user who made the payment',
        ];
    }
}

use Pstoute\WorkflowConductor\Facades\Conductor;
use App\Workflows\Triggers\PaymentReceivedTrigger;

public function boot(): void
{
    Conductor::registerTrigger(new PaymentReceivedTrigger());
}

use Pstoute\WorkflowConductor\Facades\Conductor;
use Pstoute\WorkflowConductor\Data\WorkflowContext;

// In your payment processing code
$context = new WorkflowContext([
    'payment' => $payment,
    'user' => $payment->user,
]);

Conductor::trigger('payment.received', $context);



namespace App\Workflows\Actions;

use Pstoute\WorkflowConductor\Contracts\ActionInterface;
use Pstoute\WorkflowConductor\Data\ActionResult;
use Pstoute\WorkflowConductor\Data\WorkflowContext;
use App\Services\SmsService;

class SendSmsAction implements ActionInterface
{
    public function __construct(
        protected SmsService $smsService
    ) {}

    public function getIdentifier(): string
    {
        return 'send_sms';
    }

    public function getName(): string
    {
        return 'Send SMS';
    }

    public function getDescription(): string
    {
        return 'Send an SMS message via Twilio';
    }

    public function getConfigurationSchema(): array
    {
        return [
            'type' => 'object',
            'properties' => [
                'to' => [
                    'type' => 'string',
                    'description' => 'Phone number to send to',
                    '      }
    }

    public function supportsAsync(): bool
    {
        return true;
    }

    public function getTimeout(): int
    {
        return 30;
    }

    public function getOutputData(): array
    {
        return [
            'message_sid' => 'The Twilio message SID',
            'sent_to' => 'The phone number the message was sent to',
        ];
    }
}

use Pstoute\WorkflowConductor\Facades\Conductor;
use App\Workflows\Actions\SendSmsAction;

public function boot(): void
{
    Conductor::registerAction(app(SendSmsAction::class));
}

Conductor::create()
    ->name('Order SMS Notification')
    ->trigger('model.created', ['model' => App\Models\Order::class])
    ->action('send_sms', [
        'to' => '{{ model.user.phone }}',
        'message' => 'Your order #{{ model.number }} has been received!',
    ])
    ->save();



namespace App\Workflows\Conditions;

use Pstoute\WorkflowConductor\Contracts\ConditionInterface;
use Pstoute\WorkflowConductor\Data\WorkflowContext;

class BusinessHoursCondition implements ConditionInterface
{
    public function getIdentifier(): string
    {
        return 'business_hours';
    }

    public function getName(): string
    {
        return 'Business Hours';
    }

    public function getDescription(): string
    {
        return 'Check if current time is within business hours';
    }

    public function getOperators(): array
    {
        return [
            'is_business_hours' => 'Is during business hours',
            'is_not_business_hours' => 'Is outside business hours',
        ];
    }

    public function evaluate(WorkflowContext $context, array $config): bool
    {
        $operator = $config['operator'] ?? 'is_business_hours';
        $timezone = $config['timezone'] ?? config('app.timezone');
        $startHour = $config['start_hour'] ?? 9;
        $endHour = $config['end_hour'] ?? 17;
        $workDays = $config['work_days'] ?? [1, 2, 3, 4, 5]; // Mon-Fri

        $now = now($timezone);
        $currentHour = $now->hour;
        $currentDay = $now->dayOfWeek;

        $isBusinessHours = in_array($currentDay, $workDays)
            && $currentHour >= $startHour
            && $currentHour < $endHour;

        return $operator === 'is_business_hours' ? $isBusinessHours : !$isBusinessHours;
    }

    public function getConfigurationSchema(): array
    {
        return [
            'type' => 'object',
            'properties' => [
                'operator' => [
                    'type' => 'string',
                    'enum' => ['is_business_hours', 'is_not_business_hours'],
                ],
                'timezone' => [
                    'type' => 'string',
                    'description' => 'Timezone to check',
                ],
                'start_hour' => [
                    'type' => 'integer',
                    'description' => 'Start hour (0-23)',
                    'default' => 9,
                ],
                'end_hour' => [
                    'type' => 'integer',
                    'description' => 'End hour (0-23)',
                    'default' => 17,
                ],
            ],
        ];
    }
}

Conductor::registerCondition(new BusinessHoursCondition());

Conductor::create()
    ->name('Business Hours Only')
    ->trigger('model.created', ['model' => App\Models\Lead::class])
    ->condition('business_hours', [
        'operator' => 'is_business_hours',
        'timezone' => 'America/New_York',
    ])
    ->action('send_notification', [/* ... */])
    ->save();

use Pstoute\WorkflowConductor\Actions\CustomAction;
use Pstoute\WorkflowConductor\Conditions\CustomCondition;

// Register a custom action handler
CustomAction::register('sync_to_crm', function (WorkflowContext $context, array $params) {
    $user = $context->get('model');
    // Sync logic here...
    return ActionResult::success('Synced to CRM', ['crm_id' => $crmId]);
});

// Register a custom condition
CustomCondition::register('is_premium_user', function (WorkflowContext $context, array $params) {
    $user = $context->get('model');
    return $user->subscription_type === 'premium';
});

// Use in workflow
Conductor::create()
    ->name('Premium User Sync')
    ->trigger('model.updated', ['model' => App\Models\User::class])
    ->condition('custom', ['callback' => 'is_premium_user'])
    ->action('custom', ['handler' => 'sync_to_crm'])
    ->save();



namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Pstoute\WorkflowConductor\WorkflowManager;

class AutomationExtensionProvider extends ServiceProvider
{
    public function boot(): void
    {
        $manager = $this->app->make(WorkflowManager::class);

        // Register custom triggers
        $manager->registerTrigger(new \App\Automation\Triggers\PaymentReceivedTrigger());
        $manager->registerTrigger(new \App\Automation\Triggers\FormSubmittedTrigger());

        // Register custom actions with dependency injection
        $manager->registerAction($this->app->make(\App\Automation\Actions\SendSmsAction::class));
        $manager->registerAction($this->app->make(\App\Automation\Actions\SyncToCrmAction::class));
        $manager->registerAction($this->app->make(\App\Automation\Actions\GeneratePdfAction::class));
    }
}

// config/workflow-conductor.php
'actions' => [
    'send_email' => ['enabled' => false],     // App provides enhanced version
    'webhook' => ['enabled' => false],          // App provides its own
    'create_model' => ['enabled' => true],      // Keep generic utilities
    'update_model' => ['enabled' => true],
    'delete_model' => ['enabled' => true],
],

// First action: generate a PDF
class GeneratePdfAction extends AbstractAction
{
    public function execute(WorkflowContext $context, array $config): ActionResult
    {
        $pdf = $this->pdfService->generate($config['template'], $context->all());

        return ActionResult::success('PDF generated', [
            'file_path' => $pdf->path,
            'filename' => $pdf->name,
            'download_url' => $pdf->url,
        ]);
    }
}

// Second action: send email with the PDF from the previous action
// In the action config, reference previous output:
[
    'to' => '{{ model.email }}',
    'subject' => 'Your document is ready',
    'body' => 'Download: {{ previous_actions.generate_pdf.download_url }}',
    'attachment' => '{{ previous_actions.generate_pdf.file_path }}',
]

use Pstoute\WorkflowConductor\WorkflowManager;

$manager = app(WorkflowManager::class);

// Get all registered actions with their schemas (for dropdown/form generation)
$actions = collect($manager->getActions())->map(fn ($action) => [
    'value' => $action->getIdentifier(),
    'label' => $action->getName(),
    'description' => $action->getDescription(),
    'schema' => $action->getConfigurationSchema(),
    'output' => $action->getOutputData(),
]);

// Get all registered triggers
$triggers = collect($manager->getTriggers())->map(fn ($trigger) => [
    'value' => $trigger->getIdentifier(),
    'label' => $trigger->getName(),
    'schema' => $trigger->getConfigurationSchema(),
    'available_data' => $trigger->getAvailableData(),
]);

use Pstoute\WorkflowConductor\Support\VariableInterpolator;
use Pstoute\WorkflowConductor\Data\WorkflowContext;

$interpolator = new VariableInterpolator();
$context = new WorkflowContext(['user' => $user, 'order' => $order]);

// Interpolate strings
$greeting = $interpolator->interpolate('Hello {{ user.name }}!', $context);

// Interpolate arrays recursively
$config = $interpolator->interpolate([
    'to' => '{{ user.email }}',
    'subject' => 'Order #{{ order.number }}',
    'amount' => '{{ order.total | money:$:2 }}',
], $context);

use Pstoute\WorkflowConductor\Jobs\ProcessScheduledWorkflows;

protected function schedule(Schedule $schedule): void
{
    $schedule->job(new ProcessScheduledWorkflows())->everyMinute();
}
bash
php artisan vendor:publish --tag=workflow-conductor-config
bash
php artisan migrate