Let's say you're building a plugin and, want to support flow builder by creating some default flows which would appear on the flow list after installing your plugin.
Inserting your custom flow by migration is the only way to do it, let's assume I need a default flow that looks like this:
Let's find it out by following the steps below:
Creating a migration by using the command below:
bin/console database:create-migration -p YourPluginName --name AddLoveNewOrdersFlow
A new migration is generated, it should be placed in src/Migration
folder
<pluginRoot>
└── src
└── Migration
└── Migration1654243773AddLoveNewOrdersFlow.php
This is the file content
<?php declare(strict_types=1);
namespace Example\Migration;
use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\Migration\MigrationStep;
class Migration1654243773AddLoveNewOrdersFlow extends MigrationStep
{
public function getCreationTimestamp(): int
{
return 1654243773;
}
public function update(Connection $connection): void
{
// implement update
}
public function updateDestructive(Connection $connection): void
{
// implement update destructive
}
}
Great, let's go to the next step.
Configuring the migration
Data structure
There are two tables that will be responsible for storing data of flow (flow
and flow_sequence
). The relation is 1 flow
to n flow_sequence
Flow
Type | Field name | Description |
BINARY(16) | id | Flow id |
VARCHAR(255) | name | Flow name |
MEDIUMTEXT | description | Flow description |
VARCHAR(255) | event_name | Flow event name, this is the list events and actions |
INT(11) | priority | The priority is the execution order of flows with the same event_name |
LONGBLOB | payload | The hierarchy tree of the flow, it will be built automatically after inserting or updating the flow |
TINYINT(1) | invalid | To show the payload is correct or not |
TINYINT(1) | active | Enable or disable the flow |
Flow Sequences
Type | Field name | References | Description |
BINARY(16) | id | Sequence id | |
BINARY(16) | flow_id | flow .id | Flow id |
BINARY(16) | parent_id | flow_sequence .id | Sequence id of parent sequence |
BINARY(16) | rule_id | rule .id | Rule id for IF condition |
VARCHAR(255) | action_name | Action name, this is the list events and actions | |
JSON | config | The config data of the action sequence | |
INT(11) | position | Display position of sequences that have the same parent_id | |
INT(11) | display_group | To determine which group this sequence should be displayed in | |
TINYINT(1) | true_case | To determine this sequence belongs to true case or false case of the parent sequence |
Data mapping
Write to the update()
function
Firstly, we will insert a record to flow
table
public function update(Connection $connection): void
{
$flowId = Uuid::randomBytes();
// insert flow
$connection->insert(
FlowDefinition::ENTITY_NAME,
[
'id' => $flowId,
'name' => 'Love new orders',
// check the list of event and reference actions on the link below
// https://developer.shopware.com/docs/resources/references/core-reference/flow-reference
'event_name' => 'checkout.order.placed',
'description' => 'Love new orders',
'priority' => 100,
'active' => true,
'created_at' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
]
);
}
Then insert the list of sequences with flowId above
// insert flow sequences
/* ----- Start insert sequences have display_group = 1 -------*/
$sequenceConditionId = Uuid::randomBytes();
$connection->insert(
FlowSequenceDefinition::ENTITY_NAME,
[
'id' => $sequenceConditionId,
'flow_id' => $flowId,
'parent_id' => null,
// let's say you already have the rule id for this
// the sequence has rule_id is not null and action_name is null means it is an If condition
'rule_id' => Uuid::fromHexToBytes('b482bf23d5c74df0bf40723181713aa2'),
'position' => 1,
'display_group' => 1,
'created_at' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
]
);
// the condition's true case sequences
$connection->insert(
FlowSequenceDefinition::ENTITY_NAME,
[
'id' => Uuid::randomBytes(),
'flow_id' => $flowId,
'parent_id' => $sequenceConditionId,
// the sequence has action_name is not null and rule_id is null means it is an Action
// check the list of event and reference actions on the link below
// https://developer.shopware.com/docs/resources/references/core-reference/flow-reference
'action_name' => 'action.add.order.tag',
'config' => \json_encode([
'entity' => 'order',
'tagIds' => ['4b95ee7df46c429fa629f29e485cd626' => 'Love it'],
]),
'position' => 1,
'display_group' => 1,
'true_case' => 1, // true
'created_at' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
]
);
$connection->insert(
FlowSequenceDefinition::ENTITY_NAME,
[
'id' => Uuid::randomBytes(),
'flow_id' => $flowId,
'parent_id' => $sequenceConditionId,
// the sequence has action_name is not null and rule_id is null means it is an Action
// check the list of event and reference actions on the link below
// https://developer.shopware.com/docs/resources/references/core-reference/flow-reference
'action_name' => 'action.generate.document',
'config' => \json_encode([
'documentTypes' => [
[
'documentType' => 'delivery_note',
'documentRangerType' => 'document_delivery_note',
],
],
]),
'position' => 2,
'display_group' => 1,
'true_case' => 1, // true
'created_at' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
]
);
// the condition's false case sequences
$connection->insert(
FlowSequenceDefinition::ENTITY_NAME,
[
'id' => Uuid::randomBytes(),
'flow_id' => $flowId,
'parent_id' => $sequenceConditionId,
// the sequence has action_name is not null and rule_id is null means it is an Action
// check the list of event and reference actions on the link below
// https://developer.shopware.com/docs/resources/references/core-reference/flow-reference
'action_name' => 'action.add.order.tag',
'config' => \json_encode([
'entity' => 'order',
'tagIds' => ['084dacaa7f3347e9aa4722e9fd5ee6d5' => 'Still love it'],
]),
'position' => 1,
'display_group' => 1,
'true_case' => 0, // false
'created_at' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
]
);
/* ----- End insert sequences have display_group = 1 -------*/
/* ----- Start insert sequences have display_group = 2 -------*/
$connection->insert(
FlowSequenceDefinition::ENTITY_NAME,
[
'id' => Uuid::randomBytes(),
'flow_id' => $flowId,
'parent_id' => null,
// the sequence has action_name is not null and rule_id is null means it is an Action
// check the list of event and reference actions on the link below
// https://developer.shopware.com/docs/resources/references/core-reference/flow-reference
'action_name' => 'action.generate.document',
'config' => \json_encode([
'documentTypes' => [
[
'documentType' => 'invoice',
'documentRangerType' => 'document_invoice',
],
],
]),
'position' => 1,
'display_group' => 2,
'created_at' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
]
);
/* ----- End insert sequences have display_group = 2 -------*/
Last but not least, we need to register the index for flow.indexer
or run bin/console dal:refresh:index
directly after migrating new migrations to re-index all flows, this will help to build flow.payload
(flows only works correctly when its payload is built )
public function update(Connection $connection): void
{
...
$this->registerIndexer($connection, 'flow.indexer');
}
Your implementation is done, migrate your migration and check the result in Administration -> Settings -> Flow Builder
bin/console database:migrate YourPluginName --all
Small tip
You can create your expected flow manually and then create your migration according to its API payload :D