PHPackages                             pstoute/laravel-workflow-conductor - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. pstoute/laravel-workflow-conductor

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

pstoute/laravel-workflow-conductor
==================================

A powerful, Laravel-native workflow conductor that lets users build automation with triggers, conditions, and actions

v1.1.0(3mo ago)0258MITPHPPHP ^8.2

Since Feb 3Pushed 3mo agoCompare

[ Source](https://github.com/pstoute/laravel-workflow-conductor)[ Packagist](https://packagist.org/packages/pstoute/laravel-workflow-conductor)[ Docs](https://github.com/pstoute/laravel-workflow-conductor)[ RSS](/packages/pstoute-laravel-workflow-conductor/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (9)Versions (3)Used By (0)

Laravel Workflow Conductor
==========================

[](#laravel-workflow-conductor)

A powerful, Laravel-native workflow conductor that lets users build automation with triggers, conditions, and actions. Think Zapier/Make/n8n but self-hosted and deeply integrated with Laravel.

Features
--------

[](#features)

- **Triggers**: Model events, webhooks, scheduled (cron), manual
- **Conditions**: Field comparisons, date conditions, relation checks, custom callbacks
- **Actions**: Send emails, notifications, webhooks, Slack messages, create/update/delete models
- **Variable Interpolation**: Use `{{ model.field }}` syntax with filters
- **Condition Logic**: AND/OR grouping for complex conditions
- **Async Execution**: Queue-based execution with retry support
- **Execution Logging**: Track every workflow execution and action result
- **Extensible**: Easily add custom triggers, conditions, and actions

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 10.x, 11.x, or 12.x

Installation
------------

[](#installation)

```
composer require pstoute/laravel-workflow-conductor
```

Publish the configuration file:

```
php artisan vendor:publish --tag=workflow-conductor-config
```

Run the migrations:

```
php artisan migrate
```

Using with an Existing Schema
-----------------------------

[](#using-with-an-existing-schema)

If your application already has workflow/automation tables, you can skip the package migrations and map your models:

```
// 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,
],
```

Your models should extend the package models and override `getTable()`:

```
use Pstoute\WorkflowConductor\Models\Workflow;

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

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

If you are using `dont-discover` to prevent auto-registration, create a wrapper provider:

```
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();
    }
}
```

Quick Start
-----------

[](#quick-start)

### Creating a Workflow Programmatically

[](#creating-a-workflow-programmatically)

```
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();
```

### Using the HasWorkflows Trait

[](#using-the-hasworkflows-trait)

Add the trait to your models to automatically trigger workflows on model events:

```
use Pstoute\WorkflowConductor\Traits\HasWorkflows;

class User extends Model
{
    use HasWorkflows;
}
```

### Manual Workflow Execution

[](#manual-workflow-execution)

```
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);
```

Configuration
-------------

[](#configuration)

The configuration file is located at `config/workflow-conductor.php`. Key options include:

```
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,
    ],
];
```

Triggers
--------

[](#triggers)

### Model Events

[](#model-events)

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

Available model triggers:

- `model.created` - When a model is created
- `model.updated` - When a model is updated (optionally watch specific fields)
- `model.deleted` - When a model is deleted

### Scheduled (Cron)

[](#scheduled-cron)

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

### Webhook

[](#webhook)

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

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

### Manual

[](#manual)

```
Conductor::create()
    ->trigger('manual', [
        'allowed_users' => [1, 2, 3], // Optional: restrict to specific users
        'required_data' => ['order_id'], // Optional: require specific context data
    ])
    // ...
```

Conditions
----------

[](#conditions)

### Field Conditions

[](#field-conditions)

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

Available operators:

- `equals`, `not_equals`
- `contains`, `not_contains`
- `starts_with`, `ends_with`
- `greater_than`, `less_than`, `greater_or_equal`, `less_or_equal`
- `is_null`, `is_not_null`
- `is_empty`, `is_not_empty`
- `in`, `not_in`
- `matches_regex`
- `between`

### Date Conditions

[](#date-conditions)

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

### Relation Conditions

[](#relation-conditions)

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

Actions
-------

[](#actions)

### Send Email

[](#send-email)

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

### Send Notification

[](#send-notification)

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

### Webhook

[](#webhook-1)

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

### Slack Message

[](#slack-message)

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

### Create/Update/Delete Model

[](#createupdatedelete-model)

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

### Delay

[](#delay)

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

### HTTP Request

[](#http-request)

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

Variable Interpolation
----------------------

[](#variable-interpolation)

Use `{{ variable.path }}` syntax in action configurations:

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

### Available Filters

[](#available-filters)

FilterDescriptionExample`uppercase`Convert to uppercase`{{ name | uppercase }}``lowercase`Convert to lowercase`{{ name | lowercase }}``number_format:N`Format number with N decimals`{{ price | number_format:2 }}``date:format`Format date`{{ created_at | date:Y-m-d }}``default:value`Default if null`{{ name | default:Guest }}``json`Convert to JSON`{{ data | json }}``slug`Convert to slug`{{ title | slug }}``money`Format as currency`{{ price | money }}``count`Count array items`{{ items | count }}``join:separator`Join array`{{ tags | join:, }}`Events
------

[](#events)

The package dispatches events during workflow execution:

- `WorkflowStarted` - When a workflow begins execution
- `WorkflowCompleted` - When a workflow completes successfully
- `WorkflowFailed` - When a workflow fails
- `ActionExecuted` - When an action completes successfully
- `ActionFailed` - When an action fails

```
use Pstoute\WorkflowConductor\Events\WorkflowCompleted;

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

Contributing Custom Extensions
------------------------------

[](#contributing-custom-extensions)

### Creating a Custom Trigger

[](#creating-a-custom-trigger)

To create a custom trigger, implement `TriggerInterface`:

```
