summaryrefslogtreecommitdiff
path: root/vendor/doctrine/orm/src/Mapping/AssociationMapping.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/doctrine/orm/src/Mapping/AssociationMapping.php')
-rw-r--r--vendor/doctrine/orm/src/Mapping/AssociationMapping.php359
1 files changed, 359 insertions, 0 deletions
diff --git a/vendor/doctrine/orm/src/Mapping/AssociationMapping.php b/vendor/doctrine/orm/src/Mapping/AssociationMapping.php
new file mode 100644
index 0000000..ce7bdb4
--- /dev/null
+++ b/vendor/doctrine/orm/src/Mapping/AssociationMapping.php
@@ -0,0 +1,359 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Mapping;
6
7use ArrayAccess;
8use Exception;
9use OutOfRangeException;
10
11use function assert;
12use function count;
13use function in_array;
14use function property_exists;
15use function sprintf;
16
17/** @template-implements ArrayAccess<string, mixed> */
18abstract class AssociationMapping implements ArrayAccess
19{
20 /**
21 * The names of persistence operations to cascade on the association.
22 *
23 * @var list<'persist'|'remove'|'detach'|'refresh'|'all'>
24 */
25 public array $cascade = [];
26
27 /**
28 * The fetching strategy to use for the association, usually defaults to FETCH_LAZY.
29 *
30 * @var ClassMetadata::FETCH_*|null
31 */
32 public int|null $fetch = null;
33
34 /**
35 * This is set when the association is inherited by this class from another
36 * (inheritance) parent <em>entity</em> class. The value is the FQCN of the
37 * topmost entity class that contains this association. (If there are
38 * transient classes in the class hierarchy, these are ignored, so the
39 * class property may in fact come from a class further up in the PHP class
40 * hierarchy.) To-many associations initially declared in mapped
41 * superclasses are <em>not</em> considered 'inherited' in the nearest
42 * entity subclasses.
43 *
44 * @var class-string|null
45 */
46 public string|null $inherited = null;
47
48 /**
49 * This is set when the association does not appear in the current class
50 * for the first time, but is initially declared in another parent
51 * <em>entity or mapped superclass</em>. The value is the FQCN of the
52 * topmost non-transient class that contains association information for
53 * this relationship.
54 *
55 * @var class-string|null
56 */
57 public string|null $declared = null;
58
59 public array|null $cache = null;
60
61 public bool|null $id = null;
62
63 public bool|null $isOnDeleteCascade = null;
64
65 /** @var class-string|null */
66 public string|null $originalClass = null;
67
68 public string|null $originalField = null;
69
70 public bool $orphanRemoval = false;
71
72 public bool|null $unique = null;
73
74 /**
75 * @param string $fieldName The name of the field in the entity
76 * the association is mapped to.
77 * @param class-string $sourceEntity The class name of the source entity.
78 * In the case of to-many associations
79 * initially present in mapped
80 * superclasses, the nearest
81 * <em>entity</em> subclasses will be
82 * considered the respective source
83 * entities.
84 * @param class-string $targetEntity The class name of the target entity.
85 * If it is fully-qualified it is used as
86 * is. If it is a simple, unqualified
87 * class name the namespace is assumed to
88 * be the same as the namespace of the
89 * source entity.
90 */
91 final public function __construct(
92 public readonly string $fieldName,
93 public string $sourceEntity,
94 public readonly string $targetEntity,
95 ) {
96 }
97
98 /**
99 * @param mixed[] $mappingArray
100 * @psalm-param array{
101 * fieldName: string,
102 * sourceEntity: class-string,
103 * targetEntity: class-string,
104 * cascade?: list<'persist'|'remove'|'detach'|'refresh'|'all'>,
105 * fetch?: ClassMetadata::FETCH_*|null,
106 * inherited?: class-string|null,
107 * declared?: class-string|null,
108 * cache?: array<mixed>|null,
109 * id?: bool|null,
110 * isOnDeleteCascade?: bool|null,
111 * originalClass?: class-string|null,
112 * originalField?: string|null,
113 * orphanRemoval?: bool,
114 * unique?: bool|null,
115 * joinTable?: mixed[]|null,
116 * type?: int,
117 * isOwningSide: bool,
118 * } $mappingArray
119 */
120 public static function fromMappingArray(array $mappingArray): static
121 {
122 unset($mappingArray['isOwningSide'], $mappingArray['type']);
123 $mapping = new static(
124 $mappingArray['fieldName'],
125 $mappingArray['sourceEntity'],
126 $mappingArray['targetEntity'],
127 );
128 unset($mappingArray['fieldName'], $mappingArray['sourceEntity'], $mappingArray['targetEntity']);
129
130 foreach ($mappingArray as $key => $value) {
131 if ($key === 'joinTable') {
132 assert($mapping instanceof ManyToManyAssociationMapping);
133
134 if ($value === [] || $value === null) {
135 continue;
136 }
137
138 assert($mapping instanceof ManyToManyOwningSideMapping);
139
140 $mapping->joinTable = JoinTableMapping::fromMappingArray($value);
141
142 continue;
143 }
144
145 if (property_exists($mapping, $key)) {
146 $mapping->$key = $value;
147 } else {
148 throw new OutOfRangeException('Unknown property ' . $key . ' on class ' . static::class);
149 }
150 }
151
152 return $mapping;
153 }
154
155 /**
156 * @psalm-assert-if-true OwningSideMapping $this
157 * @psalm-assert-if-false InverseSideMapping $this
158 */
159 final public function isOwningSide(): bool
160 {
161 return $this instanceof OwningSideMapping;
162 }
163
164 /** @psalm-assert-if-true ToOneAssociationMapping $this */
165 final public function isToOne(): bool
166 {
167 return $this instanceof ToOneAssociationMapping;
168 }
169
170 /** @psalm-assert-if-true ToManyAssociationMapping $this */
171 final public function isToMany(): bool
172 {
173 return $this instanceof ToManyAssociationMapping;
174 }
175
176 /** @psalm-assert-if-true OneToOneOwningSideMapping $this */
177 final public function isOneToOneOwningSide(): bool
178 {
179 return $this->isOneToOne() && $this->isOwningSide();
180 }
181
182 /** @psalm-assert-if-true OneToOneOwningSideMapping|ManyToOneAssociationMapping $this */
183 final public function isToOneOwningSide(): bool
184 {
185 return $this->isToOne() && $this->isOwningSide();
186 }
187
188 /** @psalm-assert-if-true ManyToManyOwningSideMapping $this */
189 final public function isManyToManyOwningSide(): bool
190 {
191 return $this instanceof ManyToManyOwningSideMapping;
192 }
193
194 /** @psalm-assert-if-true OneToOneAssociationMapping $this */
195 final public function isOneToOne(): bool
196 {
197 return $this instanceof OneToOneAssociationMapping;
198 }
199
200 /** @psalm-assert-if-true OneToManyAssociationMapping $this */
201 final public function isOneToMany(): bool
202 {
203 return $this instanceof OneToManyAssociationMapping;
204 }
205
206 /** @psalm-assert-if-true ManyToOneAssociationMapping $this */
207 final public function isManyToOne(): bool
208 {
209 return $this instanceof ManyToOneAssociationMapping;
210 }
211
212 /** @psalm-assert-if-true ManyToManyAssociationMapping $this */
213 final public function isManyToMany(): bool
214 {
215 return $this instanceof ManyToManyAssociationMapping;
216 }
217
218 /** @psalm-assert-if-true ToManyAssociationMapping $this */
219 final public function isOrdered(): bool
220 {
221 return $this->isToMany() && $this->orderBy() !== [];
222 }
223
224 /** @psalm-assert-if-true ToManyAssociationMapping $this */
225 public function isIndexed(): bool
226 {
227 return false;
228 }
229
230 final public function type(): int
231 {
232 return match (true) {
233 $this instanceof OneToOneAssociationMapping => ClassMetadata::ONE_TO_ONE,
234 $this instanceof OneToManyAssociationMapping => ClassMetadata::ONE_TO_MANY,
235 $this instanceof ManyToOneAssociationMapping => ClassMetadata::MANY_TO_ONE,
236 $this instanceof ManyToManyAssociationMapping => ClassMetadata::MANY_TO_MANY,
237 default => throw new Exception('Cannot determine type for ' . static::class),
238 };
239 }
240
241 /** @param string $offset */
242 public function offsetExists(mixed $offset): bool
243 {
244 return isset($this->$offset) || in_array($offset, ['isOwningSide', 'type'], true);
245 }
246
247 final public function offsetGet(mixed $offset): mixed
248 {
249 return match ($offset) {
250 'isOwningSide' => $this->isOwningSide(),
251 'type' => $this->type(),
252 'isCascadeRemove' => $this->isCascadeRemove(),
253 'isCascadePersist' => $this->isCascadePersist(),
254 'isCascadeRefresh' => $this->isCascadeRefresh(),
255 'isCascadeDetach' => $this->isCascadeDetach(),
256 default => property_exists($this, $offset) ? $this->$offset : throw new OutOfRangeException(sprintf(
257 'Unknown property "%s" on class %s',
258 $offset,
259 static::class,
260 )),
261 };
262 }
263
264 public function offsetSet(mixed $offset, mixed $value): void
265 {
266 assert($offset !== null);
267 if (! property_exists($this, $offset)) {
268 throw new OutOfRangeException(sprintf(
269 'Unknown property "%s" on class %s',
270 $offset,
271 static::class,
272 ));
273 }
274
275 if ($offset === 'joinTable') {
276 $value = JoinTableMapping::fromMappingArray($value);
277 }
278
279 $this->$offset = $value;
280 }
281
282 /** @param string $offset */
283 public function offsetUnset(mixed $offset): void
284 {
285 if (! property_exists($this, $offset)) {
286 throw new OutOfRangeException(sprintf(
287 'Unknown property "%s" on class %s',
288 $offset,
289 static::class,
290 ));
291 }
292
293 $this->$offset = null;
294 }
295
296 final public function isCascadeRemove(): bool
297 {
298 return in_array('remove', $this->cascade, true);
299 }
300
301 final public function isCascadePersist(): bool
302 {
303 return in_array('persist', $this->cascade, true);
304 }
305
306 final public function isCascadeRefresh(): bool
307 {
308 return in_array('refresh', $this->cascade, true);
309 }
310
311 final public function isCascadeDetach(): bool
312 {
313 return in_array('detach', $this->cascade, true);
314 }
315
316 /** @return array<string, mixed> */
317 public function toArray(): array
318 {
319 $array = (array) $this;
320
321 $array['isOwningSide'] = $this->isOwningSide();
322 $array['type'] = $this->type();
323
324 return $array;
325 }
326
327 /** @return list<string> */
328 public function __sleep(): array
329 {
330 $serialized = ['fieldName', 'sourceEntity', 'targetEntity'];
331
332 if (count($this->cascade) > 0) {
333 $serialized[] = 'cascade';
334 }
335
336 foreach (
337 [
338 'fetch',
339 'inherited',
340 'declared',
341 'cache',
342 'originalClass',
343 'originalField',
344 ] as $stringOrArrayProperty
345 ) {
346 if ($this->$stringOrArrayProperty !== null) {
347 $serialized[] = $stringOrArrayProperty;
348 }
349 }
350
351 foreach (['id', 'orphanRemoval', 'isOnDeleteCascade', 'unique'] as $boolProperty) {
352 if ($this->$boolProperty) {
353 $serialized[] = $boolProperty;
354 }
355 }
356
357 return $serialized;
358 }
359}