Shopware: Hard coded UUIDs

Shopware uses UUIDs. One of the cool things about UUIDs is, that they are so long and random, that you can be pretty sure (as in, no need to think about it – ever) that you never get one twice. That means that it allows setups like shopware does not decide which UUID is used for a product, the cart or an order but another system does it. Can be your JavaScript PWA, your ERP system or an app.

The past: auto increment

In the past that was not the case. When we used integer auto increment values we always had the problem, that there was only a single source of truth which knew what was the next value to use. That meant as well, that you only have one write server (what we once called master) and a lot of readers (what we once called slaves).

The futures: UUIDs

That is not the case anymore. You can have multiple master and because all use other UUIDs (remember, we can be sure to not repeat) they can sync without conflict. I’m no database pro and it might be still not advisable, but at least the IDs are no reason against it anymore!

You wanted to talk about hard coded UUIDs!

Yes you are right, let’s talk about hard coded UUIDs: Because Shopware doesn’t need control over the IDs anymore WE can decide what UUID to use. In our project we often hard code UUIDs, because it makes a few things easier. We create special products with a migration script or on \Shopware\Core\Framework\Plugin::install()? If we ever need to access it again, let’s hardcode the UUID, so we don’t need to load it by product_number – it is writeable, so we can’t be sure it is not changed.

I’m writing this blog post, because a Shopware employee during the code review of a payment extension we wrote told me, he saw it never before and I was puzzled. It is so great to load your entities by id. Entities we already gave hardcoded ids:

  • Payment methods
  • SalesChannel
  • Properties
  • Property Groups
  • Custom Fields
  • Custom Field Sets
  • Media
  • Tag
  • Child Themes (which don’t have a “plugin theme” behind them, but only inherit from one)

Example why we like it

Remember the shopware documentation example to implement a payment method? They first need to find the payment method

<?php declare(strict_types=1);

// [...]
class SwagBasicExample extends Plugin
{

    public function activate(ActivateContext $context): void
    {
        $this->setPaymentMethodIsActive(true, $context->getContext());
        parent::activate($context);
    }

    private function setPaymentMethodIsActive(bool $active, Context $context): void
    {
        /** @var EntityRepository $paymentRepository */
        $paymentRepository = $this->container->get('payment_method.repository');

        $paymentMethodId = $this->getPaymentMethodId();

        // Payment does not even exist, so nothing to (de-)activate here
        if (!$paymentMethodId) {
            return;
        }

        $paymentMethod = [
            'id' => $paymentMethodId,
            'active' => $active,
        ];

        $paymentRepository->update([$paymentMethod], $context);
    }

    private function getPaymentMethodId(): ?string
    {
        /** @var EntityRepository $paymentRepository */
        $paymentRepository = $this->container->get('payment_method.repository');

        // Fetch ID for update
        $paymentCriteria = (new Criteria())->addFilter(new EqualsFilter('handlerIdentifier', ExamplePayment::class));
        return $paymentRepository->searchIds($paymentCriteria, Context::createDefaultContext())->firstId();
    }
}

Thanks to hardcoded IDs we can reduce the code to:

<?php

// [...]

class WWPaymentExtension extends Plugin
{
    public const CARD_PAYMENT_ID = '098c19de4c714cb09aab81d95684c965';

    public function activate(ActivateContext $activateContext): void
    {
        $this->setPaymentMethodIsActive(true, $activateContext->getContext());
        parent::activate($activateContext);
    }

    private function setPaymentMethodIsActive(bool $active, Context $context): void
    {
        /** @var EntityRepository $paymentRepository */
        $paymentRepository = $this->container->get('payment_method.repository');

        $paymentMethod = [
            [
                'id' => self:: CARD_PAYMENT_ID,
                'active' => $active,
            ],
        ];

        $paymentRepository->update($paymentMethod, $context);
    }

}

But be careful

Entities are writeable, so if your user change things, they might break, because we didn’t prepare for this case.

And not every UUID needs to be hardcoded. Many can be generated with Uuid::randomHex() and it is totally fine if you don’t need to find THIS entity later!

One thought on “Shopware: Hard coded UUIDs

Leave a Reply

Discover more from Winkelwagen

Subscribe now to keep reading and get access to the full archive.

Continue reading