Last Updated on January 21, 2022

Shopware’s custom field system allows you to extend entities, without writing a complete entity extension.This is possible by storing the additional data in a JSON-Field. Custom fields therefore can only be used to store scalar values. If you’d like to create associations between entities, you’ll need to use an Entity extension.

Supporting custom fields with your entity

For creating custom fields in database we need to extend any entity definition which is already created in shopware.

In order to support custom fields with your custom entity, there are two steps necessary:

  • 1) Add a CustomFields field to your EntityDefinition
  • 2) Add a column custom_fields to your entities’ database table via migration

Also, you may want to add translatable custom fields, which is also covered in very short here.

Add custom field to entity definition

<plugin root>/src/Core/Content/Example/ExampleDefinition.php

use Shopware\Core\Framework\DataAbstractionLayer\Field\CustomFields;                                                                    

[...]
class ExampleDefinition extends EntityDefinition
{
    [...]
    protected function defineFields(): FieldCollection
    {
        return new FieldCollection([
            (new IdField('id', 'id'))->addFlags(new Required(), new PrimaryKey()),
            (new StringField('name', 'name')),
            (new StringField('description', 'description')),
            (new BoolField('active', 'active')),
            new CustomFields()
        ]);
    }
}

Note the new field that was added in the FieldCollection. That’s already it for your custom entity definition. Now go ahead and add the column to the database.

Add column in database table

If you want to support custom fields now, you have to add a new column custom_fields of type JSON to your migration.

<plugin root>/src/Migration/Migration1611664789Example.php
public function update(Connection $connection): void
{
    $sql = <<<SQL
        CREATE TABLE IF NOT EXISTS `swag_example` (
        `id` BINARY(16) NOT NULL,
        `name` VARCHAR(255) COLLATE utf8mb4_unicode_ci,
        `description` VARCHAR(255) COLLATE utf8mb4_unicode_ci,
        `active` TINYINT(1) COLLATE utf8mb4_unicode_ci,

        `custom_fields` json DEFAULT NULL,

        `created_at` DATETIME(3) NOT NULL,
        `updated_at` DATETIME(3),
        PRIMARY KEY (`id`)
        )
        ENGINE = InnoDB
        DEFAULT CHARSET = utf8mb4
        COLLATE = utf8mb4_unicode_ci;
    SQL;
    $connection->executeStatement($sql);
}

Custom field always must be json field and it should default to null, since it doesn’t have to contain values.

Add translatable custom field to entity definition

Make sure to understand entity translations in general first, which is explained here Add data translations. If you want your custom fields to be translatable, you can simply work with a TranslatedField here as well.

<plugin root>/src/Core/Content/Example/ExampleDefinition.php
use Shopware\Core\Framework\DataAbstractionLayer\Field\TranslatedField;                                                               

[...]

class ExampleDefinition extends EntityDefinition
{
    [...]

    protected function defineFields(): FieldCollection
    {
        return new FieldCollection([
            (new IdField('id', 'id'))->addFlags(new Required(), new PrimaryKey()),
            (new StringField('name', 'name')),
            (new StringField('description', 'description')),
            (new BoolField('active', 'active')),

            new TranslatedField('customFields'),
        ]);
    }
}

Just add the TranslatedField and apply customFields as a parameter.
In your translated entity definition, you then add the CustomFields field instead.

<plugin root>/src/Core/Content/Example/Aggregate/ExampleTranslation/ExampleTranslationDefinition.php
use Shopware\Core\Framework\DataAbstractionLayer\Field\CustomFields;                                                                    
[...]
class ExampleTranslationDefinition extends EntityTranslationDefinition
{
    [...]

    protected function defineFields(): FieldCollection
    {
        return new FieldCollection([
            (new StringField('name', 'name'))->addFlags(new Required()),

            new CustomFields()
        ]);
    }
}

Add custom fields to an entity

Defining a custom field set is not necessary it is only necessary, if you want it to be editable in the administration or if you need validation when writing your custom field.

Because of that, we’ll start with filling data to an actual entities’ custom field, before actually defining it.

Filling data into custom fields

$this->swagExampleRepository->upsert([[
    'id' => '<your ID here>',
    'customFields' => ['swag_example_size' => 15]
]], $context);

You can write any valid json you want to write in custom Field. Example:

$this->swagExampleRepository->upsert([[
    'id' => '<your ID here>',
    'customFields' => [ 'foo' => 'bar', 'baz' => [] ]
]], $context);

Adding an actual custom field

You can skip this section if you do not want your new custom field to be editable in the administration.

use Shopware\Core\System\CustomField\CustomFieldTypes;

[...]

$this->customFieldSetRepository->create([
    [
        'name' => 'swag_example_set',
        'config' => [
            'label' => [
                'en-GB' => 'English custom field set label',
                'de-DE' => 'German custom field set label'
            ]
        ],
        'customFields' => [
            [
                'name' => 'swag_example_size',
                'type' => CustomFieldTypes::INT,
                'config' => [
                    'label' => [
                        'en-GB' => 'English custom field label',
                        'de-DE' => 'German custom field label'
                    ],
                    'customFieldPosition' => 1
                ]
            ]
        ]
    ]
], $context);

Here is the example of removing some categories from navigation menu.

I am adding a custom field which will hide menu from sidebar.

custom/plugin/pluginRoot/src/pluginName.php

Have a look on the screenshot:
shopware remove categories 1

Here my plugin name is SidebarNavigation

This class will perform operation on custom_field_set

<?php declare(strict_types=1);

namespace SidebarNavigation;

use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\Plugin;
use Shopware\Core\Framework\Plugin\Context\UninstallContext;
use Shopware\Core\Framework\Plugin\Context\InstallContext;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\Search\IdSearchResult;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use SidebarNavigation\Services\SidebarNavigationCustomFieldSetService;

class SidebarNavigation extends Plugin
{
    public function install(InstallContext $installContext): void
    {
        parent::install($installContext);

        $SidebarNavigationCustomFieldSetService = new SidebarNavigationCustomFieldSetService(
            $this->container->get('custom_field_set.repository')
        );

        /** @var Context $context */
        $context = $installContext->getContext();

        $SidebarNavigationCustomFieldSetService->createCategoryCustomFieldSet($context);
    }

    public function uninstall(UninstallContext $context): void
    {
        parent::uninstall($context);
        if ($context->keepUserData()) {
            return;
        }

        $this->removeCustomFields($context);
    }

    private function removeCustomFields(UninstallContext $uninstallContext)
    {
        $customFieldSetRepository = $this->container->get('custom_field_set.repository');

        /** @var Context $context */
        $context = $uninstallContext->getContext();

        // Delete custom field set

        $this->deleteCustomFieldSet(
            $context,
            $customFieldSetRepository,
            SidebarNavigationCustomFieldSetService::SIDEBAR_CATEGORY_NAVIGATION_CUSTOM_FIELD_SET
        );
    }

    private function deleteCustomFieldSet(Context $context, $customFieldSetRepository, string $customFieldSet): void
    {
        $customFieldIds = $this->getCustomFieldSetIds($context, $customFieldSet);

        if ($customFieldIds) {
            $customFieldSetRepository->delete(array_values($customFieldIds->getData()), $context);
        }
    }

    private function getCustomFieldSetIds(Context $context, string $customFieldSet): ?IdSearchResult
    {
        $customFieldSetRepository = $this->container->get('custom_field_set.repository');

        $criteria = new Criteria();
        $criteria->addFilter(new EqualsAnyFilter('name', [$customFieldSet]));

        $customFieldIds = $customFieldSetRepository->searchIds($criteria, $context);

        return $customFieldIds->getTotal() > 0 ? $customFieldIds : null;
    }
}

Then create a service on the given location which will create custom field:

/custom/plugins/PluginRoot/src/Services

<?php declare(strict_types=1);

namespace SidebarNavigation\Services;

use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\System\CustomField\CustomFieldTypes;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\System\CustomField\Aggregate\CustomFieldSet\CustomFieldSetCollection;
use Shopware\Core\Framework\Uuid\Uuid;

class SidebarNavigationCustomFieldSetService {
    //custom field set
    public const SIDEBAR_CATEGORY_NAVIGATION_CUSTOM_FIELD_SET = 'sidebar_category_navigation_custom_field_set';

    public const SIDEBAR_CATEGORY_ACTIVE_CUSTOM_FIELD = 'sidebar_category_active_custom_field';
    /**
     * @var EntityRepositoryInterface
     */
    private $customFieldSetRepository;

    public function __construct(EntityRepositoryInterface $customFieldSetRepository)
    {
        $this->customFieldSetRepository = $customFieldSetRepository;
    }

    public function createCategoryCustomFieldSet(Context $context): void
    {
        $criteria = new Criteria();
        $criteria->addFilter(new EqualsFilter('name', self::SIDEBAR_CATEGORY_NAVIGATION_CUSTOM_FIELD_SET));

        /** @var CustomFieldSetCollection $customFieldSets */
        $customFieldSets = $this->customFieldSetRepository->search($criteria, $context)->getEntities();

        if (!count($customFieldSets)) {
            $this->customFieldSetRepository->create([
                [
                    'name' => self::SIDEBAR_CATEGORY_NAVIGATION_CUSTOM_FIELD_SET,
                    'global' => true,
                    'config' => [
                        'label' => [
                            'de-DE' => 'Sidebar (Navigation)',
                            'en-GB' => 'Sidebar (Navigation)'
                        ]
                    ],
                'customFields' => [
                    [
                        'name' => self::SIDEBAR_CATEGORY_ACTIVE_CUSTOM_FIELD,
                        'type' => CustomFieldTypes::SWITCH,
                        'config' => [
                            'label' => [
                                'de-DE' => 'Hide in sidebar navigation',
                                'en-GB' => 'Hide in sidebar navigation'
                            ],
//Here I am creating SWITCH type field, we can create any field you want.
                            'componentName' => 'sw-field',
                            'type' => CustomFieldTypes::SWITCH,
                            'customFieldType' => CustomFieldTypes::SWITCH,
                            'customFieldPosition' => 1
                        ]
                    ]
                ],
                'relations' => [
                    [
                        'id' => Uuid::randomHex(),
                        'entityName' => 'category' // You can use any entity where you want to create custom field.
                    ]
                ]
                ]
            ], $context);

            
        }
    }
}

Here are some more types of custom fields:

  • 1) CustomFieldTypes::TEXT
  • 2) CustomFieldTypes::JSON
  • 3) CustomFieldTypes::INT
  • 4) CustomFieldTypes::BOOL

I have removed the categories from storefront for this I have extended the category navigation file. Follow the given location
custom/plugins/PluginRoot/src/Resources/views/storefront/layout/sidebar

{% sw_extends '@Storefront/storefront/layout/sidebar/category-navigation.html.twig' %}

{% block layout_navigation_categories_list_entry %}
{% if not item.category.customFields.sidebar_category_active_custom_field is defined %}
    <li class="category-navigation-entry">
        {% block layout_navigation_categories_link_children %}
           {{parent()}}
        {% endblock %}
        {% block layout_navigation_categories_recoursion %}
            {{parent()}}
        {% endblock %}
    </li>
{% endif %}
{% endblock %}

Look into Screenshot on Category for new Custom Field:

Shopware custom field 2
Here is the screenshot of the categories with removed menus:
shopware removed menus

Avatar photo
Author

Founder and tech lead at Emizentech, Mr. Vivek has over ten years of experience in developing IT infrastructures and solutions. With his profound knowledge in eCommerce technologies like Shopware, Magento, and Shopify, Mr. Vivek has been assisting SMEs to enterprises across the globe by developing and maintaining their eCommerce applications. Technology innovation and trends insight come easy to Vivek with his thorough knowledge in the eCommerce domain. See him talking about ideas, trends, and technology in this blog. To know more about how Team Vivek can assist you in your eCommerce strategy? Connect team Vivek here.