Utilizing custom fields in Shopware 6
When developing a new plugin in Shopware 6, you’ll often need to incorporate custom fields. These fields have a variety of functions, which you can explore in detail on the Shopware documentation page. Essentially, Shopware’s custom fields allow you to store additional data on various entities. This data is stored in the database in JSON format and is accessed as an array. In this article, we’ll focus on the correct implementation of custom fields within a project. The content is geared towards developers who have already gained some experience with plugin creation.
Common mistakes to avoid
- Neglecting to remove unused custom fields – A frequent mistake occurs when a custom field definition is added during the plugin installation but isn’t removed when no longer needed. This oversight can lead to errors during plugin reinstallation, as the system will indicate that the definition already exists.
- Overloading the main plugin file with data – Another challenge arises when there is a need to add a substantial amount of data. If multiple definitions are required, the main plugin installation file can become excessively lengthy, especially if the plugin includes numerous functionalities. In such cases, it’s advisable to move the installation of custom fields to a separate class or even multiple classes if there are many custom fields to manage.
Below is an example of how the main plugin file might look.
<?php declare(strict_types=1);
namespace Example;
use Example\Service\OrderCustomFields;
use Shopware\Core\Framework\Plugin;
use Shopware\Core\Framework\Plugin\Context\ActivateContext;
use Shopware\Core\Framework\Plugin\Context\DeactivateContext;
use Shopware\Core\Framework\Plugin\Context\InstallContext;
use Shopware\Core\Framework\Plugin\Context\UninstallContext;
class Example extends Plugin
{
public function install(InstallContext $installContext): void
{
(new OrderCustomFields($this--->container))
->installCustomFields($installContext->getContext());
}
public function activate(ActivateContext $activateContext): void
{
(new OrderCustomFields($this->container))
->activateCustomFields($activateContext->getContext());
}
public function deactivate(DeactivateContext $deactivateContext): void
{
(new OrderCustomFields($this->container))
->deactivateCustomFields($deactivateContext->getContext());
}
public function uninstall(UninstallContext $uninstallContext): void
{
(new OrderCustomFields($this->container))
->uninstallCustomFields($uninstallContext->getContext());
}
}
Implementing this approach will make it easier to expand additional fields in the future and prevent the main class of the plugin from becoming overly cluttered with code.
Installing an Order Custom Field
Let’s take a closer look at the OrderCustomField class itself. To streamline the management of custom fields, it’s recommended to define the keys for these fields as constants at the beginning of the class definition.
class OrderCustomFields
{
public const LABEL_CUSTOM_FIELD_KEY = custom_label;
public const ONE_CUSTOM_FIELD_KEY = 'custom_field_one';
public const TWO_CUSTOM_FIELD_KEY = 'custom_field_two';
protected ContainerInterface $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function installCustomFields(Context $context): void
{
$searchResults = $this->getCustomFields($context);
if($searchResults->getTotal() === 0)
{
// Add custom fields
}
}
public function activateCustomFields(Context $context): void
{
$this->setCustomFieldsActivate(
$this->getCustomFields($context)->getIds(), true, $context);
}
public function deactivateCustomFields(Context $context): void
{
$this->setCustomFieldsActivate(
$this->getCustomFields($context)->getIds(), false, $context);
}
public function uninstallCustomFields(Context $context): void
{
$customFieldsIds = $this->getCustomFields($context)->getIds();
if(count($customFieldsIds) !== 0)
{
$this->container->get('custom_field_set.repository')->delete([[
'id' => $customFieldsIds[0]
]], $context);
}
}
private function setCustomFieldsActivate(array $customFieldsIds, bool $active, Context $context): void
{
$this->container->get('custom_field_set.repository')->update([[
'id' => $customFieldsIds[0],
'active' => $active
]], $context);
}
private function getCustomFields(Context $context): IdSearchResult
{
return $this->container->get('custom_field_set.repository')->searchIds(
(new Criteria())->addFilter(new EqualsFilter(
'name', self::LABEL_CUSTOM_FIELD_KEY)), $context);
}
}
Creating Custom Fields
Once you have established the flow for managing custom fields, you can proceed with the actual creation of these fields. When adding custom fields, it’s important to remember that both the custom field set definition (which corresponds to a table in the database named “custom_field_set”) and the field definitions themselves (found in “custom_field” tables) must be included. Any changes made to the set’s status will affect all associated fields.
For instance, a basic structure for creating a field might look like this:
public function installCustomFields(Context $context): void
{
$searchResults = $this->getCustomFields($context);
if($searchResults->getTotal() === 0)
{
$customField = [
'name' => self::LABEL_CUSTOM_FIELD_KEY,
'active' => false,
'config' => [
'label' => [
'en-GB' => 'Label translation'
]
],
'relations' => [
[
'entityName' => 'order'
]
],
'customFields' => [
[
'name' => self::ONE_CUSTOM_FIELD_KEY,
'type' => CustomFieldTypes::TEXT,
'config' => [
'label' => [
'en-GB' => 'Field one translation'
]
]
], [
'name' => self::TWO_CUSTOM_FIELD_KEY,
'type' => CustomFieldTypes::TEXT,
'config' => [
'label' => [
'en-GB' => 'Field two translation'
]
]
]
]
];
$this->container->get('custom_field_set.repository')->create([$customField], $context);
}
}
At this stage, the process could technically be considered complete. However, custom fields are typically added in a single language, the one the customer will use. When developing a plugin for a store, it’s essential to include translations in multiple languages. How can this be done most effectively? The following example demonstrates not only how to separate translations from the code that adds fields but also how to ensure that only translations for languages already present in the Shopware database are added.
The first step is to create a new class that will handle the translations:
class CustomFieldsTranslation
{
public const TRANSLATIONS = [
'en-GB' => [
OrderCustomFields::LABEL_CUSTOM_FIELD_KEY => 'Label translation',
OrderCustomFields::ONE_CUSTOM_FIELD_KEY => 'Field one translation',
OrderCustomFields::TWO_CUSTOM_FIELD_KEY => 'Field two translation'
],
'pl-PL' => [
OrderCustomFields::LABEL_CUSTOM_FIELD_KEY => 'Label translation',
OrderCustomFields::ONE_CUSTOM_FIELD_KEY => 'Field one translation',
OrderCustomFields::TWO_CUSTOM_FIELD_KEY => 'Field two translation'
]
];
}
Returning to the class responsible for creating custom fields, you’ll need to add a function that retrieves the available languages:
private function getAvailableLanguages(Context $context): array
{
return $this->container->get('language.repository')->search(
(new Criteria())->addAssociation('locale'),
$context
)->getElements();
}
Next, include a function that will handle the generation of translations:
private function generateTranslation(string $key, array $languages): array
{
$translation = [];
foreach($languages as $language)
{
$code = $language->getLocale()->getCode();
if(isset(CustomFieldsTranslation::TRANSLATIONS[$code]))
{
$translation['config']['label'][$code] = CustomFieldsTranslation::TRANSLATIONS[$code][$key];
}
}
return $translation;
}
Afterward, make the necessary adjustments to the generator function:
if($searchResults->getTotal() === 0)
{
$languages = $this->getAvailableLanguages($context);
$customField = [
'name' => self::LABEL_CUSTOM_FIELD_KEY,
'active' => false,
'config' => $this->generateTranslation(self::LABEL_CUSTOM_FIELD_KEY, $languages),
'customFields' => [
[
'name' => self::ONE_CUSTOM_FIELD_KEY,
'type' => CustomFieldTypes::TEXT,
'config' => $this->generateTranslation(self::ONE_CUSTOM_FIELD_KEY, $languages)
], [
'name' => self::TWO_CUSTOM_FIELD_KEY,
'type' => CustomFieldTypes::TEXT,
'config' => $this->generateTranslation(self::TWO_CUSTOM_FIELD_KEY, $languages)
]
]
];
$this->container->get('custom_field_set.repository')->create([$customField], $context);
}
This approach results in a robust implementation of custom fields that can be effortlessly expanded to include additional fields or languages as needed.
Categories:Shopware
Tags:


