WEBcoast Logo

Symfony: Generated UUID in additions to auto incremented id property

In one of my projects I wanted to add a UUID or GUID to one of the models, that already had an `id` property with a generated value (auto increment). Obviously it is not allowed or not possible to use multiple properties with the `@GeneratedValue` annotation.

All code examples set the UUID manually in the controller code. As the project both has a user interface and a ReST API I wanted to avoid that scenario. The solution I came up with, was using Symfony's/Doctrine's event system. For performance reasons I went with a Doctrine Entity Listener.

In my model I now have the property `uid` like that:

<?php

namespace MyVendor\MyProject\Entity;

// ...
use Symfony\Component\Uid\Uuid;
// ...

class MyModel {
    // ...

    /**
     * @ORM\Column(type="uuid")
     */
    private Uuid $uid;

    // ...

    public function getUid(): Uuid
    {
        return $this->uid;
    }

    public function setUid(Uuid $uid): MyModel
    {
        $this->uid = $uid;

        return $this;
    }

    // ...
}

I implemented the listener class as follows:

<?php

declare(strict_types=1);

namespace MyVendor\MyProject\EventListener;

use Doctrine\Persistence\Event\LifecycleEventArgs;
use MyVendor\MyProject\Entity\MyModel;
use Symfony\Component\Uid\Factory\UuidFactory;

class MyModelPersistenceListener
{
    protected UuidFactory $uuidFactory;

    /**
     * @param UuidFactory $uuidFactory
     */
    public function __construct(UuidFactory $uuidFactory)
    {
        $this->uuidFactory = $uuidFactory;
    }

    public function prePersist(MyModel $model, LifecycleEventArgs $lifecycleEventArgs)
    {
        $model->setUid($this->uuidFactory->create());
    }
}

In my `services.yaml` i configured the listener like:

# ...

    MyVendor\MyProject\EventListener\MyModelPersistenceListener:
        tags:
            -
                name: 'doctrine.orm.entity_listener'
                event: 'prePersist'
                entity: 'MyVendor\MyProject\Entity\MyModel'
# ...

This solution may seem obvious, but it took me a while to figure it out. So I thought I share it.