PHP code example of maestrodimateo / laravel-workflow

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

    

maestrodimateo / laravel-workflow example snippets


use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Model;
use Maestrodimateo\Workflow\Traits\Workflowable;

class Invoice extends Model
{
    use HasUuids, Workflowable;
}

use Maestrodimateo\Workflow\Facades\Workflow;

// Get current status
$basket = Workflow::for($invoice)->currentStatus();
echo $basket->name;   // "Brouillon"
echo $basket->status; // "DRAFT"

// See available next steps
$options = Workflow::for($invoice)->nextBaskets();

// Transition
Workflow::for($invoice)->transition(
    $nextBasket->id,
    'Approved by manager',  // optional comment
);

workflow($invoice)->currentStatus();
workflow($invoice)->transition($basketId);

$wf = Workflow::for($model);

// Circuits accessible to a role
Workflow::circuitsForRole('manager');
Workflow::circuitsForRoles(['admin', 'manager']);

// Baskets accessible to a role (optionally scoped to a circuit)
Workflow::basketsForRole('validator');
Workflow::basketsForRole('validator', $circuitId);
Workflow::basketsForRoles(['admin', 'operator'], $circuitId);

use Maestrodimateo\Workflow\Models\Circuit;
use Maestrodimateo\Workflow\Models\Basket;

Circuit::forRole('admin')->get();
Basket::forRoles(['admin', 'manager'])->get();

$basket->hasRole('validator'); // true/false
$circuit->hasRole('admin');    // true/false

$history = Workflow::for($invoice)->history();

foreach ($history as $entry) {
    echo $entry->previous_status;  // "DRAFT"
    echo $entry->next_status;      // "REVIEW"
    echo $entry->duration_seconds; // 3600
    echo $entry->duration_human;   // "1h"
    echo $entry->done_by;          // User ID
    echo $entry->comment;          // "Sent for review"
}

// Total time
$seconds = Workflow::for($invoice)->totalDuration();

// Time in a specific step
$reviewTime = Workflow::for($invoice)->durationInStatus('REVIEW');

// Scope to a specific circuit
Workflow::for($invoice)->in($approvalCircuit)->currentStatus();
Workflow::for($invoice)->in($complianceCircuit)->transition($basketId);
Workflow::for($invoice)->in('circuit-uuid')->history();

// See status in ALL circuits at once
$statuses = Workflow::for($invoice)->allStatuses();
// [
//     'circuit-a-id' => ['circuit' => Circuit, 'basket' => Basket],
//     'circuit-b-id' => ['circuit' => Circuit, 'basket' => Basket],
// ]

// List circuits the model belongs to
$circuits = Workflow::for($invoice)->circuits();

// Lock the model (default: 30 minutes)
Workflow::for($invoice)->lock();
Workflow::for($invoice)->lock(60); // 1 hour

// Check lock status
Workflow::for($invoice)->isLocked();       // true
Workflow::for($invoice)->isLockedByMe();   // true
Workflow::for($invoice)->lockedBy();       // "user-uuid"
Workflow::for($invoice)->lockExpiration(); // Carbon instance

// Transition (auto-checks the lock)
Workflow::for($invoice)->transition($basketId);
// → OK if you hold the lock (lock is released after transition)
// → ModelLockedException if locked by someone else

// Release manually
Workflow::for($invoice)->unlock();

// Admin force unlock
Workflow::for($invoice)->unlock(force: true);

// Available models (not locked or lock expired)
Invoice::fromBasket($reviewBasket)->unlocked()->get();

// Models I'm working on
Invoice::lockedBy(auth()->id())->get();

use Maestrodimateo\Workflow\Exceptions\ModelLockedException;

try {
    Workflow::for($invoice)->transition($basketId);
} catch (ModelLockedException $e) {
    return back()->withErrors([
        'lock' => $e->getMessage(),
        // "Ce dossier est verrouillé par [user] jusqu'à [14:30]."
    ]);
}

use Maestrodimateo\Workflow\Contracts\TransitionAction;

class GeneratePdfAction implements TransitionAction
{
    public static function key(): string { return 'generate_pdf'; }
    public static function label(): string { return 'Generate Pdf'; }

    public function execute(Model $model, Basket $from, Basket $to, array $config = []): void
    {
        // Your logic here
    }
}

Workflow::registerAction(GeneratePdfAction::class);

// In your EventServiceProvider
protected $listen = [
    \Maestrodimateo\Workflow\Events\TransitionEvent::class => [
        \App\Listeners\NotifySlack::class,
        \App\Listeners\SyncWithExternalSystem::class,
    ],
];

public function handle(TransitionEvent $event): void
{
    $event->currentBasket; // Source basket
    $event->nextBasket;    // Target basket
    $event->model;         // The transitioned model
    $event->comment;       // Transition comment
}

// config/workflow.php
'message_variables' => [
    'reference' => fn ($model) => $model->reference,
    'montant'   => fn ($model) => number_format($model->amount, 2, ',', ' ') . ' €',
],

use Maestrodimateo\Workflow\Facades\Workflow;

// In a seeder
Workflow::importFromJson(database_path('seeders/workflow-invoices.json'));

// Or via the manager directly
app(\Maestrodimateo\Workflow\WorkflowManager::class)::importFromJson($path);

// config/workflow.php
return [
    'routes' => [
        'prefix'           => 'workflow',
        'middleware'        => ['api'],
        'admin_middleware'  => ['web'],
    ],
    'auth_identifier' => 'id',
    'message_variables' => [],
    'lock' => [
        'duration_minutes' => 30,
    ],
];

$invoice->baskets;        // Collection<Basket> — all baskets the model is/has been in
$invoice->histories;      // Collection<History> — all transition history entries
$invoice->workflowLock;   // WorkflowLock|null — the active lock on this model

// Current status (last basket attached)
$invoice->currentStatus();                // ?Basket — across all circuits
$invoice->currentStatus($circuit);        // ?Basket — in a specific circuit
$invoice->currentStatus('circuit-uuid');   // same with a string ID

// Inspect the current basket
$invoice->currentStatus()->status;        // "REVIEW"
$invoice->currentStatus()->name;          // "Under Review"
$invoice->currentStatus()->color;         // "#2563eb"
$invoice->currentStatus()->roles;         // ["manager", "validator"]
$invoice->currentStatus()->hasRole('manager'); // true

// Access the circuit
$invoice->currentStatus()->circuit->name; // "Invoice Approval"

// Navigate the workflow graph
$invoice->currentStatus()->next;          // Collection<Basket> — possible next steps
$invoice->currentStatus()->previous;      // Collection<Basket> — where it came from

// History with duration tracking
$invoice->histories->each(function ($h) {
    $h->previous_status;   // "DRAFT"
    $h->next_status;       // "REVIEW"
    $h->comment;           // "Sent for review"
    $h->done_by;           // "user-uuid"
    $h->duration_seconds;  // 3600
    $h->duration_human;    // "1h"
    $h->created_at;        // Carbon
});

// Lock info
$invoice->workflowLock?->locked_by;      // "user-uuid" or null
$invoice->workflowLock?->expires_at;     // Carbon or null
$invoice->workflowLock?->isActive();     // true if not expired

// Models currently in a specific basket
Invoice::fromBasket($reviewBasket)->get();

// Available models (not locked or lock expired)
Invoice::unlocked()->get();

// Models locked by a specific user
Invoice::lockedBy(auth()->id())->get();

// Combine scopes
Invoice::fromBasket($reviewBasket)->unlocked()->get();

$invoice = Invoice::create(['number' => 'INV-001']);

$invoice->currentStatus()->status; // "DRAFT" — automatic
$invoice->baskets->count();        // 1 (or more if multiple circuits)
bash
php artisan vendor:publish --tag=workflow-config
php artisan vendor:publish --tag=workflow-migrations
php artisan migrate
bash
php artisan vendor:publish --tag=workflow-views
bash
php artisan make:workflow-action GeneratePdfAction