Skip to content

Creating Custom Triggers

FlowDrop's trigger system is extensible via the FlowDropEventType plugin. You can define new event types that fire workflows in response to any Drupal event — Commerce orders, Webform submissions, custom module hooks, or anything else you can subscribe to.

Module Required

Custom event type plugins require the flowdrop_trigger module.

API Stability

FlowDropEventTypeInterface and TriggerManagerInterface are @api in the 1.x release line and covered by the BC policy.

How It Works

  1. You create a PHP class implementing FlowDropEventTypeInterface with the #[FlowDropEventType] attribute.
  2. FlowDrop discovers your plugin automatically via Drupal's plugin discovery.
  3. Your event type appears in the trigger node's configuration panel in the visual editor.
  4. When your Drupal event fires, you call TriggerManagerInterface::processEvent() to dispatch it into FlowDrop.

Step 1: Create the Event Type Plugin

Create your class in src/Plugin/FlowDropEventType/ within your module:

<?php

declare(strict_types=1);

namespace Drupal\my_module\Plugin\FlowDropEventType;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\flowdrop_trigger\Attribute\FlowDropEventType;
use Drupal\flowdrop_trigger\Enum\EventCategory;
use Drupal\flowdrop_trigger\Enum\TriggerExecutionMode;
use Drupal\flowdrop_trigger\FlowDropEventTypeInterface;
use Drupal\flowdrop_trigger\Plugin\FlowDropEventTypeBase;

/**
 * Event type for Commerce order placement.
 *
 * Triggers when a new order is placed via Drupal Commerce.
 *
 * @internal This class is not part of the public API.
 */
#[FlowDropEventType(
  id: 'commerce.order.place',
  label: new TranslatableMarkup('Commerce: Order Placed'),
  description: new TranslatableMarkup('Triggered when a customer places a new order'),
  category: EventCategory::Entity,
  defaultOrchestrator: 'flowdrop_runtime:asynchronous',
  defaultExecutionMode: TriggerExecutionMode::Async,
  supportedEntityTypes: ['commerce_order'],
)]
class CommerceOrderPlace extends FlowDropEventTypeBase implements FlowDropEventTypeInterface {

  /**
   * {@inheritdoc}
   */
  public function extractTriggerData(
    ?EntityInterface $entity,
    ?EntityInterface $originalEntity = NULL,
    array $context = [],
  ): array {
    if ($entity === NULL) {
      throw new \InvalidArgumentException('Entity cannot be NULL for order events.');
    }

    // Build the data that will be available in the workflow.
    return [
      'event_type'   => $this->getEventId(),
      'order_id'     => $entity->id(),
      'order_number' => $entity->getOrderNumber(),
      'order_total'  => $entity->getTotalPrice()->getNumber(),
      'currency'     => $entity->getTotalPrice()->getCurrencyCode(),
      'customer_id'  => $entity->getCustomerId(),
      'email'        => $entity->getEmail(),
      'timestamp'    => time(),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getParameterSchema(): array {
    // Parameters available to configure in the trigger node panel.
    return [
      'type' => 'object',
      'properties' => [
        'min_order_total' => [
          'type'        => 'number',
          'title'       => 'Minimum order total',
          'description' => 'Only trigger for orders above this amount (0 = all orders)',
          'default'     => 0,
        ],
      ],
    ];
  }

}

#[FlowDropEventType] Attribute Parameters

Parameter Type Required Description
id string Yes Unique event type ID (e.g., commerce.order.place)
label TranslatableMarkup Yes Human-readable label shown in the editor
description TranslatableMarkup Yes Shown in the trigger config panel
category EventCategory No Groups events in the UI — Entity, User, Form, or Cron
defaultOrchestrator string No Default orchestrator ID (default: flowdrop_runtime:asynchronous)
defaultExecutionMode TriggerExecutionMode No Async or Sync (default: Async)
supportedEntityTypes array No Restrict to specific entity types (empty = all)

Step 2: Dispatch the Event

When your Drupal event fires, inject TriggerManagerInterface and call processEvent():

<?php

declare(strict_types=1);

namespace Drupal\my_module\EventSubscriber;

use Drupal\commerce_order\Event\OrderEvent;
use Drupal\flowdrop_trigger\Service\TriggerManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Dispatches FlowDrop triggers on Commerce order events.
 */
class CommerceOrderEventSubscriber implements EventSubscriberInterface {

  public function __construct(
    private readonly TriggerManagerInterface $triggerManager,
  ) {}

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents(): array {
    return [
      'commerce_order.place.post_transition' => 'onOrderPlace',
    ];
  }

  /**
   * Fires FlowDrop triggers when an order is placed.
   */
  public function onOrderPlace(OrderEvent $event): void {
    $order = $event->getOrder();

    $this->triggerManager->processEvent(
      'commerce.order.place',  // must match your plugin's id
      $order,
      [],                       // optional extra context
    );
  }

}

Register the subscriber in your module's services.yml:

services:
  my_module.commerce_order_event_subscriber:
    class: Drupal\my_module\EventSubscriber\CommerceOrderEventSubscriber
    arguments: ['@flowdrop_trigger.trigger_manager']
    tags:
      - { name: event_subscriber }

Non-Entity Events

For events that are not entity-based (e.g., custom module hooks), use processCronEvent() or processFormEvent(), or call processEvent() with NULL as the entity and pass all data through the $context array.

Then in your plugin, read from $context in extractTriggerData():

public function extractTriggerData(
  ?EntityInterface $entity,
  ?EntityInterface $originalEntity = NULL,
  array $context = [],
): array {
  return [
    'event_type'  => $this->getEventId(),
    'custom_data' => $context['my_custom_key'] ?? NULL,
    'timestamp'   => time(),
  ];
}

Accessing Trigger Data in a Workflow

The array returned by extractTriggerData() is passed as the initial data to the workflow. Use a Get Workflow Data node or Data Extractor node to access individual fields in your workflow nodes.

For the Commerce example above, a Data Extractor node can pull out order_id, order_total, email, etc.

Testing Your Event Type

Verify your plugin is discovered by FlowDrop:

# Open a workflow in the editor and add a Trigger node.
# Your event type should appear in the "Event Type" dropdown.

# Or confirm via Drush:
drush php:eval "
  \$manager = \Drupal::service('plugin.manager.flowdrop_event_type');
  print_r(array_keys(\$manager->getDefinitions()));
"

Trigger the event manually to test:

drush php:eval "
  \$order = \Drupal::entityTypeManager()->getStorage('commerce_order')->load(1);
  \$manager = \Drupal::service('flowdrop_trigger.trigger_manager');
  \$manager->processEvent('commerce.order.place', \$order);
  echo 'Trigger dispatched.';
"

Then check Administration > FlowDrop > Pipelines for the resulting execution.

Next Steps