Skip to content

View Helper

The WorkflowHelper provides template helpers for rendering workflow-related UI elements.

Setup

Load the helper in your controller or AppView:

php
public function initialize(): void
{
    parent::initialize();
    $this->addHelper('Workflow.Workflow');
}

Including Mermaid.js

To render workflow diagrams, include the Mermaid.js library:

php
<?= $this->Workflow->includeMermaid() ?>

This outputs the CDN script tag and initializes Mermaid. Include it once in your layout or before any diagrams.

Rendering Diagrams

Render a visual workflow diagram:

php
<?= $this->Workflow->diagram($definition) ?>

For an order workflow this renders a diagram like the following, with the current state highlighted in amber when you pass the entity's state:

With options:

php
<?= $this->Workflow->diagram($definition, [
    'id' => 'order-workflow-diagram',
    'class' => 'mermaid workflow-diagram',
    'currentState' => $order->state,
    'showDetails' => true,
    'detailMarkers' => 'ascii',
]) ?>

currentState is what highlights the active node. Pass the entity state value directly when you want the current step emphasized.

To get raw Mermaid code (useful for custom rendering):

php
$mermaidCode = $this->Workflow->getMermaidCode($definition);

State Badge

Display the current state as a styled badge:

php
<?= $this->Workflow->stateBadge($definition, $entity->state) ?>

The badge automatically uses the state's configured color and calculates a contrasting text color.

With custom class:

php
<?= $this->Workflow->stateBadge($definition, $entity->state, [
    'class' => 'badge rounded-pill',
]) ?>

Transition Buttons

Render buttons for available transitions:

php
<?= $this->Workflow->transitionButtons($entity, $availableTransitions, [
    'definition' => $definition,
]) ?>

With custom URL and styling:

php
<?= $this->Workflow->transitionButtons($entity, $availableTransitions, [
    'definition' => $definition,
    'url' => ['controller' => 'Orders', 'action' => 'transition'],
    'buttonClass' => 'btn btn-primary btn-sm',
]) ?>

Each button includes a data-transition attribute for JavaScript handling. When you pass the workflow definition, button labels use each transition's display label automatically instead of the raw transition name.

Transition Labels

Transitions keep their stable internal names (such as queue_ai_draft) for code, routing, and storage, but you can also define human-readable labels for UI rendering:

php
'transitions' => [
    'queue_ai_draft' => [
        'from' => 'extracted',
        'to' => 'ai_drafting',
        'label' => 'Apply AI draft',
    ],
],

The helper and renderers use these labels automatically:

  • diagram() / getMermaidCode()
  • transitionButtons()
  • postTransitionButtons()
  • panel()

For custom app UIs, you can resolve labels directly:

php
$label = $this->Workflow->transitionLabel($definition, 'queue_ai_draft');
$labels = $this->Workflow->transitionLabels($definition, $availableTransitions);

WARNING

transitionButtons() renders GET links. For state changes prefer panel() / postTransitionButtons() below, which render CSRF-protected POST forms.

Workflow Panel (drop-in)

panel() renders the current-state badge plus a CSRF-protected POST button for each available transition — the whole status widget in one call:

php
<?= $this->Workflow->panel($definition, $order, $availableTransitions, [
    'url' => ['controller' => 'Orders', 'action' => 'transition'],
]) ?>

The entity id and transition name are appended to the URL automatically (e.g. /orders/transition/42/pay), and each button POSTs transition, ready to be handled by WorkflowComponent::handleTransition():

php
// OrdersController::transition()
public function transition($id)
{
    $order = $this->Orders->get($id);

    return $this->Workflow->handleTransition($this->Orders, $order, ['action' => 'view', $id]);
}

Use postTransitionButtons() if you only want the buttons without the badge wrapper.

For compact embedded diagrams, widget() includes browser-side export controls for the rendered graph:

php
<?= $this->Workflow->widget($definition, [
    'title' => 'Order workflow',
    'currentState' => $order->state,
    'export' => ['svg', 'png', 'mmd'],
]) ?>
  • svg exports the original rendered Mermaid SVG.
  • png sanitizes the rendered graph before rasterizing it in-browser.
  • mmd downloads the Mermaid source used by the widget.

If you need canonical server-side exports instead, use the workflow draw action:

text
/workflow/workflows/draw/{workflow}?format=svg|png|mmd

Server-side svg / png rendering requires GraphViz (dot) and is separate from the widget export path.

To highlight a specific state in the server-side export:

text
/workflow/workflows/draw/order?format=svg&currentState=paid

Or let the controller resolve the state from a workflow entity id:

text
/workflow/workflows/draw/order?format=png&id=42

When id is present, the export endpoint loads the configured workflow table and highlights the current value from the workflow field automatically.

Getting State Color

Get the color configured for a state:

php
$color = $this->Workflow->getStateColor($definition, $entity->state);

Returns the hex color (e.g., #00AA00) or a default gray if none is configured.

Complete Example

php
<?php
// In your controller: pass definition and availableTransitions to view
// $workflow = $this->workflowRegistry->get($order);
// $this->set('definition', $workflow->getDefinition());
// $this->set('availableTransitions', $workflow->getAvailableTransitions());
?>

<?= $this->Workflow->includeMermaid() ?>

<div class="order-details">
    <h3>Order #<?= $order->id ?></h3>

    <p>
        Status: <?= $this->Workflow->stateBadge($definition, $order->state) ?>
    </p>

    <?php if ($availableTransitions) { ?>
        <div class="actions">
            <?= $this->Workflow->transitionButtons($order, $availableTransitions) ?>
        </div>
    <?php } ?>
</div>

<div class="workflow-visualization">
    <h4>Workflow</h4>
    <?= $this->Workflow->diagram($definition) ?>
</div>

Released under the MIT License.