Plenty Flow

The Flow module, commonly referred to as plentyflow, provides a powerful, event-driven Workflow Automation System for plentymarkets. It enables users to define, configure, and execute automated workflows based on a variety of triggers, actions, and filters. This significantly enhances operational efficiency by automating complex business processes across the platform, from order processing to customer communication.

Registering Triggers to Flow

To register a new trigger to the flow, you need to implement the FlowTriggerPlentyDefinitionContract abstract class and use the TriggerRegistrationService to register your trigger.

Step-by-Step Guide

  1. Create a New Trigger Class

    • Implement the FlowTriggerPlentyDefinitionContract abstract class.

    • Define all the required abstract methods.

      <?php
      
      namespace YourNamespace;
      
      use Plenty\Modules\Flow\Triggers\Definitions\Contracts\FlowTriggerPlentyDefinitionContract;
      
      class YourCustomTrigger extends FlowTriggerPlentyDefinitionContract
      {
          public function getIdentifier(): string
          {
              return 'your_custom_trigger'; // Unique identifier for your trigger
          }
      
          public function getTriggerObject(): string
          {
              return 'your_trigger_object'; // Object type for your trigger
          }
      
          public function getName(): string
          {
              return 'Your Custom Trigger';  // Display name of your trigger
          }
      
          public function getDescription(): string
          {
              return 'Description of your custom trigger'; // Description of your trigger
          }
      
          public function getIcon(): string
          {
              return 'icon-name'; // Icon name for your trigger eg. 'shopping_cart'
          }
      
          public function getTooltip(): string
          {
              return 'Tooltip for your custom trigger'; // Tooltip for your trigger
          }
      
          public function getUIConfigFields(): array
          {
              return []; // Return an array of FormField objects
          }
      }
  2. Register the Trigger

    • Use the TriggerRegistrationService to register your trigger.

    • Ensure that the registration method is called during the application bootstrapping process.

      // In your service provider or bootstrap file
      /** @var TriggerRegistrationService $registrationService */
      $registrationService = app(TriggerRegistrationService::class);
      $registrationService->registerTrigger(app(YourNamespace\YourTriggerRegistrar::class));

Example: OrderTriggerDefinition

  1. Create the Base Trigger Class

    • Implement the FlowTriggerPlentyDefinitionContract abstract class.

    • Define common methods that will be shared by multiple triggers.

      <?php
      
      namespace Plenty\Modules\Flow\Triggers\Definitions\Models\OrderTrigger;
      
      use Plenty\Modules\Flow\Triggers\Definitions\Contracts\FlowTriggerPlentyDefinitionContract;
      
      abstract class OrderTriggerDefinition extends FlowTriggerPlentyDefinitionContract
      {
          public function getTriggerObject(): string
          {
              return 'order';
          }
      
          public function getIcon(): string
          {
              return 'shopping_cart';
          }
      
          public function getTooltip(): string
          {
              return 'Order related trigger';
          }
      }
  2. Create the Trigger Class

    • Extend the OrderTriggerDefinition class.

    • Define all the required abstract methods.

      <?php
      
      namespace Plenty\Modules\Flow\Triggers\Definitions\Models\OrderTrigger;
      
      use Plenty\Modules\Flow\Contracts\UIConfigFormContract;
      use Plenty\Modules\Flow\DataModels\ConfigForm\CheckboxGroupField;
      use Plenty\Modules\Order\Status\Contracts\OrderStatusRepositoryContract;
      
      class OrderStatusChangedTrigger extends OrderTriggerDefinition
      {
          const IDENTIFIER = 'orderStatusChanged';
      
          public function getIdentifier(): string
          {
              return self::IDENTIFIER;
          }
      
          public function getName(): string
          {
              return 'Order Status Changed';
          }
      
          public function getDescription(): string
          {
              return 'Triggered when the status of an order changes';
          }
      
          public function getUIConfigFields(): array
          {
              $configForm = app(UIConfigFormContract::class, [
                  'translationNamespace' => 'module_flow'
              ]);
      
              $orderStatusRepository = app(OrderStatusRepositoryContract::class);
              $statuses = $orderStatusRepository->all();
              $lang = \App::getLocale() ?? config('defaultSystemLanguage');
      
              $statusField = $this->getFormField(CheckboxGroupField::class, [
                  'name' => 'statusId',
                  'label' => 'config.orderStatus'
              ]);
      
              foreach ($statuses as $status) {
                  $statusField->addCheckboxGroupValue(
                      $status->names[$lang] ?? (string)array_first($status->names) ?? (string)$status->statusId,
                      $status->statusId,
                      false
                  );
              }
      
              $configForm->addCheckboxGroupField($statusField);
      
              return $configForm->getConfigFields();
          }
      }
  3. Register the Trigger

    • Use the TriggerRegistrationService to register your trigger.

    • Ensure that the registration method is called during the application bootstrapping process.

      // In your service provider or bootstrap file
      /** @var TriggerRegistrationService $registrationService */
      $registrationService = app(TriggerRegistrationService::class);
      $registrationService->registerTrigger(app(Plenty\Modules\Flow\Triggers\OrderStatusChangedTrigger::class));

Explanation of EventTriggerService

The EventTriggerService is responsible for firing triggers based on specific events. The fireTrigger method verifies if a trigger exists and then starts the flows associated with that trigger.

<?php

namespace Plenty\Modules\Flow\Services;

class EventTriggerService
{
    public function fireTrigger(
        string $triggerIdentifier,
        string $object,
        string $objectId,
        string $fieldName = null,
        $fieldValue = null
    ): bool {
        // Logic to fire the trigger
        return true;
    }
}

Example of Firing the Order Status Change Trigger

/** @var EventTriggerService $eventTriggerService */
$eventTriggerService = app(EventTriggerService::class);
/** @var OrderStatusChangedTrigger $orderStatusChangedTrigger */
$orderStatusChangedTrigger = app(OrderStatusChangedTrigger::class);
$eventTriggerService->fireTrigger(
    $orderStatusChangedTrigger->getTriggerIdentifier(),
    $orderStatusChangedTrigger->getTriggerObject(),
    (string)$order->id,
    'statusId',
    $order->statusId
);

Registering Filters to Flow

To register a new filter to the flow, you need to implement the FilterDefinitionContract abstract class and use the FilterRegistrationService to register your filter.

Step-by-Step Guide

  1. Create a New Filter Class

    • Implement the FilterDefinitionContract abstract class.

    • Define all the required abstract methods.

      <?php
      
      namespace YourNamespace\Flow\Filters;
      
      use Plenty\Modules\Flow\Filters\Definitions\Contracts\FilterDefinitionContract;
      
      class YourCustomFilter extends FilterDefinitionContract
      {
          public function getIdentifier(): string
          {
              return 'your_custom_filter'; // Unique identifier for your filter
          }
      
          public function getName(): string
          {
              return 'Your Custom Filter'; // Display name of your filter
          }
      
          public function getDescription(): string
          {
              return 'Description of your custom filter';  // Description of your filter
          }
      
          public function getIcon(): string
          {
              return 'icon-name'; // Icon name for your filter eg. 'shopping_cart'
          }
      
          public function getTooltip(): string
          {
              return 'Tooltip for your custom filter';  // Tooltip for your filter
          }
      
          public function getUIConfigFields(): array
          {
              return []; // Return an array of FormField objects
          }
      
          public function getRequiredInputTypes(): array
          {
              return []; // Return an array of required input types eg. FlowTriggerObjectOrder::IDENTIFIER
          }
      
          public function getOperators(): array
          {
              return []; // Return an array of operators eg. FilterOperators::IN, FilterOperators::NOT_IN
          }
      
          public function getAvailabilities(): array
          {
              return []; // Return an array of availabilities eg. BranchDefinition::IDENTIFIER, SearchDefinition::IDENTIFIER. '.' .SearchDefinition::ORDER_SEARCH
          }
      
          public function performFilter(array $inputs, array $filterField, array $extraParams = []): bool
          {
               // Perform filter logic here
              return true;
          }
      }
  2. Register the Filter

    • Use the FilterRegistrationService to register your filter.

    • Ensure that the registration method is called during the application bootstrapping process.

      // In your service provider or bootstrap file
      /** @var FilterRegistrationService $registrationService */
      $registrationService = app(FilterRegistrationService::class);
      $registrationService->registerFilter(app(YourNamespace\YourCustomFilter::class));

Example: StatusFilter

  1. Create the Base Filter Class

    • Implement the FilterDefinitionContract abstract class.

    • Define common methods that will be shared by multiple filters.

      <?php
      
      namespace Plenty\Modules\Flow\Filters\Definitions\Models\OrderFilter;
      
      use Plenty\Modules\Flow\Filters\Definitions\Contracts\FilterDefinitionContract;
      
      abstract class OrderFilterDefinition extends FilterDefinitionContract
      {
          public function getIcon(): string
          {
              return 'shopping_cart';
          }
      
          public function getTooltip(): string
          {
              return 'Order related filter';
          }
      }
  2. Create the Filter Class

    • Extend the OrderFilterDefinition class.

    • Define all the required abstract methods.

      <?php
      
      namespace Plenty\Modules\Flow\Filters\Definitions\Models\OrderFilter;
      
      use Plenty\Modules\Flow\Contracts\UIConfigFormContract;
      use Plenty\Modules\Flow\DataModels\ConfigForm\CheckboxGroupField;
      use Plenty\Modules\Flow\Enums\FilterOperators;
      use Plenty\Modules\Flow\StepControls\Definitions\Models\Branch\BranchDefinition;
      use Plenty\Modules\Flow\StepControls\Definitions\Models\Search\SearchDefinition;
      use Plenty\Modules\Flow\Triggers\Objects\FlowTriggerObjectOrder;
      use Plenty\Modules\Flow\DataModels\ConfigForm\SelectboxField;
      use Plenty\Modules\Order\Facades\OrderCache;
      use Plenty\Modules\Order\Status\Contracts\OrderStatusRepositoryContract;
      use Plenty\Modules\Order\Models\Order;
      use Plenty\Modules\Order\Status\Models\OrderStatus;
      
      class StatusFilter extends OrderFilterDefinition
      {
          const IDENTIFIER = 'filterOrderStatus';
          const KEY = 'statusId';
      
          public function getIdentifier(): string
          {
              return self::IDENTIFIER;
          }
      
          public function getName(): string
          {
              return 'Order Status Filter';
          }
      
          public function getDescription(): string
          {
              return 'Filter orders by status';
          }
      
          public function getOperators(): array
          {
              return [
                  FilterOperators::IN,
                  FilterOperators::NOT_IN,
                  FilterOperators::LESS_THAN,
                  FilterOperators::LESS_OR_EQUAL,
                  FilterOperators::GREATER_THAN,
                  FilterOperators::GREATER_OR_EQUAL,
              ];
          }
      
          public function getAvailabilities(): array
          {
              return [
                  BranchDefinition::IDENTIFIER,
                  SearchDefinition::IDENTIFIER. '.' .SearchDefinition::ORDER_SEARCH
              ];
          }
      
          public function addOperators(UIConfigFormContract $configForm, string $key = 'key'): UIConfigFormContract
          {
              $operatorSelectBox = $this->getFormField(SelectboxField::class, [
                  'name' => 'operator',
                  'label' => 'config.operator'
              ]);
              $operatorSelectBox->effectedFields = ['statusId'];
      
              foreach ($this->getOperators() as $operator) {
                  $operatorSelectBox->addSelectboxValue($this->translator->get('module_flow::factories/filters/filterOperator.' . $operator), $operator, false);
              }
              $configForm->addSelectboxField($operatorSelectBox, $key);
              return $configForm;
          }
      
          public function getUIConfigFields(): array
          {
              $configForm = app(UIConfigFormContract::class, [
                  'translationNamespace' => 'module_flow'
              ]);
              $configForm = $this->addOperators($configForm, self::KEY);
      
              $lang = \App::getLocale() ?? config('defaultSystemLanguage');
              $orderStatusRepository = app(OrderStatusRepositoryContract::class);
              $statuses = $orderStatusRepository->all();
      
              $orderStatusSelectBox = $this->getFormField(SelectboxField::class, [
                  'name' => self::KEY,
                  'label' => 'config.orderStatuses'
              ]);
      
              $orderStatusCheckBoxGroup = $this->getFormField(CheckboxGroupField::class, [
                  'name' => self::KEY,
                  'label' => 'config.orderStatuses'
              ]);
      
              foreach ($statuses as $status) {
                  $orderStatusSelectBox->addSelectboxValue($status->names[$lang] ?? (string)array_first($status->names) ?? (string)$status->statusId, $status->statusId, false);
                  $orderStatusCheckBoxGroup->addCheckBoxValue($status->names[$lang] ?? (string)array_first($status->names) ?? (string)$status->statusId, $status->statusId, false);
              }
              $orderStatusSelectBox->condition = 'operator != "IN" && operator != "NIN"';
              $orderStatusCheckBoxGroup->condition = 'operator == "IN" || operator == "NIN"';
      
              $configForm->addSelectboxField($orderStatusSelectBox, self::KEY);
              $configForm->addCheckboxGroupField($orderStatusCheckBoxGroup, self::KEY);
      
              return $configForm->getConfigFields();
          }
      
          public function performFilter(array $inputs, array $filterField, array $extraParams = []): bool
          {
              $this->validateInputs($inputs);
              $filterField = $this->mapFilterFields($filterField);
      
              $orderId = (int)$inputs[FlowTriggerObjectOrder::IDENTIFIER]->value;
              $order = OrderCache::getOrderById($orderId, [], [], true);
      
              $operator = $filterField[self::KEY]['operator'];
              return match ($operator) {
                  FilterOperators::LESS_THAN => $order->statusId < $filterField[self::KEY]['value'],
                  FilterOperators::LESS_OR_EQUAL => $order->statusId <= $filterField[self::KEY]['value'],
                  FilterOperators::GREATER_THAN => $order->statusId > $filterField[self::KEY]['value'],
                  FilterOperators::GREATER_OR_EQUAL => $order->statusId >= $filterField[self::KEY]['value'],
                  FilterOperators::IN => in_array($order->statusId, $filterField[self::KEY]['value']),
                  FilterOperators::NOT_IN => !in_array($order->statusId, $filterField[self::KEY]['value']),
                  default => false,
              };
          }
      
          public function getRequiredInputTypes(): array
          {
              return [FlowTriggerObjectOrder::IDENTIFIER];
          }
      
          public function searchCriteria(array $field = []): ?string
          {
              return 'statusId';
          }
      }
  3. Register the Filter

    • Use the FilterRegistrationService to register your filter.

    • Ensure that the registration method is called during the application bootstrapping process.

      // In your service provider or bootstrap file
      /** @var FilterRegistrationService $registrationService */
      $registrationService = app(FilterRegistrationService::class);
      $registrationService->registerFilter(app(Plenty\Modules\Flow\Filters\StatusFilter::class));

Registering Actions to Flow

To register a new action to the flow, you need to implement the StepActionDefinitionContract abstract class and use the StepActionRegistrationService to register your action.

Deprecation (optional)

Actions can be marked as deprecated by providing a deprecation date. Flow will automatically derive a deprecation level from this date.

  • The deprecation date must be a date string in the format YYYY-MM-DD.

  • If the configured date is in the future, the action is considered in transition and will be shown with a deprecation tag in the UI.

  • If the configured date is reached or exceeded, the action is considered deprecated.

Behavior for deprecated actions:

  • Deprecated actions are shown with a red deprecation tag in the UI.

  • Flows containing deprecated actions cannot be activated.

  • Deprecated actions cannot be added to a flow anymore.

  • During execution, deprecated actions are skipped (not executed).

To configure a deprecation date, override getDeprecationDate() in your action definition:

public function getDeprecationDate(): ?string
{
    // Date string (YYYY-MM-DD)
    return '2026-12-31';
}

Step-by-Step Guide

  1. Create a New Action Class

    • Implement the StepActionDefinitionContract abstract class.

    • Define all the required abstract methods.

      <?php
      
      namespace YourNamespace;
      
      use Plenty\Modules\Flow\StepActions\Definitions\Contracts\StepActionDefinitionContract;
      use Plenty\Modules\Flow\Models\Filter;
      use Plenty\Modules\Flow\Models\Input;
      use Plenty\Modules\Flow\Models\Output;
      use Exception;
      
      class YourCustomAction extends StepActionDefinitionContract
      {
          public function getIdentifier(): string
          {
              return 'your_custom_action'; // Unique identifier for your action
          }
      
          public function getPath(): string
          {
              return 'path/to/your/action'; // Path to your action eg. 'order'
          }
      
          public function getPathIcon(): string
          {
              return 'path/to/your/icon'; // Path to your icon eg. 'shopping_cart'
          }
      
          public function getName(): string
          {
              return 'Your Custom Action'; // Display name of your action
          }
      
          public function getIcon(): string
          {
              return 'icon-name'; // Icon name for your action eg. 'shopping_cart'
          }
      
          public function getDescription(): string
          {
              return 'Description of your custom action'; // Description of your action
          }
      
          public function getUIConfigFields(): array
          {
              return []; // Return an array of FormField objects
          }
      
          public function getRequiredInputTypes(): array
          {
              return []; // Return an array of required input types eg. FlowTriggerObjectOrder::IDENTIFIER
          }
      
          public function getProvidedOutputTypes(): array
          {
              return []; // Return an array of output types eg. FlowTriggerObjectOrder::IDENTIFIER
          }
      
          public function getTooltip(): string
          {
              return 'Tooltip for your custom action'; // Tooltip for your action
          }
      
          public function getDeprecationDate(): ?string
          {
              // Optional: Date string (YYYY-MM-DD)
              return null;
          }
      
          public function performTask(array $inputs, array $configFields, Filter $filter = null, array $extraParams = []): array
          {
              return []; // Perform action logic here
          }
      }
  2. Register the Action

    • Use the StepActionRegistrationService to register your action.

    • Ensure that the registration method is called during the application bootstrapping process.

      // In your service provider or bootstrap file
      /** @var StepActionRegistrationService $registrationService */
      $registrationService = app(StepActionRegistrationService::class);
      $registrationService->registerAction(app(YourNamespace\YourActionRegistrar::class));

Example: OrderActionDefinition

  1. Create the Base Action Class

    • Implement the StepActionDefinitionContract abstract class.

    • Define common methods that will be shared by multiple actions.

      <?php
      
      namespace Plenty\Modules\Flow\StepActions\Definitions\Models\OrderAction;
      
      use Plenty\Modules\Flow\StepActions\Definitions\Contracts\StepActionDefinitionContract;
      use Plenty\Modules\Flow\Triggers\Definitions\Models\OrderCreatedTrigger;
      use Plenty\Modules\Flow\Triggers\Objects\FlowTriggerObjectOrder;
      
      abstract class OrderActionDefinition extends StepActionDefinitionContract
      {
          public function getRequiredInputTypes(): array
          {
              return [ FlowTriggerObjectOrder::IDENTIFIER ];
          }
      
          public function getProvidedOutputTypes(): array
          {
              return [ FlowTriggerObjectOrder::IDENTIFIER ];
          }
      
          public function getPath(): string
          {
              return $this->translator->get('module_flow::factories/stepActions/' . static::IDENTIFIER . '.path');
          }
      
          public function getName(): string
          {
              return $this->translator->get('module_flow::factories/stepActions/' . static::IDENTIFIER . '.name');
          }
      
          public function getDescription(): string
          {
              return $this->translator->get('module_flow::factories/stepActions/' . static::IDENTIFIER . '.description');
          }
      
          public function getIdentifier(): string
          {
              return 'plentymarkets/order::' . static::IDENTIFIER;
          }
      
          public function getIcon(): string
          {
              return 'shopping_cart';
          }
      
          public function getPathIcon(): string
          {
              return 'shopping_cart';
          }
      
          public function getTooltip(): string
          {
              $translation = $this->translator->get('module_flow::factories/stepActions/' . static::IDENTIFIER . '.tooltip');
              return $translation != 'module_flow::factories/stepActions/' . static::IDENTIFIER . '.tooltip'
                  ? $translation
                  :   "";
          }
      }
  2. Create the Action Class

    • Extend the OrderActionDefinition class.

    • Define all the required abstract methods, taking into account that inputs can be a list of objects of different types.

      <?php
      
      namespace Plenty\Modules\Flow\StepActions\Definitions\Models\OrderAction;
      
      use Plenty\Modules\Flow\Contracts\UIConfigFormContract;
      use Plenty\Modules\Flow\Helper\FlowNewRelicLogHelper;
      use Plenty\Modules\Flow\Models\Filter;
      use Plenty\Modules\Flow\Models\Output;
      use Plenty\Modules\Flow\Triggers\Objects\FlowTriggerObjectOrder;
      use Plenty\Modules\Flow\DataModels\ConfigForm\CheckboxField;
      use Plenty\Modules\Flow\DataModels\ConfigForm\SelectboxField;
      use Plenty\Modules\Order\Contracts\OrderSwitchRepositoryContract;
      use Plenty\Modules\Order\Facades\OrderCache;
      use Plenty\Modules\Order\Models\OrderReference;
      use Plenty\Modules\Order\Status\Contracts\OrderStatusRepositoryContract;
      
      class ChangeOrderStatusDefinition extends OrderActionDefinition
      {
          const IDENTIFIER = 'changeOrderStatus';
          const ORDER_FAMILY_LEVEL = 'order';
          const PARENT_ORDER_FAMILY_LEVEL = 'parentOrder';
      
          public function getUIConfigFields(): array
          {
              $configForm = app(UIConfigFormContract::class, [
                  'translationNamespace' => 'module_flow'
              ]);
      
              $orderFamilyLevelSelectBox = $this->getFormField(SelectboxField::class, [
                  'name' => 'orderFamilyLevel',
                  'label' => 'config.orderFamilyLevel'
              ]);
              $orderFamilyLevelSelectBox->effectedFields = ['ignoreDeliveries'];
      
              foreach ([self::ORDER_FAMILY_LEVEL, self::PARENT_ORDER_FAMILY_LEVEL] as $orderFamilyLevel) {
                  $orderFamilyLevelSelectBox->addSelectboxValue($this->translator->get('module_flow::factories/stepActions/changeOrderStatus.' . $orderFamilyLevel), $orderFamilyLevel, false);
              }
      
              $orderStatusesSelectBox = $this->getFormField(SelectboxField::class, [
                  'name' => 'statusId',
                  'label' => 'config.orderStatus'
              ]);
      
              $ignoreDeliveries = $this->getFormField(CheckboxField::class, [
                  'name' => 'ignoreDeliveries',
                  'label' => 'config.ignoreDeliveries',
                  'value' => '1'
              ]);
              $ignoreDeliveries->isVisible = false;
              $ignoreDeliveries->condition = 'orderFamilyLevel == "order"';
      
              $lang = \App::getLocale() ?? config('defaultSystemLanguage');
      
              $orderStatusRepository = app(OrderStatusRepositoryContract::class);
              $statuses = $orderStatusRepository->all();
      
              foreach ($statuses as $status) {
                  $orderStatusesSelectBox->addSelectboxValue($status->names[$lang] ?? (string)array_first($status->names) ?? (string)$status->statusId, $status->statusId, false);
              }
      
              $configForm->addSelectboxField($orderFamilyLevelSelectBox);
              $configForm->addSelectboxField($orderStatusesSelectBox);
              $configForm->addCheckboxField($ignoreDeliveries);
      
              return $configForm->getConfigFields();
          }
      
          public function performTask(array $inputs, array $configFields, Filter $filter = null, array $extraParams = []): array
          {
              $this->validateInputs($inputs);
              $this->validateConfigFields($configFields);
      
              $orderRepository = app(OrderSwitchRepositoryContract::class);
      
              $outputs = [];
              foreach ($inputs[FlowTriggerObjectOrder::IDENTIFIER] as $input) {
                  $orderId = (int)$input->value;
      
                  try {
                      $orderFamilyLevel = $configFields['orderFamilyLevel']->value;
      
                      if ($orderFamilyLevel == self::PARENT_ORDER_FAMILY_LEVEL) {
                          $order = OrderCache::getOrderById($orderId);
                          $parentOrderReference = $order->orderReferences->where('referenceType', OrderReference::REFERENCE_TYPE_PARENT)->first();
                          if ($parentOrderReference !== null) {
                              $orderRepository->update($parentOrderReference->referenceOrderId, [
                                  'statusId' => (float)$configFields['statusId']->value,
                                  'metaData' => [
                                      'statusHistoryMessage' => 'plentyFlow ('.$extraParams['workflowName'].')'
                                  ]
                              ]);
                          }
                      } else {
                          $updateData = [
                              'statusId' => (float)$configFields['statusId']->value,
                              'metaData' => [
                                  'updateStatusWithDeliveryOrderLogic' => !((int)$configFields['ignoreDeliveries']->value),
                                  'statusHistoryMessage' => 'plentyFlow ('.$extraParams['workflowName'].')'
                              ]
                          ];
                          $orderRepository->update($orderId, $updateData);
                      }
                  } catch (\Throwable $e) {
                      FlowNewRelicLogHelper::writeException($extraParams['workflowName'], $e);
                  }
      
                  $outputs[] = app(Output::class, [
                      'name' => FlowTriggerObjectOrder::IDENTIFIER,
                      'value' => $input->value
                  ]);
              }
      
              return $outputs;
          }
      }
  3. Register the Action

    • Use the StepActionRegistrationService to register your action.

    • Ensure that the registration method is called during the application bootstrapping process.

      // In your service provider or bootstrap file
      /** @var TriggerRegistrationService $registrationService */
      $registrationService = app(TriggerRegistrationService::class);
      $registrationService->registerTrigger(app(Plenty\Modules\Flow\StepActions\ChangeOrderStatusRegistrar::class));

Summary

By following these steps, you can create and register a custom action in flow. Ensure that your custom action class implements all the required methods from StepActionDefinitionContract and is registered using the StepActionRegistrationService. The ChangeOrderStatusDefinition example demonstrates handling a list of objects of different types as inputs. The OrderActionDefinition class helps to avoid code duplication by providing common functionality for multiple actions.