Introduction
Shopware 6.7 introduced significant enhancements to the Flow Builder system, providing developers with more flexibility and control over automated business processes. One of the most powerful features is the ability to create custom conditions that can be used within flow actions to make decisions based on specific criteria. This technical deep dive will explore how to implement a custom condition in Shopware 6.7's Flow Builder, demonstrating the underlying architecture and providing practical implementation examples.
Understanding Flow Builder in Shopware 6.7
The Flow Builder in Shopware 6.7 represents a major evolution from previous versions, offering a more robust and extensible framework for creating automated workflows. At its core, the Flow Builder system is built around events, conditions, and actions that work together to create business logic automation.
Key Components of Flow Builder
- Events: Triggers that initiate flow execution
- Conditions: Logical checks that determine whether a flow should proceed
- Actions: Operations performed when conditions are met
- Flow Context: The environment in which flows execute
In Shopware 6.7, the condition system has been significantly enhanced to support more complex business logic while maintaining performance and extensibility.
Prerequisites for Custom Condition Development
Before diving into implementation details, ensure you have the following:
- Shopware 6.7+ installed
- Basic understanding of Symfony dependency injection
- Familiarity with Shopware's plugin architecture
- Access to development environment with Composer installed
Creating a Custom Condition Class
The foundation of any custom condition lies in implementing the proper interface and extending the existing condition structure. Let's start by creating our custom condition class:
<?php declare(strict_types=1);
namespace MyPlugin\Flow\Condition;
use Shopware\Core\Framework\Rule\Rule;
use Shopware\Core\Framework\Rule\RuleComparison;
use Shopware\Core\Framework\Rule\RuleContext;
use Symfony\Component\Validator\Constraints\NotBlank;
class CustomerGroupCondition extends Rule
{
protected ?string $customerGroupId = null;
public function __construct(?string $customerGroupId = null)
{
parent::__construct();
$this->customerGroupId = $customerGroupId;
}
public function match(RuleContext $context): bool
{
if (!$context->getCustomer()) {
return false;
}
$customer = $context->getCustomer();
// Check if customer belongs to the specified group
if ($this->customerGroupId && $customer->getGroupId() !== $this->customerGroupId) {
return false;
}
return true;
}
public function getConstraints(): array
{
return [
'customerGroupId' => [new NotBlank()]
];
}
public function getName(): string
{
return 'my_plugin.customer_group';
}
}
Registering the Condition Service
To make our custom condition available within the Flow Builder, we need to register it as a service and tag it appropriately:
# config/services.yaml
services:
MyPlugin\Flow\Condition\CustomerGroupCondition:
tags:
- { name: shopware.rule, type: customer_group_condition }
Implementing the Condition Definition
In Shopware 6.7, conditions must be properly defined to appear in the Flow Builder interface. This involves creating a definition that describes how the condition should be displayed and configured:
<?php declare(strict_types=1);
namespace MyPlugin\Flow\Condition;
use Shopware\Core\Framework\Rule\RuleDefinition;
use Symfony\Component\Validator\Constraints\NotBlank;
class CustomerGroupConditionDefinition extends RuleDefinition
{
public function getLabel(): string
{
return 'Customer Group Condition';
}
public function getDescription(): string
{
return 'Checks if customer belongs to a specific group';
}
public function getParameters(): array
{
return [
'customerGroupId' => [
'type' => 'select',
'label' => 'Customer Group',
'options' => $this->getCustomerGroupOptions(),
'required' => true,
],
];
}
private function getCustomerGroupOptions(): array
{
// Fetch customer groups from database or service
return [
['value' => 'group1', 'label' => 'Premium Customers'],
['value' => 'group2', 'label' => 'Regular Customers'],
['value' => 'group3', 'label' => 'New Customers'],
];
}
}
Advanced Condition Implementation
For more complex scenarios, we might need to implement additional features such as caching, performance optimization, or integration with external services:
<?php declare(strict_types=1);
namespace MyPlugin\Flow\Condition;
use Shopware\Core\Framework\Rule\Rule;
use Shopware\Core\Framework\Rule\RuleContext;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
class AdvancedCustomerCondition extends Rule
{
protected ?string $customerGroupId = null;
protected ?int $minOrderCount = null;
protected ?float $minOrderValue = null;
public function __construct(
?string $customerGroupId = null,
?int $minOrderCount = null,
?float $minOrderValue = null
) {
parent::__construct();
$this->customerGroupId = $customerGroupId;
$this->minOrderCount = $minOrderCount;
$this->minOrderValue = $minOrderValue;
}
public function match(RuleContext $context): bool
{
if (!$context->getCustomer()) {
return false;
}
$customer = $context->getCustomer();
// Validate customer group
if ($this->customerGroupId && $customer->getGroupId() !== $this->customerGroupId) {
return false;
}
// Check order count and value if specified
if ($this->minOrderCount || $this->minOrderValue) {
$orderStats = $this->getCustomerOrderStats($customer->getId());
if ($this->minOrderCount && $orderStats['count'] < $this->minOrderCount) {
return false;
}
if ($this->minOrderValue && $orderStats['total'] < $this->minOrderValue) {
return false;
}
}
return true;
}
private function getCustomerOrderStats(string $customerId): array
{
// This would typically be implemented with proper caching
// to avoid repeated database queries
// Simulated cache lookup
static $cache = [];
if (isset($cache[$customerId])) {
return $cache[$customerId];
}
// Actual implementation would query the database
// and return order statistics for the customer
$stats = [
'count' => 5,
'total' => 1250.00,
];
$cache[$customerId] = $stats;
return $stats;
}
public function getConstraints(): array
{
return [
'customerGroupId' => [new NotBlank()],
'minOrderCount' => ['integer', 'min' => 0],
'minOrderValue' => ['numeric', 'min' => 0.0],
];
}
public function getName(): string
{
return 'my_plugin.advanced_customer_condition';
}
}
Configuration and Validation
Proper validation ensures that custom conditions work reliably in production environments:
<?php declare(strict_types=1);
namespace MyPlugin\Flow\Condition;
use Shopware\Core\Framework\Rule\Rule;
use Shopware\Core\Framework\Rule\RuleContext;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Type;
class ValidatedCustomerGroupCondition extends Rule
{
protected ?string $customerGroupId = null;
protected ?string $operator = '==';
public function __construct(?string $customerGroupId = null, ?string $operator = '==')
{
parent::__construct();
$this->customerGroupId = $customerGroupId;
$this->operator = $operator;
}
public function match(RuleContext $context): bool
{
if (!$context->getCustomer()) {
return false;
}
$customer = $context->getCustomer();
$customerGroup = $customer->getGroupId();
// Perform comparison based on operator
switch ($this->operator) {
case '==':
return $customerGroup === $this->customerGroupId;
case '!=':
return $customerGroup !== $this->customerGroupId;
case 'in':
return in_array($customerGroup, explode(',', $this->customerGroupId), true);
default:
return false;
}
}
public function getConstraints(): array
{
return [
'customerGroupId' => [new NotBlank()],
'operator' => [
new Type('string'),
['in_array' => ['==', '!=', 'in']]
],
];
}
public function getName(): string
{
return 'my_plugin.validated_customer_group_condition';
}
}
Integration with Flow Builder
To ensure our conditions appear correctly in the Flow Builder interface, we need to register them properly:
<?php declare(strict_types=1);
namespace MyPlugin\Flow;
use Shopware\Core\Framework\Plugin;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class MyPlugin extends Plugin
{
public function build(ContainerBuilder $container): void
{
parent::build($container);
// Register flow conditions
$container->addCompilerPass(new FlowConditionCompilerPass());
}
}
Performance Considerations
When implementing custom conditions, performance should be a primary concern:
- Caching: Implement caching strategies for expensive operations
- Database Queries: Minimize database calls within match methods
- Memory Usage: Be mindful of memory consumption in large-scale scenarios
- Indexing: Ensure proper database indexing for condition parameters
Testing Custom Conditions
Comprehensive testing is crucial for custom Flow Builder conditions:
<?php declare(strict_types=1);
namespace MyPlugin\Tests\Flow\Condition;
use PHPUnit\Framework\TestCase;
use Shopware\Core\Framework\Rule\RuleContext;
use Shopware\Core\Framework\Test\TestCaseBase\IntegrationTestBehaviour;
use MyPlugin\Flow\Condition\CustomerGroupCondition;
class CustomerGroupConditionTest extends TestCase
{
use IntegrationTestBehaviour;
public function testMatchWithMatchingCustomer(): void
{
$condition = new CustomerGroupCondition('premium');
$context = $this->createMock(RuleContext::class);
$customer = $this->createMock(Customer::class);
$customer->method('getGroupId')->willReturn('premium');
$context->method('getCustomer')->willReturn($customer);
$result = $condition->match($context);
$this->assertTrue($result);
}
public function testMatchWithNonMatchingCustomer(): void
{
$condition = new CustomerGroupCondition('premium');
$context = $this->createMock(RuleContext::class);
$customer = $this->createMock(Customer::class);
$customer->method('getGroupId')->willReturn('regular');
$context->method('getCustomer')->willReturn($customer);
$result = $condition->match($context);
$this->assertFalse($result);
}
}
Deployment and Best Practices
When deploying custom Flow Builder conditions to production environments:
- Version Compatibility: Ensure compatibility with Shopware 6.7 features
- Backward Compatibility: Consider how changes might affect existing flows
- Documentation: Provide clear documentation for condition usage
- Monitoring: Implement logging and monitoring for condition execution
Conclusion
Adding custom conditions to Shopware 6.7's Flow Builder system opens up tremendous possibilities for creating sophisticated automated business processes. Through proper implementation of the Rule interface, careful attention to performance considerations, and thorough testing, developers can create powerful conditions that enhance the platform's automation capabilities.
The examples provided demonstrate various approaches to condition implementation, from simple group checks to more complex multi-parameter validations. By following these patterns and best practices, you can extend Shopware's Flow Builder functionality to meet your specific business requirements while maintaining performance and reliability.
Remember that custom conditions should be well-documented, thoroughly tested, and optimized for production use. The flexibility of Shopware 6.7's Flow Builder system makes it an excellent foundation for building complex automated workflows that can significantly improve operational efficiency and customer experience.