Entity identifiers

Entity identifiers

The traits in Ufo\DoctrineHelper\Traits add a protected $id property with the #[ORM\Id] and #[ORM\Column] attributes and a getId() method to an entity. The rest of the entity mapping (#[ORM\Entity], table, other fields) is described as usual.

Trait Column Generation getId()
AutoIncrementIdTrait INTEGER, unsigned by the database (#[ORM\GeneratedValue]) ?int
UuidIdTrait string(36), unique in code: generateUniqueId() → UUID v7 string
UniqueIdTrait string(13), unique in code: generateUniqueId() → uniqid() string
UniqueUuidIdTrait (deprecated) string(36), unique as in UuidIdTrait string

AutoIncrementIdTrait

use Doctrine\ORM\Mapping as ORM;
use Ufo\DoctrineHelper\Traits\AutoIncrementIdTrait;

#[ORM\Entity]
class Customer
{
    use AutoIncrementIdTrait;
}

The trait declares:

#[ORM\Id]
#[ORM\Column(type: Types::INTEGER, options: ['unsigned' => true])]
#[ORM\GeneratedValue]
protected ?int $id = null;

The value appears after flush(); until then getId() returns null.

Do not combine the trait with the IHaveAutoincrementId interface: the trait's getId(): ?int is incompatible with the interface's getId(): int, and PHP stops with a fatal error. Details and a workaround are in Known issues.

UuidIdTrait

#[ORM\Id]
#[ORM\Column(type: Types::STRING, length: 36, unique: true)]
#[ORM\GeneratedValue(strategy: 'NONE')]
protected string $id;

Doctrine does not generate the value (strategy: 'NONE'), so the entity sets its own identifier, usually in the constructor:

use Doctrine\ORM\Mapping as ORM;
use Ufo\DoctrineHelper\Interfaces\IHaveUniqueUuid;
use Ufo\DoctrineHelper\Traits\UuidIdTrait;

#[ORM\Entity]
class Order implements IHaveUniqueUuid
{
    use UuidIdTrait;

    public function __construct()
    {
        $this->generateUniqueId();   // e.g. 01a155a4-36cd-7341-9406-48aa6d401e53
    }
}

Trait methods:

  • protected generateUniqueId(): void — writes a new UUID v7 (Uuid::generate()) to $id. UUID v7 values are ordered by creation time, so they work well as an indexed primary key.
  • public getId(): string.
  • public static uniqueIdFromString(string $string): string — a deterministic UUID v3 from a string (Uuid::hash()): the same string always gives the same identifier.

A deterministic identifier is handy when an entity is uniquely defined by a natural key:

#[ORM\Entity]
class Tag implements IHaveUniqueUuid
{
    use UuidIdTrait;

    #[ORM\Column]
    private string $name;

    public function __construct(string $name)
    {
        $this->id = self::uniqueIdFromString($name);
        $this->name = $name;
    }
}

If you neither call generateUniqueId() nor assign $id, the property stays uninitialized, and getId() or persist() fail with an error.

UniqueIdTrait

The same approach, but the identifier is the result of uniqid(): 13 hexadecimal characters (string(13), unique), e.g. 6ad66fabb1662.

use Ufo\DoctrineHelper\Traits\UniqueIdTrait;

#[ORM\Entity]
class Session
{
    use UniqueIdTrait;

    public function __construct()
    {
        $this->generateUniqueId();
    }
}

uniqid() is built from the current time in microseconds and does not guarantee uniqueness when identifiers are created in parallel by several processes. Where that matters, use UuidIdTrait. This trait has no uniqueIdFromString() method.

UniqueUuidIdTrait

Marked @deprecated Use UuidIdTrait instead. It differs from UuidIdTrait only in lacking the #[ORM\GeneratedValue] attribute (for #[ORM\Id] without it Doctrine does not generate the value either). Switching is a one-line use change.

Interfaces

Interface Method Compatible traits
IHaveUniqueUuid getId(): string UuidIdTrait, UniqueIdTrait, UniqueUuidIdTrait
IHaveAutoincrementId getId(): int none (see the warning above)

The interfaces let you type code that works with any entity of a given key kind without binding it to a concrete class:

use Ufo\DoctrineHelper\Interfaces\IHaveUniqueUuid;

function cacheKey(IHaveUniqueUuid $entity): string
{
    return $entity::class . ':' . $entity->getId();
}

Uuid

Ufo\DoctrineHelper\Utils\Uuid is a wrapper over ramsey/uuid used by the traits. It can also be called directly:

use Ufo\DoctrineHelper\Utils\Uuid;

Uuid::generate();        // a new UUID v7
Uuid::hash('test');      // UUID v3 in the OID namespace: always 61df151d-7508-321d-ada6-27936752b809

Before 1.1.0 Uuid::generate() returned a UUID v4 (see the Changelog).