Adding a Custom DataField Type
A custom DataField type is needed when creating DataFields for a new line item type (e.g. a third-party plugin with custom cart positions), or when introducing a semantically new category of fields.
Overview of steps
- Create the type enum
- Create the abstract base class for the new field
- Implement the concrete DataField
- Create the LineItemContextResolver
- Register the services
1. Create the type enum
The enum implements DataFieldTypeInterface and defines the type name.
<?php
declare(strict_types=1);
namespace MyPlugin\Connector\DataField;
use AgiqonConnector\Connector\DataField\DataFieldTypeInterface;
enum MyCustomDataFieldType: string implements DataFieldTypeInterface
{
case MyCustomType = 'my_custom_type';
public function getName(): string
{
return $this->value;
}
}
2. Create the abstract base class
The abstract class carries the #[DataFieldTypeAttribute(...)] for the new type and encapsulates the null check on the line item.
<?php
declare(strict_types=1);
namespace MyPlugin\Connector\DataField\Field;
use AgiqonConnector\Connector\DataField\Attribute\DataFieldTypeAttribute;
use AgiqonConnector\Connector\DataField\DataFieldResolutionContext;
use AgiqonConnector\Connector\DataField\Field\AbstractDataField;
use MyPlugin\Connector\DataField\MyCustomDataFieldType;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
#[DataFieldTypeAttribute(MyCustomDataFieldType::MyCustomType)]
abstract readonly class AbstractMyCustomField extends AbstractDataField
{
public function resolve(DataFieldResolutionContext $context): ?string
{
$lineItem = $context->getLineItem();
// Check the line item and optionally filter by type
if ($lineItem === null || $lineItem->getType() !== 'my-custom-line-item-type') {
return null;
}
return $this->getValue($lineItem);
}
abstract public function getValue(LineItem $lineItem): ?string;
}
3. Implement the concrete DataField
<?php
declare(strict_types=1);
namespace MyPlugin\Connector\DataField\Field;
use AgiqonConnector\Connector\System\ConnectorSystemType;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
readonly class MyCustomField extends AbstractMyCustomField
{
public function __construct()
{
parent::__construct(
'myCustomField',
[ConnectorSystemType::OCI, ConnectorSystemType::cXML]
);
}
public function getValue(LineItem $lineItem): ?string
{
// Custom logic to retrieve the value from the line item
$payload = $lineItem->getPayload();
return $payload['my_custom_value'] ?? null;
}
}
4. Create the LineItemContextResolver
The resolver determines via supports() whether it is responsible for a given line item.
<?php
declare(strict_types=1);
namespace MyPlugin\Connector\DataField\LineItemContextResolver;
use AgiqonConnector\Connector\DataField\LineItemContextResolver\AbstractLineItemContextResolver;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
class MyCustomContextResolver extends AbstractLineItemContextResolver
{
public function supports(LineItem $lineItem): bool
{
return $lineItem->getType() === 'my-custom-line-item-type';
}
}
5. Register the services
Register both services in your plugin's services.xml with the appropriate tags:
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<!-- The new DataField -->
<service id="MyPlugin\Connector\DataField\Field\MyCustomField">
<tag name="agiqon_connector.data_field"/>
</service>
<!-- The matching LineItemContextResolver -->
<service id="MyPlugin\Connector\DataField\LineItemContextResolver\MyCustomContextResolver">
<tag name="agiqon_connector.line_item_context_resolver"/>
</service>
</services>
</container>
Result
After registration:
myCustomFieldis selectable in the connector configuration under the new typemy_custom_type.- For cart positions of type
my-custom-line-item-type,MyCustomContextResolveris used automatically. - All other positions are handled by the built-in resolvers.