WEBcoast Logo

Symfony: Automatisch generierte UUID zusätzlich zu auto_increment id property

In einem meiner Projekte wollte ich einem meiner Models, das bereits eine `id` mit auto_increment besaß, eine UUID oder GUID hinzufügen. Offensichtlich ist es allerdings nicht erlaubt bzw. nicht möglich mehrere Properties mit der `@GeneratedValue` Annotation zu versehen.

Alle Code-Beispiele, die ich dazu gefunden habe, haben allerdings die UUID immer manuell im Controller-Code erzeugt und dann im Model gesetzt. Da im Projekt allerdings sowohl ein Web-Interface als auch eine ReST-API vorhanden ist, wollte ich die Generierung gerne zentral und automatisch haben. Die Lösung, zu der ich bekommen bin, nutzt Symfony's/Doctrine's Event System. Ich habe mich in diesem Fall für die Doctrine Entity Listener entschieden.

In meinem Model, habe ich die Property `uid` hinzugefügt:

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

    // ...
}

Den Listener habe wie folgt implementiert:

<?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 der `services.yaml` habe ich den Listener folgender Maßen registriert:

# ...

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

Für manche ist die Lösung vielleicht offensichtlich. Ich habe dennoch einen Moment gebraucht, um darauf zu kommen. Daher dachte ich mir, ich das Teile meine Erkenntnisse mit euch.