Defining Node¶
Define a Plugin¶
Create a FlowDropNodeProcessor plugin in your module.
<?php
namespace Drupal\my_module\Plugin\FlowDropNodeProcessor;
use Drupal\flowdrop\Attribute\FlowDropNodeProcessor;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\flowdrop\Plugin\FlowDropNodeProcessor\AbstractFlowDropNodeProcessor;
use Drupal\flowdrop\DTO\ParameterBagInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
#[FlowDropNodeProcessor(
id: "your_plugin_id",
label: new TranslatableMarkup("Your Plugin Label"),
type: "default",
supportedTypes: ["default"],
category: "your_category",
description: "Brief description of what this plugin does",
version: "1.0.0",
tags: ["tag1", "tag2", "tag3"]
)]
class YourPlugin extends AbstractFlowDropNodeProcessor {
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
protected LoggerChannelInterface $logger,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
public static function create(
ContainerInterface $container,
array $configuration,
$plugin_id,
$plugin_definition
): static {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get("logger.channel.flowdrop"),
);
}
protected function getLogger(): LoggerChannelInterface {
return $this->logger;
}
public function getParameterSchema(): array {
return [
"type" => "object",
"properties" => [
"input_data" => [
"type" => "mixed",
"description" => "Data input from workflow",
],
"option" => [
"type" => "string",
"description" => "Configuration option",
"default" => "default_value",
"enum" => ["option1", "option2", "option3"],
],
],
];
}
protected function process(ParameterBagInterface $params): array {
$inputData = $params->get("input_data");
$option = $params->getString("option", "default_value");
// Your processing logic here...
return ["result" => $inputData];
}
}
Required Parameters¶
| Parameter | Example | Description |
|---|---|---|
id |
"custom_processor" |
Unique identifier (lowercase, underscores) |
label |
new TranslatableMarkup("Custom Processor") |
Human-readable name |
type |
"default" |
Node type for frontend (usually "default") |
supportedTypes |
["default"] |
All supported node types |
category |
"processing" |
Component category |
Example
Input Component¶
#[FlowDropNodeProcessor(
id: "custom_input",
label: new TranslatableMarkup("Custom Input"),
type: "default",
supportedTypes: ["default"],
category: "inputs",
description: "Custom data input component",
tags: ["input", "custom", "data"]
)]
class CustomInput extends AbstractFlowDropNodeProcessor {
public function getParameterSchema(): array {
return [
"type" => "object",
"properties" => [
"value" => [
"type" => "string",
"description" => "The input value",
"default" => "",
],
],
];
}
protected function process(ParameterBagInterface $params): array {
return ["text" => $params->getString("value")];
}
}
AI Model Component¶
#[FlowDropNodeProcessor(
id: "custom_ai_model",
label: new TranslatableMarkup("Custom AI Model"),
type: "default",
supportedTypes: ["default"],
category: "models",
description: "Custom AI model integration",
version: "1.2.0",
tags: ["ai", "model", "custom", "integration"]
)]
class CustomAiModel extends AbstractFlowDropNodeProcessor {
public function getParameterSchema(): array {
return [
"type" => "object",
"properties" => [
"prompt" => [
"type" => "string",
"description" => "The input prompt",
],
"model" => [
"type" => "string",
"description" => "Model identifier",
"default" => "gpt-4",
"enum" => ["gpt-4", "gpt-3.5-turbo"],
],
],
];
}
protected function process(ParameterBagInterface $params): array {
$prompt = $params->getString("prompt");
$model = $params->getString("model", "gpt-4");
return ["response" => "AI response here"];
}
}
Processing Component¶
#[FlowDropNodeProcessor(
id: "data_transformer",
label: new TranslatableMarkup("Data Transformer"),
type: "default",
supportedTypes: ["default"],
category: "processing",
description: "Transforms data between formats",
tags: ["data", "transform", "processing"]
)]
class DataTransformer extends AbstractFlowDropNodeProcessor {
public function getParameterSchema(): array {
return [
"type" => "object",
"properties" => [
"data" => [
"type" => "mixed",
"description" => "Data to transform",
],
"format" => [
"type" => "string",
"description" => "Output format",
"default" => "json",
"enum" => ["json", "xml", "csv"],
],
],
];
}
protected function process(ParameterBagInterface $params): array {
$data = $params->get("data");
$format = $params->getString("format", "json");
return ["transformed" => $data];
}
}
Tool Component¶
#[FlowDropNodeProcessor(
id: "custom_api",
label: new TranslatableMarkup("Custom API"),
type: "default",
supportedTypes: ["default"],
category: "tools",
description: "Custom API integration",
tags: ["api", "http", "integration", "custom"]
)]
class CustomApi extends AbstractFlowDropNodeProcessor {
public function getParameterSchema(): array {
return [
"type" => "object",
"properties" => [
"endpoint" => [
"type" => "string",
"description" => "API endpoint URL",
"format" => "uri",
],
"method" => [
"type" => "string",
"description" => "HTTP method",
"default" => "GET",
"enum" => ["GET", "POST", "PUT", "DELETE"],
],
],
];
}
protected function process(ParameterBagInterface $params): array {
$endpoint = $params->getString("endpoint");
$method = $params->getString("method", "GET");
return ["response" => []];
}
}
Creating Plugin Instance¶
Use the plugin.manager.flowdrop_node_processor service to create plugin instances:
<?php
// Get service and create instance
$plugin_manager = \Drupal::service("plugin.manager.flowdrop_node_processor");
$plugin = $plugin_manager->createInstance("text_input", ["defaultValue" => "Hello"]);
// Available methods
$definitions = $plugin_manager->getDefinitions(); // Get all plugins
$definition = $plugin_manager->getDefinition("plugin_id"); // Get specific plugin
$exists = $plugin_manager->hasDefinition("plugin_id"); // Check if exists
Example
use Drupal\flowdrop\DTO\ParameterBag;
// In a service or controller
$plugin = $this->pluginManager->createInstance("chat_model");
// Create parameter bag with resolved values
$params = new ParameterBag([
"prompt" => "Hello, how can I help you?",
"model" => "gpt-4",
"temperature" => 0.7,
]);
$output = $plugin->execute($params);
Entity Configuration¶
The config entity controls which parameters are input ports vs config fields:
# flowdrop_node_type.flowdrop_node_type.your_plugin.yml
id: your_plugin
label: 'Your Plugin'
executor_plugin: your_plugin_id
parameters:
input_data:
connectable: true # Shows as input port
configurable: false # Not in config form
required: true # Must have value
option:
connectable: false # Config only
configurable: true # Shows in config form
required: false # Optional
System Defaults¶
When entity config doesn't specify a flag:
| Flag | Default | Meaning |
|---|---|---|
connectable |
FALSE |
Not an input port |
configurable |
FALSE |
Not shown in config form |
required |
FALSE |
Optional parameter |
ParameterBag Methods¶
Use typed accessors to retrieve parameter values:
protected function process(ParameterBagInterface $params): array {
// Get any value
$data = $params->get("data");
// Typed accessors with defaults
$name = $params->getString("name", "default");
$count = $params->getInt("count", 0);
$rate = $params->getFloat("rate", 0.5);
$enabled = $params->getBool("enabled", FALSE);
$items = $params->getArray("items", []);
// Check if parameter exists
if ($params->has("optional_param")) {
// ...
}
return ["result" => $data];
}
Supported JSON Schema Properties¶
| Property | Description |
|---|---|
type |
string, number, integer, boolean, array, object, mixed |
description |
Human-readable description |
default |
Default value |
enum |
Array of allowed values |
minimum, maximum |
Numeric constraints |
minLength, maxLength |
String length constraints |
pattern |
Regex pattern for strings |
format |
email, uri, date, date-time, uuid, etc. |
Note
For more detailed usage you can check out Flowdrop Node Processor.