PHPackages                             chevere/workflow-laravel - 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. chevere/workflow-laravel

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

chevere/workflow-laravel
========================

Chevere Workflow integration for Laravel.

1.0.0(4mo ago)546↓59.3%1Apache-2.0PHPPHP ^8.1CI passing

Since Mar 17Pushed 2mo agoCompare

[ Source](https://github.com/chevere/workflow-laravel)[ Packagist](https://packagist.org/packages/chevere/workflow-laravel)[ Docs](https://chevere.org)[ RSS](/packages/chevere-workflow-laravel/feed)WikiDiscussions 1.0 Synced 1w ago

READMEChangelog (1)Dependencies (8)Versions (2)Used By (0)

Chevere Workflow for Laravel
============================

[](#chevere-workflow-for-laravel)

[![Chevere](chevere.svg)](chevere.svg)

[![Build](https://camo.githubusercontent.com/71a3fb4e22ee5d563534d699224fb4a3d072caa71bd4fa835589161ec29a74c5/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f636865766572652f776f726b666c6f772d6c61726176656c2f746573742e796d6c3f6272616e63683d312e30267374796c653d666c61742d737175617265)](https://github.com/chevere/workflow-laravel/actions)[![Code size](https://camo.githubusercontent.com/87e0952827e578d4facdd30bf43446b5067ed064e63888cf09ba0ed7b33a27eb/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c616e6775616765732f636f64652d73697a652f636865766572652f776f726b666c6f772d6c61726176656c3f7374796c653d666c61742d737175617265)](https://camo.githubusercontent.com/87e0952827e578d4facdd30bf43446b5067ed064e63888cf09ba0ed7b33a27eb/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c616e6775616765732f636f64652d73697a652f636865766572652f776f726b666c6f772d6c61726176656c3f7374796c653d666c61742d737175617265)[![Apache-2.0](https://camo.githubusercontent.com/8a5ebfebf7eab5003e5081557d22b1bf5e2a2c3f8b234dbb21fd25592fbe8946/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f636865766572652f776f726b666c6f772d6c61726176656c3f7374796c653d666c61742d737175617265)](LICENSE)[![PHPStan](https://camo.githubusercontent.com/6016298b28550819030c76e9327f62501596a31fd76406695bae2f3d2a1f26a4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230392d626c756576696f6c65743f7374796c653d666c61742d737175617265)](https://phpstan.org/)[![Mutation testing badge](https://camo.githubusercontent.com/bbdefc1f0c0f2373d7b8a6df1c35e2086ad25e768813cda80e711a502a0b8c62/68747470733a2f2f696d672e736869656c64732e696f2f656e64706f696e743f7374796c653d666c61742d7371756172652675726c3d687474707325334125324625324662616467652d6170692e737472796b65722d6d757461746f722e696f2532466769746875622e636f6d25324663686576657265253246776f726b666c6f772d6c61726176656c253246312e30)](https://dashboard.stryker-mutator.io/reports/github.com/chevere/workflow-laravel/1.0)

[![Quality Gate Status](https://camo.githubusercontent.com/cfd5e4f2614921780e83532eb8dcf76a6302d3dda0851cf55070d94f00fa8f98/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d636865766572655f776f726b666c6f772d6c61726176656c266d65747269633d616c6572745f737461747573)](https://sonarcloud.io/dashboard?id=chevere_workflow-laravel)[![Maintainability Rating](https://camo.githubusercontent.com/1f52fa2bdfb71207bf1554a646ac33de43df144ceacd306c4335cf320961bf02/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d636865766572655f776f726b666c6f772d6c61726176656c266d65747269633d7371616c655f726174696e67)](https://sonarcloud.io/dashboard?id=chevere_workflow-laravel)[![Reliability Rating](https://camo.githubusercontent.com/c74c945d2cb94afe212fc5e9ddc9211200b4e90a0be85302d0ef71c81e0342c2/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d636865766572655f776f726b666c6f772d6c61726176656c266d65747269633d72656c696162696c6974795f726174696e67)](https://sonarcloud.io/dashboard?id=chevere_workflow-laravel)[![Security Rating](https://camo.githubusercontent.com/1b978c96fe024c4ce667036b3eb8c75b15ff8c9ebb54029a3c4334dcfdbe7520/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d636865766572655f776f726b666c6f772d6c61726176656c266d65747269633d73656375726974795f726174696e67)](https://sonarcloud.io/dashboard?id=chevere_workflow-laravel)[![Coverage](https://camo.githubusercontent.com/411249a4386fbd0a0726f37fb6d78cde5eb7f23265c6ecbb4e00c96f21b2566d/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d636865766572655f776f726b666c6f772d6c61726176656c266d65747269633d636f766572616765)](https://sonarcloud.io/dashboard?id=chevere_workflow-laravel)[![Technical Debt](https://camo.githubusercontent.com/73cd7cf4dfcddac4e5df2ffd3491af048b0a2c62aee78023af36f68ffcd90c33/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d636865766572655f776f726b666c6f772d6c61726176656c266d65747269633d7371616c655f696e646578)](https://sonarcloud.io/dashboard?id=chevere_workflow-laravel)[![CodeFactor](https://camo.githubusercontent.com/67868625d7e63cd0635a8529deb66904e0b2c11a3dcca775b943ba3c65647bb8/68747470733a2f2f7777772e636f6465666163746f722e696f2f7265706f7369746f72792f6769746875622f636865766572652f776f726b666c6f772d6c61726176656c2f6261646765)](https://www.codefactor.io/repository/github/chevere/workflow-laravel)

Summary
-------

[](#summary)

**[Chevere Workflow](https://chevere.org/packages/workflow) for Laravel** is a PHP library for building and executing multi-step procedures with automatic dependency resolution. Define independent jobs that can run synchronously or asynchronously, pass data between them using typed responses, and let the engine handle execution order automatically.

**Key features:**

- **Declarative job definitions**: Define what to do, not how to orchestrate it
- **Automatic dependency graph**: Jobs execute in optimal order based on their dependencies
- **Sync and async execution**: Mix blocking and non-blocking jobs freely
- **Type-safe responses**: Access job outputs with full type safety
- **Conditional execution**: Run jobs based on variables or previous responses
- **Built-in retry policies**: Handle transient failures automatically
- **Testable**: Each job is independently testable and workflow graph can be verified

You define jobs and how they connect and depend on each other, **Chevere Workflow** figures out the execution order and runs them accordingly.

---

What does this solve for Laravel developers?
--------------------------------------------

[](#what-does-this-solve-for-laravel-developers)

- **Dependency Management**: Like `Bus::chain()`, execution order follows dependencies. But instead of manually sequencing every step, simply declare what data each job needs with `response()` and the engine infers the order.
- **Parallel Processing**: Like `Bus::batch()`, independent jobs run concurrently using `async()`.
- **Sequential Flows**: Like `Pipeline`, you can model linear operations but workflow graphs also handle branching and fan-in without custom plumbing.
- **Conditional Execution**: Like feature flags or guard clauses, jobs can be skipped using declarative `withRunIf()` and `withRunIfNot()`.

---

How This Integration Works
--------------------------

[](#how-this-integration-works)

This Laravel package is a thin wrapper around [chevere/workflow](https://chevere.org/packages/workflow). It provides:

ComponentWhat It Does`WorkflowServiceProvider`Registers the workflow system with Laravel's service container`AbstractWorkflow`Base class you extend to define workflows (like Laravel's `Mailable` or `Notification`)`WorkflowManager`Service that runs workflows with Laravel's container for dependency injection`Workflow` FacadeStatic access (`Workflow::run(...)`) for convenienceArtisan Commands`make:workflow`, `workflow:run`, `workflow:list`This integration uses a PSR-11 bridge backed by Laravel's container, so your Laravel services (Eloquent models, mailers, cache, etc.) are automatically available for dependency injection inside workflow jobs.

---

Installing
----------

[](#installing)

Workflow for Laravel is available through [Packagist](https://packagist.org/packages/chevere/workflow-laravel) and the repository source is at [chevere/workflow-laravel](https://github.com/chevere/workflow-laravel).

### Step 1: Install the package

[](#step-1-install-the-package)

```
composer require chevere/workflow-laravel
```

### Step 2: Verify installation

[](#step-2-verify-installation)

```
php artisan workflow:list
```

You should see: `No workflows found.`

---

Quick Start: Your First Workflow
--------------------------------

[](#quick-start-your-first-workflow)

### Step 1: Generate a workflow

[](#step-1-generate-a-workflow)

```
php artisan make:workflow GreetUser
```

This creates `app/Workflows/GreetUser.php`:

```
namespace App\Workflows;

use Chevere\Workflow\Interfaces\WorkflowInterface;
use Chevere\Workflow\Laravel\AbstractWorkflow;
use function Chevere\Workflow\sync;
use function Chevere\Workflow\variable;
use function Chevere\Workflow\workflow;

class GreetUser extends AbstractWorkflow
{
    protected function definition(): WorkflowInterface
    {
        return workflow(
            greet: sync(
                fn(string $name): string => "Hello, {$name}!",
                name: variable('name'),
            ),
        );
    }
}
```

### Step 2: Run it from a controller

[](#step-2-run-it-from-a-controller)

```
namespace App\Http\Controllers;

use App\Workflows\GreetUser;
use Illuminate\Http\JsonResponse;

class GreetController extends Controller
{
    public function __invoke(GreetUser $workflow): JsonResponse
    {
        $run = $workflow->run(name: 'Taylor');

        return response()->json([
            'message' => $run->response('greet')->string(),
            // "Hello, Taylor!"
        ]);
    }
}
```

### Step 3: Or use the Facade

[](#step-3-or-use-the-facade)

```
use Chevere\Workflow\Laravel\Facades\Workflow;
use App\Workflows\GreetUser;

$run = Workflow::run(GreetUser::class, name: 'Taylor');
echo $run->response('greet')->string(); // "Hello, Taylor!"
```

---

Core Concepts
-------------

[](#core-concepts)

Chevere Workflow uses **jobs**, **variables**, and **responses** to build a dependency graph that automatically determines execution order. Jobs can be `sync()` or `async()`, variables are runtime inputs declared with `variable()`, and responses reference outputs from previous jobs using `response()`.

For detailed information about these core concepts, execution graphs, and how the workflow engine works, see the [Chevere Workflow documentation](https://chevere.org/packages/workflow).

**Laravel-specific behavior:** When you extend `AbstractWorkflow`, Laravel's service container is automatically available for dependency injection in your job callables.

---

The AbstractWorkflow Class
--------------------------

[](#the-abstractworkflow-class)

This is the main class you extend. It follows the same pattern as Laravel's `Mailable`, `Notification`, or `FormRequest`:

```
namespace App\Workflows;

use Chevere\Workflow\Interfaces\WorkflowInterface;
use Chevere\Workflow\Laravel\AbstractWorkflow;
use function Chevere\Workflow\{workflow, sync, async, variable, response};

class ProcessOrder extends AbstractWorkflow
{
    protected function definition(): WorkflowInterface
    {
        return workflow(
            validate: sync(
                fn(int $orderId): array => ['id' => $orderId, 'valid' => true],
                orderId: variable('orderId'),
            ),
            charge: sync(
                fn(array $order): array => ['charged' => true, 'amount' => 99.99],
                order: response('validate'),
            ),
            sendReceipt: async(
                fn(array $charge): bool => true,
                charge: response('charge'),
            ),
            updateInventory: async(
                fn(array $order): bool => true,
                order: response('validate'),
            ),
        );
    }
}
```

### Methods Available

[](#methods-available)

MethodReturnsDescription`run(mixed ...$variables)``RunInterface`Execute the workflow`getWorkflow()``WorkflowInterface`Get the workflow definition (cached)`graph()``array`Get the execution graph### Using in Controllers

[](#using-in-controllers)

Laravel auto-injects `AbstractWorkflow` subclasses via the container:

```
class OrderController extends Controller
{
    public function store(ProcessOrder $workflow, Request $request): JsonResponse
    {
        $run = $workflow->run(orderId: $request->input('order_id'));

        return response()->json([
            'charged' => $run->response('charge', 'charged')->bool(),
            'amount'  => $run->response('charge', 'amount')->float(),
        ]);
    }
}
```

---

Using the Facade
----------------

[](#using-the-facade)

The `Workflow` facade provides static access to the `WorkflowManager`:

```
use Chevere\Workflow\Laravel\Facades\Workflow;

// Run a workflow class
$run = Workflow::run(ProcessOrder::class, orderId: 42);

// Run an inline workflow definition
use function Chevere\Workflow\{workflow, sync, variable};

$run = Workflow::run(
    workflow(
        greet: sync(
            fn(string $name): string => "Hello, {$name}!",
            name: variable('name'),
        ),
    ),
    name: 'World'
);
```

---

Using the WorkflowManager
-------------------------

[](#using-the-workflowmanager)

Inject `WorkflowManager` when you prefer dependency injection over facades:

```
use Chevere\Workflow\Laravel\WorkflowManager;

class OrderService
{
    public function __construct(
        private WorkflowManager $workflows
    ) {}

    public function processOrder(int $orderId): array
    {
        $run = $this->workflows->run(ProcessOrder::class, orderId: $orderId);

        return $run->toArray();
    }
}
```

---

Artisan Commands
----------------

[](#artisan-commands)

### `make:workflow`

[](#makeworkflow)

Generate a new workflow class:

```
php artisan make:workflow ProcessOrder
# Creates: app/Workflows/ProcessOrder.php
```

### `workflow:run`

[](#workflowrun)

Run a workflow from the command line:

```
php artisan workflow:run "App\Workflows\GreetUser" --var=name=Artisan
```

`workflow:run` accepts any class that extends `Chevere\Workflow\Laravel\AbstractWorkflow`.

Output:

```
Running workflow: App\Workflows\GreetUser
Execution graph:
  Level 0: greet
Workflow completed successfully.
UUID: a1b2c3d4-...
Responses:
  greet: Hello, Artisan!

```

Pass multiple variables:

```
php artisan workflow:run "App\Workflows\ProcessOrder" --var=orderId=42 --var=notify=true
```

### `workflow:list`

[](#workflowlist)

List all workflows found in the application:

```
php artisan workflow:list
```

Output:

```
+-----------------------------------------+--------+-------------+
| Workflow                                | Jobs   | Graph Depth |
+-----------------------------------------+--------+-------------+
| App\Workflows\ProcessOrder              | 4 jobs | 3 levels    |
| App\Workflows\ImageResize               | 4 jobs | 2 levels    |
+-----------------------------------------+--------+-------------+

```

Workflows are auto-discovered by scanning your application's `app/` directory for classes extending `AbstractWorkflow`.

---

Real-World Use Cases
--------------------

[](#real-world-use-cases)

### Use Case 1: User Registration Flow

[](#use-case-1-user-registration-flow)

This is the **best starting point** for Laravel developers. It mirrors what you'd normally build with events/listeners or job chains, but with automatic ordering.

```
namespace App\Workflows;

use App\Actions\CreateUser;
use App\Actions\SendWelcomeEmail;
use App\Actions\LogRegistration;
use App\Actions\AssignDefaultRole;
use Chevere\Workflow\Interfaces\WorkflowInterface;
use Chevere\Workflow\Laravel\AbstractWorkflow;
use function Chevere\Workflow\{workflow, sync, async, variable, response};

class RegisterUser extends AbstractWorkflow
{
    protected function definition(): WorkflowInterface
    {
        return workflow(
            // Step 1: Validate and create the user (must be sync — everything else depends on it)
            createUser: sync(
                CreateUser::class,
                email: variable('email'),
                password: variable('password'),
                name: variable('name'),
            ),

            // Step 2a: Send welcome email (async — can run async with 2b and 2c)
            sendWelcome: async(
                SendWelcomeEmail::class,
                userId: response('createUser', 'id'),
                email: response('createUser', 'email'),
            ),

            // Step 2b: Log the registration (async — runs async with 2a and 2c)
            logEvent: async(
                LogRegistration::class,
                userId: response('createUser', 'id'),
            ),

            // Step 2c: Assign default role (async — runs async with 2a and 2b)
            assignRole: async(
                AssignDefaultRole::class,
                userId: response('createUser', 'id'),
                role: 'member',
            ),
        );
    }
}
```

**Execution graph:**

```
Level 0: [createUser]                          ← runs first
Level 1: [sendWelcome, logEvent, assignRole]   ← run async after createUser

```

**In your controller:**

```
class RegisterController extends Controller
{
    public function store(Request $request, RegisterUser $workflow): JsonResponse
    {
        $request->validate([
            'email'    => 'required|email|unique:users',
            'password' => 'required|min:8',
            'name'     => 'required|string',
        ]);

        $run = $workflow->run(
            email: $request->input('email'),
            password: $request->input('password'),
            name: $request->input('name'),
        );

        return response()->json([
            'user_id' => $run->response('createUser', 'id')->int(),
            'message' => 'Registration complete',
        ], 201);
    }
}
```

**What each Action class looks like** (using `chevere/action`):

```
namespace App\Actions;

use App\Models\User;
use Chevere\Action\Action;
use Illuminate\Support\Facades\Hash;

class CreateUser extends Action
{
    public function __invoke(string $email, string $password, string $name): array
    {
        $user = User::create([
            'email'    => $email,
            'password' => Hash::make($password),
            'name'     => $name,
        ]);

        return ['id' => $user->id, 'email' => $user->email];
    }
}
```

You can also use plain closures instead of Action classes — see the Quick Start example.

---

### Use Case 2: Image Processing Pipeline

[](#use-case-2-image-processing-pipeline)

Process multiple image sizes async, then store them all:

```
namespace App\Workflows;

use Chevere\Workflow\Interfaces\WorkflowInterface;
use Chevere\Workflow\Laravel\AbstractWorkflow;
use Illuminate\Support\Facades\Storage;
use function Chevere\Workflow\{workflow, sync, async, variable, response};

class ProcessImage extends AbstractWorkflow
{
    protected function definition(): WorkflowInterface
    {
        return workflow(
            // These 3 resize jobs run async (no dependencies on each other)
            thumb: async(
                fn(string $path): string => $this->resize($path, 150, 150),
                path: variable('imagePath'),
            ),
            medium: async(
                fn(string $path): string => $this->resize($path, 800, 600),
                path: variable('imagePath'),
            ),
            large: async(
                fn(string $path): string => $this->resize($path, 1920, 1080),
                path: variable('imagePath'),
            ),

            // This runs after ALL three above complete
            store: sync(
                fn(string $thumb, string $medium, string $large): array => [
                    'thumb'  => Storage::put('thumbs', $thumb),
                    'medium' => Storage::put('medium', $medium),
                    'large'  => Storage::put('large', $large),
                ],
                thumb: response('thumb'),
                medium: response('medium'),
                large: response('large'),
            ),
        );
    }

    private function resize(string $path, int $w, int $h): string
    {
        // Your image processing logic (Intervention Image, GD, Imagick, etc.)
        return "/resized/{$w}x{$h}/" . basename($path);
    }
}
```

**Execution graph:**

```
Level 0: [thumb, medium, large]   ← async
Level 1: [store]                  ← after all three

```

---

### Use Case 3: Order Processing with Conditional Steps

[](#use-case-3-order-processing-with-conditional-steps)

This example demonstrates using `withRunIf()` for conditional job execution ([learn more](https://chevere.org/packages/workflow)):

```
namespace App\Workflows;

use Chevere\Workflow\Interfaces\WorkflowInterface;
use Chevere\Workflow\Laravel\AbstractWorkflow;
use function Chevere\Workflow\{workflow, sync, async, variable, response};

class ProcessOrder extends AbstractWorkflow
{
    protected function definition(): WorkflowInterface
    {
        return workflow(
            validate: sync(
                fn(int $orderId): array => ['id' => $orderId, 'total' => 150.00, 'valid' => true],
                orderId: variable('orderId'),
            ),

            charge: sync(
                fn(array $order): array => [
                    'transactionId' => 'txn_' . $order['id'],
                    'amount' => $order['total'],
                ],
                order: response('validate'),
            ),

            // Conditionally send receipt based on runtime variable
            sendReceipt: async(
                fn(string $txnId, float $amount): bool => true,
                txnId: response('charge', 'transactionId'),
                amount: response('charge', 'amount'),
            )->withRunIf(
                variable('sendReceipt')
            ),
        );
    }
}
```

---

### Use Case 4: API Aggregation

[](#use-case-4-api-aggregation)

Fetch data from multiple external APIs async:

```
namespace App\Workflows;

use Chevere\Workflow\Interfaces\WorkflowInterface;
use Chevere\Workflow\Laravel\AbstractWorkflow;
use Illuminate\Support\Facades\Http;
use function Chevere\Workflow\{workflow, sync, async, variable, response};

class FetchDashboardData extends AbstractWorkflow
{
    protected function definition(): WorkflowInterface
    {
        return workflow(
            // All three API calls run async
            weather: async(
                fn(string $city): array => Http::get("https://api.weather.example/v1/{$city}")->json(),
                city: variable('city'),
            ),
            news: async(
                fn(string $category): array => Http::get("https://api.news.example/v1/{$category}")->json(),
                category: variable('newsCategory'),
            ),
            stocks: async(
                fn(string $symbol): array => Http::get("https://api.stocks.example/v1/{$symbol}")->json(),
                symbol: variable('stockSymbol'),
            ),

            // Combine all results after they arrive
            dashboard: sync(
                fn(array $weather, array $news, array $stocks): array => [
                    'weather' => $weather,
                    'news' => $news,
                    'stocks' => $stocks,
                    'generated_at' => now()->toISOString(),
                ],
                weather: response('weather'),
                news: response('news'),
                stocks: response('stocks'),
            ),
        );
    }
}
```

Without Workflow, those 3 API calls would run sequentially (3x the latency). With `async()`, they run concurrently.

---

### Use Case 5: Data ETL Pipeline

[](#use-case-5-data-etl-pipeline)

Extract, transform, and load data with clear step separation:

```
namespace App\Workflows;

use Chevere\Workflow\Interfaces\WorkflowInterface;
use Chevere\Workflow\Laravel\AbstractWorkflow;
use function Chevere\Workflow\{workflow, sync, variable, response};

class ImportCsvData extends AbstractWorkflow
{
    protected function definition(): WorkflowInterface
    {
        return workflow(
            // Extract: read and parse CSV
            extract: sync(
                fn(string $path): array => array_map('str_getcsv', file($path)),
                path: variable('csvPath'),
            ),

            // Transform: clean and validate rows
            transform: sync(
                fn(array $rows): array => array_filter($rows, fn($row) => count($row) >= 3),
                rows: response('extract'),
            ),

            // Load: insert into database
            load: sync(
                fn(array $rows): int => count($rows), // DB::table('imports')->insert($rows)
                rows: response('transform'),
            ),
        );
    }
}
```

---

Advanced Workflow Features
--------------------------

[](#advanced-workflow-features)

Chevere Workflow supports several advanced features:

- **Conditional Execution**: Use `withRunIf()` and `withRunIfNot()` to conditionally skip jobs based on variables or previous job responses
- **Retry Policies**: Configure automatic retries with `withRetry()` for transient failures
- **Sync/Async Execution**: Use `sync()` for sequential jobs and `async()` for async execution
- **Mermaid Graphs**: Visualize your workflow's execution graph with Mermaid syntax

For detailed documentation on these features, see the [Chevere Workflow documentation](https://chevere.org/packages/workflow).

---

Dependency Injection
--------------------

[](#dependency-injection)

This is where the Laravel integration shines. **Laravel's container is automatically passed to Chevere Workflow**, so your Action classes can use constructor injection:

```
use Chevere\Action\Action;
use App\Services\PaymentGateway;
use Illuminate\Log\LogManager;

class ChargePayment extends Action
{
    public function __construct(
        private PaymentGateway $gateway,  // ← auto-injected from Laravel
        private LogManager $logger,       // ← auto-injected from Laravel
    ) {}

    public function __invoke(int $orderId, float $amount): array
    {
        $this->logger->info("Charging order {$orderId}: \${$amount}");
        $result = $this->gateway->charge($amount);

        return ['transactionId' => $result->id, 'status' => $result->status];
    }
}
```

Any service registered in Laravel's container can be injected. No extra configuration needed.

---

Error Handling
--------------

[](#error-handling)

When a job fails, Chevere Workflow wraps the original exception in a `WorkflowException`:

```
use Chevere\Workflow\Exceptions\WorkflowException;

try {
    $run = $workflow->run(orderId: 42);
} catch (WorkflowException $e) {
    // Which job failed?
    echo $e->name;        // "charge"

    // The job instance
    $job = $e->job;

    // The original exception
    $original = $e->throwable;
    echo $original->getMessage();
}
```

In a Laravel controller:

```
public function store(Request $request, ProcessOrder $workflow): JsonResponse
{
    try {
        $run = $workflow->run(orderId: $request->input('order_id'));

        return response()->json($run->toArray());
    } catch (WorkflowException $e) {
        report($e->throwable); // Log with Laravel

        return response()->json([
            'error' => "Job '{$e->name}' failed: " . $e->throwable->getMessage(),
        ], 500);
    }
}
```

---

Testing Your Workflows
----------------------

[](#testing-your-workflows)

### Setup

[](#setup)

In a Laravel app consuming this package, use your application's base test case as usual:

```
namespace Tests\Feature;

use Tests\TestCase;

class ProcessOrderTest extends TestCase
{
    // ...
}
```

If you are developing this package itself (or another package), use Orchestra Testbench and register `WorkflowServiceProvider`:

```
use Chevere\Workflow\Laravel\WorkflowServiceProvider;
use Orchestra\Testbench\TestCase;

class ProcessOrderTest extends TestCase
{
    protected function getPackageProviders($app): array
    {
        return [WorkflowServiceProvider::class];
    }
}
```

### Test the Execution Graph

[](#test-the-execution-graph)

Verify jobs run in the expected order:

```
public function testGraphOrder(): void
{
    $workflow = $this->app->make(ProcessOrder::class);
    $graph = $workflow->graph();

    // Level 0: validate runs first
    $this->assertSame(['validate'], $graph[0]);

    // Level 1: charge runs after validate
    $this->assertSame(['charge'], $graph[1]);

    // Level 2: sendReceipt, updateInventory run async
    $this->assertContains('sendReceipt', $graph[2]);
    $this->assertContains('updateInventory', $graph[2]);
}
```

### Test Responses

[](#test-responses)

```
public function testWorkflowResponses(): void
{
    $workflow = $this->app->make(ProcessOrder::class);
    $run = $workflow->run(orderId: 42, sendReceipt: true, hasDiscount: false);

    $this->assertTrue($run->response('charge', 'charged')->bool());
    $this->assertSame(42, $run->response('validate', 'id')->int());
}
```

### Test Conditional Execution

[](#test-conditional-execution)

```
public function testSkippedJobs(): void
{
    $workflow = $this->app->make(ProcessOrder::class);
    $run = $workflow->run(orderId: 42, sendReceipt: false, hasDiscount: false);

    $this->assertTrue($run->skip()->contains('sendReceipt'));
    $this->assertTrue($run->skip()->contains('applyDiscount'));
}
```

### Test Error Handling

[](#test-error-handling)

```
use Chevere\Workflow\Exceptions\WorkflowException;

public function testJobFailure(): void
{
    $this->expectException(WorkflowException::class);

    $workflow = $this->app->make(FailingWorkflow::class);
    $workflow->run(input: 'invalid');
}
```

### Test with the Facade

[](#test-with-the-facade)

```
use Chevere\Workflow\Laravel\Facades\Workflow;

public function testFacade(): void
{
    $run = Workflow::run(ProcessOrder::class, orderId: 42);

    $this->assertNotEmpty($run->uuid());
}
```

---

FAQ
---

[](#faq)

### How is this different from Laravel Queues?

[](#how-is-this-different-from-laravel-queues)

Laravel Queues push jobs to a queue worker (Redis, SQS, database) for background processing. Chevere Workflow runs jobs **in the current process** (with AMP async for concurrency). Use queues when you need background processing; use workflows when you need coordinated multi-step logic with data flowing between steps. By the way, you can dispatch a queued job that internally runs a workflow!

### Can I use Eloquent models inside jobs?

[](#can-i-use-eloquent-models-inside-jobs)

Yes. Laravel's container is passed to the workflow engine, so any service registered in the container (including Eloquent, facades, etc.) works normally.

### Can I mix this with Laravel's queue system?

[](#can-i-mix-this-with-laravels-queue-system)

Yes. You can dispatch a queued job that internally runs a workflow. The workflow itself runs synchronously/async within the queue worker process.

```
class ProcessOrderJob implements ShouldQueue
{
    public function handle(ProcessOrder $workflow): void
    {
        $workflow->run(orderId: $this->orderId);
    }
}
```

### Does the Facade work in Tinker?

[](#does-the-facade-work-in-tinker)

Yes:

```
>>> Chevere\Workflow\Laravel\Facades\Workflow::run(App\Workflows\GreetUser::class, name: 'Tinker')
```

Documentation
-------------

[](#documentation)

Documentation is available at [chevere.org/packages/workflow-laravel](https://chevere.org/packages/workflow-laravel).

License
-------

[](#license)

Copyright [Rodolfo Berrios A.](https://rodolfoberrios.com/)

Chevere is licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for the full license text.

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance82

Actively maintained with recent releases

Popularity16

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~65 days

Total

2

Last Release

64d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a87fbbcb1518fe64878bc53dedd9f61b81243cbb21b73c26f5e299d501375984?d=identicon)[rodolfo](/maintainers/rodolfo)

---

Top Contributors

[![rodber](https://avatars.githubusercontent.com/u/20590102?v=4)](https://github.com/rodber "rodber (17 commits)")

---

Tags

asynccheverelaravel-packagephpworkflowworkflow-engine

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleECS

Type Coverage Yes

### Embed Badge

![Health badge](/badges/chevere-workflow-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/chevere-workflow-laravel/health.svg)](https://phpackages.com/packages/chevere-workflow-laravel)
```

###  Alternatives

[laravel/ai

The official AI SDK for Laravel.

1.0k3.2M246](/packages/laravel-ai)[laravel/sail

Docker files for running a basic Laravel application.

1.9k205.7M1.3k](/packages/laravel-sail)[tallstackui/tallstackui

TallStackUI is a powerful suite of Blade components that elevate your workflow of Livewire applications.

728176.2k14](/packages/tallstackui-tallstackui)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[spatie/laravel-export

Create a static site bundle from a Laravel app

674146.0k6](/packages/spatie-laravel-export)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77922.3M186](/packages/laravel-mcp)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
