dereuromark/cakephp-workflow

State machine and workflow engine for CakePHP with PHP 8 Attributes, YAML support, and admin UI

Maintainers

Package info

github.com/dereuromark/cakephp-workflow

Type:cakephp-plugin

pkg:composer/dereuromark/cakephp-workflow

Statistics

Installs: 313

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

0.1.0 2026-03-29 23:14 UTC

README

CI codecov Latest Stable Version Minimum PHP Version License

State machine and workflow engine for CakePHP with PHP 8 Attributes, YAML/NEON config support, and admin UI.

Requirements

  • PHP 8.2+
  • CakePHP 5.2+

Installation

composer require dereuromark/cakephp-workflow

Load the plugin:

bin/cake plugin load Workflow

Run migrations:

bin/cake migrations migrate --plugin Workflow

Configuration

Configure the plugin in your config/app.php:

'Workflow' => [
    'loader' => [
        'namespaces' => [
            'App\\Workflow',
        ],
        'configPath' => CONFIG . 'workflows' . DS,
    ],
    'logging' => true,
    'locking' => true,
    'timeouts' => true,
    'lockDuration' => 30,
],

Defining Workflows

Using PHP 8 Attributes (Recommended)

Create state classes in your namespace:

<?php
namespace App\Workflow\Order;

use Workflow\Attribute\StateMachine;
use Workflow\State\AbstractState;

#[StateMachine(name: 'order', table: 'Orders', field: 'state')]
abstract class OrderState extends AbstractState
{
}
<?php
namespace App\Workflow\Order;

use Workflow\Attribute\Command;
use Workflow\Attribute\FinalState;
use Workflow\Attribute\Guard;
use Workflow\Attribute\InitialState;
use Workflow\Attribute\Transition;

#[InitialState]
#[Transition(to: PaidState::class, name: 'pay', happy: true)]
class PendingState extends OrderState
{
    #[Guard('pay')]
    public function ensurePayable(): bool|string
    {
        return (float)$this->getEntity()?->get('total') > 0
            ? true
            : 'Order total must be positive';
    }

    #[Command('pay')]
    public function markPaymentCaptured(): void
    {
        $this->getEntity()?->set('payment_captured', true);
    }
}
<?php
namespace App\Workflow\Order;

use Workflow\Attribute\FinalState;
use Workflow\Attribute\OnEnter;

#[FinalState]
class PaidState extends OrderState
{
    #[OnEnter]
    public function sendReceipt(): void
    {
        // Runs after the entity enters the paid state.
    }
}

Using NEON or YAML

Install the optional parser you want:

  • NEON: composer require nette/neon
  • YAML: composer require symfony/yaml

Create workflow files in config/workflows/:

order:
  table: Orders
  field: state
  states:
    pending:
      initial: true
    paid:
      color: '#00AA00'
    completed:
      final: true
  transitions:
    pay:
      from: [pending]
      to: paid
      happy: true
    complete:
      from: [paid]
      to: completed

Using the Workflow

Add the behavior to your table:

public function initialize(array $config): void
{
    $this->addBehavior('Workflow.Workflow', [
        'workflow' => 'order',
    ]);
}

Apply transitions:

$behavior = $this->Orders->getBehavior('Workflow');

if ($behavior->canTransition($order, 'pay')) {
    // Atomic: applies transition, saves entity, logs - all in one transaction
    $result = $behavior->transition($order, 'pay', ['user_id' => $userId]);
}

See the documentation for the full API.

CLI Commands

bin/cake workflow init order Orders    # Scaffold new workflow
bin/cake workflow list                 # List all workflows
bin/cake workflow show order           # Show workflow details
bin/cake workflow validate             # Validate definitions

Features

  • PHP 8 Attributes or NEON/YAML definitions
  • Guards, commands, and lifecycle callbacks
  • Audit logging with user tracking
  • Pessimistic locking for concurrent transitions
  • Automatic timeouts
  • Admin UI with Mermaid.js diagrams
  • CLI tools for management and validation

Documentation

Full documentation: https://dereuromark.github.io/cakephp-workflow/