październik 26 2023

3min czytania

Custom fields w Shopware 6

-


Tworząc nową wtyczkę bardzo często musimy skorzystać z custom fields. O ich ogromnych funkcjonalnościach możemy przeczytać na stronie dokumentacji Shopware. W dużym skrócie, custom fields w Shopware służą do przechowywania dodatkowych danych przy encjach. W bazie danych przechowywane są w formacje JSON, władowane jako tablica. W tym artykule pochylimy się nad tym, jak powinno wyglądać ich prawidłowe wdrożenie w projekcie. Tekst jest adresowany do praktyków, którzy mają już za sobą pierwsze doświadczenia, związane z dodawaniem wtyczek. 

Najczęściej popełniane błędy 

  1. Najczęstszym błędem jest to, że podczas instalacji wtyczki dodajemy definicję custom fields, której nie usuwamy, gdy przestają być potrzebne. W efekcie wtyczka przy ponownej instalacji będzie wyrzucała błąd, że taka definicja już istnieje. 

  2. Kolejnym problemem z jakim możemy napotkać, jest zbyt duża ilość danych. Czasem potrzebujemy dodać kilka definicji. Wtedy nasz główny plik instalacyjny wtyczki będzie naprawdę długi, jeśli dojdą jeszcze kolejne funkcjonalności. W takim przypadku radzimy przenieść instalację custom fields do osobnej klasy, a jeśli posiadamy  dużo custom fields to nawet do kilku.

Poniżej zaprezentowaliśmy przykładowy główny plik wtyczki.

Takie rozwiązanie zapewni łatwiejszą rozbudowę dodatkowe pola w przyszłości oraz zapobiegnie nadmiernej ilości kodu w głównej klasie wtyczki. 

Jak instalować OrderCustomField

<?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());
    }
}

Przechodząc już do samej klasy OrderCustomField. Aby ułatwić późniejszą obsługę custom fieldów sugerujemy zdefiniować ich klucze jako const na początku definicji klasy.

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';
//...

Pierwszy klucz pozwala nam identyfikować cały set custom fields, a pozostałe dotyczą identyfikacji samych pól.

Klasa bez tworzenia samych pól – do tego przejdziemy później – będzie prezentować się tak jak poniżej. Pamiętajmy jednak, że klasa to jedna definicja pól. Jeśli chcemy umieścić kilka definicji custom fields w jednej klasie musimy uwzględnić to w funkcjach do aktywacji, usuwania. 

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);
    }
}

Tworzenie custom fields

Jeśli mamy już utworzoną strukturę zarządzającą flow custom fields, możemy przejść do samego tworzenia pól.

Dodając custom fields należy pamiętać, że dodajemy definicję setu custom fields (tabela w bazie danych o nazwie “custom_field_set”) oraz z definicji samych pól (tabel “custom_field”). Zmiana statutu setu ma wpływ na wszystkie powiązane pola.

Dla przykładu, najprostsza struktura tworzona pól wygląda tak:

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);
     }
}

Tworząc custom fields zazwyczaj dodajemy ją w jednym języku, w tym w którym klient będzie pracował. Tworząc wtyczkę do sklepu musimy wprowadzić translację w kilku językach. Jak to zrobić w nalepszy sposób? Poniższy przykład nie tylko rozdziela translację od kodu dodawania pól, ale również doda do bazy danych tylko te tłumaczenia, których język jest dodany do bazy Shopware.

Pierwszym krokiem będzie dodanie nowej klasy zawierającej tłumaczenia:

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'
        ]
    ];
}

Wracając do klasy tworzącej custom fields. Dodajemy funkcję która pobiera języki:

private function getAvailableLanguages(Context $context): array
{
    return $this->container->get('language.repository')->search(
        (new Criteria())->addAssociation('locale'),
        $context
    )->getElements();
}

Oraz funkcję która będzie generowała translacje:

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;
}

A następnie robimy poprawki w funkcji generującej:

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);
}

Tym sposobem mamy najbardziej kompleksowe wdrożenie custom fields, które w łatwy sposób możemy rozszerzać dodając kolejne pola lub języki. 


Do you have any questions?

Do you want to deepen this topic? Write!

hello@salestube.tech
You liked the article, share it with:

Let's change the world of e‑commerce together!

We're always happy when we can answer your questions. Feel free to contact us!