Plugin Integration

Voodflow provides a first-class API for third-party Laravel plugins and packages to expose their own events and models. This allows users to build workflows triggered by any plugin event — not just CRUD operations on core models.

There are three independent mechanisms, and you can use them together:

What you need Mechanism
Expose a custom event in the trigger picker Voodflow::registerEvent()
Let Voodflow read fields from your model HasVoodflow interface or Voodflow::registerModelFields()
Control the exact payload sent to a workflow PayloadProvider interface
Customize the trigger identifier of an event IdentifiableEvent interface

1. Registering a Custom Event

Call Voodflow::registerEvent() inside your plugin's ServiceProvider::boot() to make an event selectable in the Event Trigger node.

PHP
use Voodflow\Voodflow\Voodflow;

public function boot(): void
{
    Voodflow::registerEvent(
        eventClass: \Vendor\MyPlugin\Events\InvoicePaid::class,
        name:       'Invoice Paid',
        description: 'Fired when an invoice is marked as paid',
        group:      'MyPlugin',
    );

    Voodflow::registerEvent(
        eventClass: \Vendor\MyPlugin\Events\SubscriptionExpired::class,
        name:       'Subscription Expired',
        group:      'MyPlugin',
    );
}
Parameter Type Description
eventClass string Fully-qualified class name of the event
name string Human-readable label shown in the event picker
description string|null Optional tooltip/description in the UI
group string|null Groups events in the dropdown (e.g. "MyPlugin")

Events are standard Laravel events dispatched with event(). Voodflow listens for them automatically — there is nothing else to configure.

PHP
// Anywhere in your plugin:
event(new \Vendor\MyPlugin\Events\InvoicePaid($invoice));

2. Exposing Model Fields (HasVoodflow)

To let Voodflow read and map fields from your Eloquent model (e.g. for {{tags}} resolution in nodes), implement the HasVoodflow interface on the model:

PHP
use Voodflow\Voodflow\Contracts\HasVoodflow;

class Invoice extends Model implements HasVoodflow
{
    public static function getVoodflowFields(): array
    {
        return [
            'essential' => [
                'id'         => 'ID',
                'number'     => 'Invoice Number',
                'total'      => 'Total',
                'status'     => 'Status',
                'due_date'   => 'Due Date',
                'created_at' => 'Created At',
            ],
            'relations' => [
                'customer' => [
                    'fields' => [
                        'id'    => 'Customer ID',
                        'name'  => 'Customer Name',
                        'email' => 'Customer Email',
                    ],
                ],
                'items' => [
                    'fields' => [
                        'description' => 'Item Description',
                        'quantity'    => 'Quantity',
                        'unit_price'  => 'Unit Price',
                    ],
                ],
            ],
        ];
    }
}

Return value structure

Key Type Description
essential array Flat fields of this model. Key = field name, value = label.
relations array Optional. Keyed by relation method name.
relations.*.fields array Fields from the related model to expose.
relations.*.expand array Nested relations to recursively expand.

Once HasVoodflow is implemented, Voodflow automatically discovers the model's fields when an event carrying that model instance is fired.


3. Registering Fields for Third-Party Models

If you cannot modify a model (e.g. it belongs to another package), use Voodflow::registerModelFields() in your ServiceProvider::boot():

PHP
use Voodflow\Voodflow\Voodflow;

public function boot(): void
{
    Voodflow::registerModelFields(
        modelClass: \SomeVendor\Package\Models\Order::class,
        fields: [
            'essential' => [
                'id'         => 'Order ID',
                'reference'  => 'Reference',
                'total'      => 'Total',
                'status'     => 'Status',
            ],
            'relations' => [
                'customer' => [
                    'fields' => ['name' => 'Name', 'email' => 'Email'],
                ],
            ],
        ],
        alias: 'order',   // Optional: key used in the payload (defaults to model name)
    );
}

The optional alias parameter controls the key under which the model data is nested in the workflow context. For example, with alias: 'order', templates use {{order.total}} instead of {{Order.total}}.


4. Controlling the Payload (PayloadProvider)

By default, Voodflow serializes the public properties of your event class using reflection. To take full control of what data the workflow receives, implement PayloadProvider on your event:

PHP
use Voodflow\Voodflow\Contracts\PayloadProvider;

class InvoicePaid implements PayloadProvider
{
    public function __construct(
        public readonly Invoice $invoice,
        public readonly string  $paidBy,
    ) {}

    public function toVoodflowPayload(): array
    {
        return [
            'invoice' => [
                'id'     => $this->invoice->id,
                'number' => $this->invoice->number,
                'total'  => $this->invoice->total,
                'status' => $this->invoice->status,
            ],
            'paid_by'    => $this->paidBy,
            'paid_at'    => now()->toIso8601String(),
        ];
    }
}

The array returned by toVoodflowPayload() becomes the root context of the workflow execution. All {{tags}} in downstream nodes resolve against this structure.


5. Customizing the Event Identifier (IdentifiableEvent)

By default, Voodflow uses the fully-qualified class name (FQCN) as the trigger identifier. If you want to use a stable, human-friendly string (for example to maintain compatibility across refactors), implement IdentifiableEvent:

PHP
use Voodflow\Voodflow\Contracts\IdentifiableEvent;

class InvoicePaid implements IdentifiableEvent
{
    public function voodflowEventIdentifier(): string
    {
        return 'myplugin.invoice.paid';
    }
}

Changing the identifier of an existing event will break any workflow triggers already pointing to the old identifier. Use this when first creating the event, not as a retroactive refactor.


Full Example

A typical plugin ServiceProvider integrating all mechanisms:

PHP
namespace Vendor\MyPlugin;

use Illuminate\Support\ServiceProvider;
use Voodflow\Voodflow\Voodflow;
use Vendor\MyPlugin\Events\InvoicePaid;
use Vendor\MyPlugin\Events\SubscriptionExpired;
use Vendor\MyPlugin\Models\Invoice;
use Vendor\MyPlugin\Models\Subscription;

class MyPluginServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // Register events in the trigger picker
        Voodflow::registerEvent(
            eventClass: InvoicePaid::class,
            name: 'Invoice Paid',
            description: 'Fired when an invoice payment is confirmed',
            group: 'MyPlugin',
        );

        Voodflow::registerEvent(
            eventClass: SubscriptionExpired::class,
            name: 'Subscription Expired',
            group: 'MyPlugin',
        );

        // Expose model fields for tag resolution
        // (only needed if the models don't already implement HasVoodflow)
        Voodflow::registerModelFields(
            modelClass: Subscription::class,
            fields: [
                'essential' => [
                    'id'         => 'ID',
                    'plan'       => 'Plan',
                    'expires_at' => 'Expires At',
                ],
            ],
            alias: 'subscription',
        );
    }
}

Dispatch events from anywhere in your plugin as normal:

PHP
event(new InvoicePaid($invoice, auth()->id()));
event(new SubscriptionExpired($subscription));

Plugin nodes as triggers

From 0.2.6, a companion plugin node can start a workflow when it stores eventClass in its config. It does not have to use the core type trigger. Register the event with Voodflow::registerEvent() as usual, and set function to trigger if the node should use the amber trigger accent while remaining in the Integrations tab.

Upstream field hints follow the same rule: nodes that expose getAvailableFields() keep availableFields after a config save, including plugin nodes that publish a filterFieldsMap.

VoodBuilder blocks

See Configuration → VoodBuilder companion blocks. Those blocks are optional marketing sections for sites that also run VoodBuilder. Leave VOODFLOW_VOODBUILDER_BLOCKS unset (false) on application installs.

Interface Summary

Interface / Class Namespace Purpose
HasVoodflow Voodflow\Voodflow\Contracts Declare field schema on a model
PayloadProvider Voodflow\Voodflow\Contracts Return a custom payload array from an event
IdentifiableEvent Voodflow\Voodflow\Contracts Override the trigger identifier string
Voodflow::registerEvent() Voodflow\Voodflow\Voodflow Register an event in the UI picker at runtime
Voodflow::registerModelFields() Voodflow\Voodflow\Voodflow Register field schema for a third-party model