Ідентифікатори сутностей

← Ідентифікатори сутностей

Ashterix

docs: Doctrine section with Doctrine Behaviors and Doctrine Helper (uk + en)

uk/docs/doctrine/doctrine-helper/entity_ids.md новий файл +149 −0

1 + # Ідентифікатори сутностей
2 +
3 + Трейти з `Ufo\DoctrineHelper\Traits` додають у сутність властивість `protected $id` з атрибутами `#[ORM\Id]` і `#[ORM\Column]` та метод `getId()`. Решту мапінгу сутності (`#[ORM\Entity]`, таблиця, інші поля) ви описуєте як завжди.
4 +
5 + | Трейт | Колонка | Генерація | `getId()` |
6 + |-------|---------|-----------|-----------|
7 + | `AutoIncrementIdTrait` | `INTEGER`, unsigned | базою даних (`#[ORM\GeneratedValue]`) | `?int` |
8 + | `UuidIdTrait` | `string(36)`, unique | у коді: `generateUniqueId()` → UUID v7 | `string` |
9 + | `UniqueIdTrait` | `string(13)`, unique | у коді: `generateUniqueId()` → `uniqid()` | `string` |
10 + | `UniqueUuidIdTrait` (застарілий) | `string(36)`, unique | як у `UuidIdTrait` | `string` |
11 +
12 + ## AutoIncrementIdTrait
13 +
14 + ```php
15 + use Doctrine\ORM\Mapping as ORM;
16 + use Ufo\DoctrineHelper\Traits\AutoIncrementIdTrait;
17 +
18 + #[ORM\Entity]
19 + class Customer
20 + {
21 + use AutoIncrementIdTrait;
22 + }
23 + ```
24 +
25 + Трейт оголошує:
26 +
27 + ```php
28 + #[ORM\Id]
29 + #[ORM\Column(type: Types::INTEGER, options: ['unsigned' => true])]
30 + #[ORM\GeneratedValue]
31 + protected ?int $id = null;
32 + ```
33 +
34 + Значення з'являється після `flush()`; до того `getId()` повертає `null`.
35 +
36 + :::warning
37 + Не поєднуйте трейт з інтерфейсом `IHaveAutoincrementId`: `getId(): ?int` трейту несумісний з `getId(): int` інтерфейсу, і PHP завершується з fatal error. Деталі й обхідний шлях — у [Відомих помилках](/docs/doctrine/doctrine-helper/known_issues/open).
38 + :::
39 +
40 + ## UuidIdTrait
41 +
42 + ```php
43 + #[ORM\Id]
44 + #[ORM\Column(type: Types::STRING, length: 36, unique: true)]
45 + #[ORM\GeneratedValue(strategy: 'NONE')]
46 + protected string $id;
47 + ```
48 +
49 + Doctrine не генерує значення (`strategy: 'NONE'`), тому ідентифікатор задає сама сутність — зазвичай у конструкторі:
50 +
51 + ```php
52 + use Doctrine\ORM\Mapping as ORM;
53 + use Ufo\DoctrineHelper\Interfaces\IHaveUniqueUuid;
54 + use Ufo\DoctrineHelper\Traits\UuidIdTrait;
55 +
56 + #[ORM\Entity]
57 + class Order implements IHaveUniqueUuid
58 + {
59 + use UuidIdTrait;
60 +
61 + public function __construct()
62 + {
63 + $this->generateUniqueId(); // напр. 01a155a4-36cd-7341-9406-48aa6d401e53
64 + }
65 + }
66 + ```
67 +
68 + Методи трейту:
69 +
70 + * `protected generateUniqueId(): void` — записує в `$id` новий UUID v7 (`Uuid::generate()`). UUID v7 впорядковані за часом створення, тож добре працюють як первинний ключ з індексом.
71 + * `public getId(): string`.
72 + * `public static uniqueIdFromString(string $string): string` — детермінований UUID v3 з рядка (`Uuid::hash()`): той самий рядок завжди дає той самий ідентифікатор.
73 +
74 + Детермінований ідентифікатор зручний, коли сутність однозначно визначається природним ключем:
75 +
76 + ```php
77 + #[ORM\Entity]
78 + class Tag implements IHaveUniqueUuid
79 + {
80 + use UuidIdTrait;
81 +
82 + #[ORM\Column]
83 + private string $name;
84 +
85 + public function __construct(string $name)
86 + {
87 + $this->id = self::uniqueIdFromString($name);
88 + $this->name = $name;
89 + }
90 + }
91 + ```
92 +
93 + Якщо не викликати `generateUniqueId()` і не присвоїти `$id`, властивість лишиться неініціалізованою, і `getId()` чи `persist()` завершаться помилкою.
94 +
95 + ## UniqueIdTrait
96 +
97 + Той самий підхід, але ідентифікатор — результат `uniqid()`: 13 шістнадцяткових символів (`string(13)`, unique), напр. `6ad66fabb1662`.
98 +
99 + ```php
100 + use Ufo\DoctrineHelper\Traits\UniqueIdTrait;
101 +
102 + #[ORM\Entity]
103 + class Session
104 + {
105 + use UniqueIdTrait;
106 +
107 + public function __construct()
108 + {
109 + $this->generateUniqueId();
110 + }
111 + }
112 + ```
113 +
114 + `uniqid()` будується з поточного часу в мікросекундах і не гарантує унікальності, якщо ідентифікатори створюються паралельно в кількох процесах. Там, де це важливо, беріть `UuidIdTrait`. Методу `uniqueIdFromString()` цей трейт не має.
115 +
116 + ## UniqueUuidIdTrait
117 +
118 + Позначений `@deprecated Use UuidIdTrait instead`. Відрізняється від `UuidIdTrait` лише тим, що не має атрибута `#[ORM\GeneratedValue]` (для `#[ORM\Id]` без нього Doctrine теж не генерує значення). Заміна — зміна одного рядка `use`.
119 +
120 + ## Інтерфейси
121 +
122 + | Інтерфейс | Метод | Сумісні трейти |
123 + |-----------|-------|----------------|
124 + | `IHaveUniqueUuid` | `getId(): string` | `UuidIdTrait`, `UniqueIdTrait`, `UniqueUuidIdTrait` |
125 + | `IHaveAutoincrementId` | `getId(): int` | жоден (див. попередження вище) |
126 +
127 + Інтерфейси дають змогу типізувати код, що працює з будь-якою сутністю певного виду ключа, без прив'язки до конкретного класу:
128 +
129 + ```php
130 + use Ufo\DoctrineHelper\Interfaces\IHaveUniqueUuid;
131 +
132 + function cacheKey(IHaveUniqueUuid $entity): string
133 + {
134 + return $entity::class . ':' . $entity->getId();
135 + }
136 + ```
137 +
138 + ## Uuid
139 +
140 + `Ufo\DoctrineHelper\Utils\Uuid` — обгортка над `ramsey/uuid`, яку використовують трейти. Її можна викликати й напряму:
141 +
142 + ```php
143 + use Ufo\DoctrineHelper\Utils\Uuid;
144 +
145 + Uuid::generate(); // новий UUID v7
146 + Uuid::hash('test'); // UUID v3 у просторі імен OID: завжди 61df151d-7508-321d-ada6-27936752b809
147 + ```
148 +
149 + До 1.1.0 `Uuid::generate()` повертав UUID v4 (див. [Історію змін](/docs/doctrine/doctrine-helper/change_log)).