1 | <?php |
||||
2 | |||||
3 | namespace Bdf\Prime\Relations; |
||||
4 | |||||
5 | use Bdf\Prime\Query\Contract\EntityJoinable; |
||||
6 | use Bdf\Prime\Query\Contract\ReadOperation; |
||||
7 | use Bdf\Prime\Query\Contract\WriteOperation; |
||||
8 | use Bdf\Prime\Query\ReadCommandInterface; |
||||
9 | use Bdf\Prime\Repository\RepositoryInterface; |
||||
10 | use InvalidArgumentException; |
||||
11 | |||||
12 | /** |
||||
13 | * OneOrMany |
||||
14 | * |
||||
15 | * @package Bdf\Prime\Relations |
||||
16 | * |
||||
17 | * @todo possibilité de désactiver les constraints globales |
||||
18 | * |
||||
19 | * @template L as object |
||||
20 | * @template R as object |
||||
21 | * |
||||
22 | * @extends Relation<L, R> |
||||
23 | */ |
||||
24 | abstract class OneOrMany extends Relation |
||||
25 | { |
||||
26 | /** |
||||
27 | * {@inheritdoc} |
||||
28 | */ |
||||
29 | 84 | public function relationRepository(): RepositoryInterface |
|||
30 | { |
||||
31 | 84 | return $this->distant; |
|||
32 | } |
||||
33 | |||||
34 | /** |
||||
35 | * {@inheritdoc} |
||||
36 | */ |
||||
37 | 240 | protected function applyConstraints(ReadCommandInterface $query, $constraints = [], $context = null): ReadCommandInterface |
|||
38 | { |
||||
39 | 240 | parent::applyConstraints($query, $constraints, $context); |
|||
40 | |||||
41 | // Si le dépot distant possède la foreign key, on estime qu'il possède le discriminator |
||||
42 | // On applique donc la contrainte de relation sur le discriminitor |
||||
43 | 240 | if ($this->isPolymorphic() && $this->isForeignKeyBarrier($this->distant->entityClass())) { |
|||
44 | 9 | $query->where( |
|||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
45 | 9 | $this->applyContext($context, [$this->discriminator => $this->discriminatorValue]) |
|||
46 | 9 | ); |
|||
47 | } |
||||
48 | |||||
49 | 240 | return $query; |
|||
50 | } |
||||
51 | |||||
52 | /** |
||||
53 | * {@inheritdoc} |
||||
54 | */ |
||||
55 | 77 | public function join(EntityJoinable $query, string $alias): void |
|||
56 | { |
||||
57 | // @fixme ? |
||||
58 | // if ($alias === null) { |
||||
59 | // $alias = $this->attributeAim; |
||||
60 | // } |
||||
61 | |||||
62 | 77 | $query->joinEntity($this->distant->entityName(), $this->distantKey, $this->getLocalAlias($query).$this->localKey, $alias); |
|||
63 | |||||
64 | // apply relation constraints |
||||
65 | 77 | $this->applyConstraints($query, [], '$'.$alias); |
|||
66 | } |
||||
67 | |||||
68 | /** |
||||
69 | * {@inheritdoc} |
||||
70 | */ |
||||
71 | 63 | public function joinRepositories(EntityJoinable $query, string $alias, $discriminator = null): array |
|||
72 | { |
||||
73 | 63 | return [ |
|||
74 | 63 | $alias => $this->relationRepository() |
|||
75 | 63 | ]; |
|||
76 | } |
||||
77 | |||||
78 | /** |
||||
79 | * {@inheritdoc} |
||||
80 | */ |
||||
81 | #[ReadOperation] |
||||
82 | 216 | protected function relations($keys, $with, $constraints, $without): array |
|||
83 | { |
||||
84 | /** @var R[] */ |
||||
85 | 216 | return $this->relationQuery($keys, $constraints) |
|||
86 | 216 | ->with($with) |
|||
0 ignored issues
–
show
The method
with() does not exist on Bdf\Prime\Query\ReadCommandInterface . It seems like you code against a sub-type of said class. However, the method does not exist in Bdf\Prime\Query\Contract...\KeyValueQueryInterface or Bdf\Prime\Query\QueryInterface or Bdf\Prime\Query\SqlQueryInterface . Are you sure you never get one of those?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
87 | 216 | ->without($without) |
|||
88 | 216 | ->all(); |
|||
89 | } |
||||
90 | |||||
91 | /** |
||||
92 | * {@inheritdoc} |
||||
93 | */ |
||||
94 | 213 | protected function match($collection, $relations): void |
|||
95 | { |
||||
96 | 213 | foreach ($relations as $key => $distant) { |
|||
97 | 203 | foreach ($collection[$key] as $local) { |
|||
98 | 203 | $this->setRelation($local, $distant); |
|||
99 | } |
||||
100 | } |
||||
101 | } |
||||
102 | |||||
103 | /** |
||||
104 | * {@inheritdoc} |
||||
105 | */ |
||||
106 | 28 | public function link($owner): ReadCommandInterface |
|||
107 | { |
||||
108 | 28 | return $this->query($this->getLocalKeyValue($owner)); |
|||
109 | } |
||||
110 | |||||
111 | /** |
||||
112 | * {@inheritdoc} |
||||
113 | */ |
||||
114 | 13 | public function associate($owner, $entity) |
|||
115 | { |
||||
116 | 13 | if (!$this->isForeignKeyBarrier($owner)) { |
|||
117 | 6 | throw new InvalidArgumentException('The local entity is not the foreign key barrier.'); |
|||
118 | } |
||||
119 | |||||
120 | 7 | if ($this->isPolymorphic()) { |
|||
121 | 4 | $this->discriminatorValue = $this->discriminator(get_class($entity)); |
|||
122 | } |
||||
123 | |||||
124 | 7 | $this->setForeignKeyValue($owner, $this->getDistantKeyValue($entity)); |
|||
125 | 7 | $this->setRelation($owner, $entity); |
|||
126 | |||||
127 | 7 | return $owner; |
|||
128 | } |
||||
129 | |||||
130 | /** |
||||
131 | * {@inheritdoc} |
||||
132 | */ |
||||
133 | 13 | public function dissociate($owner) |
|||
134 | { |
||||
135 | 13 | if (!$this->isForeignKeyBarrier($owner)) { |
|||
136 | 6 | throw new InvalidArgumentException('The local entity is not the foreign key barrier.'); |
|||
137 | } |
||||
138 | |||||
139 | 7 | if ($this->isPolymorphic()) { |
|||
140 | 4 | $this->discriminatorValue = null; |
|||
141 | } |
||||
142 | |||||
143 | // TODO Dont update key if it is embedded in the relation object |
||||
144 | 7 | $this->setForeignKeyValue($owner, null); |
|||
145 | 7 | $this->setRelation($owner, null); |
|||
146 | |||||
147 | 7 | return $owner; |
|||
148 | } |
||||
149 | |||||
150 | /** |
||||
151 | * {@inheritdoc} |
||||
152 | */ |
||||
153 | 15 | public function create($owner, array $data = []) |
|||
154 | { |
||||
155 | 15 | if ($this->isForeignKeyBarrier($owner)) { |
|||
156 | 6 | throw new InvalidArgumentException('The local entity is not the primary key barrier.'); |
|||
157 | } |
||||
158 | |||||
159 | 9 | $entity = $this->distant->entity($data); |
|||
160 | |||||
161 | 9 | $this->setForeignKeyValue($entity, $this->getLocalKeyValue($owner)); |
|||
162 | |||||
163 | 9 | return $entity; |
|||
164 | } |
||||
165 | |||||
166 | /** |
||||
167 | * {@inheritdoc} |
||||
168 | */ |
||||
169 | #[WriteOperation] |
||||
170 | public function add($owner, $related): int |
||||
171 | { |
||||
172 | if ($this->isForeignKeyBarrier($owner)) { |
||||
173 | throw new InvalidArgumentException('The local entity is not the primary key barrier.'); |
||||
174 | } |
||||
175 | |||||
176 | $this->setForeignKeyValue($related, $this->getLocalKeyValue($owner)); |
||||
177 | |||||
178 | return $this->distant->save($related); |
||||
179 | } |
||||
180 | |||||
181 | /** |
||||
182 | * {@inheritdoc} |
||||
183 | */ |
||||
184 | #[WriteOperation] |
||||
185 | 30 | public function saveAll($owner, array $relations = []): int |
|||
186 | { |
||||
187 | 30 | $entities = $this->getRelation($owner); |
|||
188 | |||||
189 | 30 | if (empty($entities)) { |
|||
190 | 4 | return 0; |
|||
191 | } |
||||
192 | |||||
193 | 26 | $id = $this->getLocalKeyValue($owner); |
|||
194 | |||||
195 | //Detach all relations |
||||
196 | 26 | if ($this->saveStrategy === self::SAVE_STRATEGY_REPLACE) { |
|||
197 | 4 | $this->query($id)->delete(); |
|||
198 | } |
||||
199 | |||||
200 | 26 | if (!is_array($entities)) { |
|||
201 | 22 | $entities = [$entities]; |
|||
202 | } |
||||
203 | |||||
204 | // Save new relations |
||||
205 | 26 | $nb = 0; |
|||
206 | |||||
207 | 26 | foreach ($entities as $entity) { |
|||
208 | 26 | $this->setForeignKeyValue($entity, $id); |
|||
209 | 26 | $nb += $this->distant->saveAll($entity, $relations); |
|||
210 | } |
||||
211 | |||||
212 | 26 | return (int) $nb; |
|||
213 | } |
||||
214 | |||||
215 | /** |
||||
216 | * {@inheritdoc} |
||||
217 | */ |
||||
218 | #[WriteOperation] |
||||
219 | 25 | public function deleteAll($owner, array $relations = []): int |
|||
220 | { |
||||
221 | 25 | $entities = $this->getRelation($owner); |
|||
222 | |||||
223 | 25 | if (empty($entities)) { |
|||
224 | 2 | return 0; |
|||
225 | } |
||||
226 | |||||
227 | 24 | if (!is_array($entities)) { |
|||
228 | 19 | $entities = [$entities]; |
|||
229 | } |
||||
230 | |||||
231 | 24 | $nb = 0; |
|||
232 | |||||
233 | 24 | foreach ($entities as $entity) { |
|||
234 | 24 | $nb += $this->distant->deleteAll($entity, $relations); |
|||
235 | } |
||||
236 | |||||
237 | 24 | return (int) $nb; |
|||
238 | } |
||||
239 | |||||
240 | /** |
||||
241 | * Get the repository that owns the foreign key and the key name |
||||
242 | * |
||||
243 | * @return array{0:RepositoryInterface,1:string} |
||||
244 | */ |
||||
245 | abstract protected function getForeignInfos(): array; |
||||
246 | |||||
247 | /** |
||||
248 | * Get the query used to load relations |
||||
249 | * |
||||
250 | * @param array $keys The owner keys |
||||
251 | * @param array $constraints Constraints to apply on the query |
||||
252 | * |
||||
253 | * @return ReadCommandInterface |
||||
254 | */ |
||||
255 | abstract protected function relationQuery($keys, $constraints): ReadCommandInterface; |
||||
256 | |||||
257 | /** |
||||
258 | * Check if the entity is the foreign key barrier |
||||
259 | * |
||||
260 | * @param string|object $entity |
||||
261 | * |
||||
262 | * @return bool |
||||
263 | */ |
||||
264 | 161 | private function isForeignKeyBarrier($entity): bool |
|||
265 | { |
||||
266 | 161 | list($repository) = $this->getForeignInfos(); |
|||
267 | |||||
268 | 161 | if (!is_string($entity)) { |
|||
269 | 41 | $entity = get_class($entity); |
|||
270 | } |
||||
271 | |||||
272 | 161 | return $repository->entityClass() === $entity; |
|||
273 | } |
||||
274 | |||||
275 | /** |
||||
276 | * Set the foreign key value on an entity |
||||
277 | * |
||||
278 | * @param object $entity |
||||
279 | * @param mixed $id |
||||
280 | */ |
||||
281 | 49 | private function setForeignKeyValue($entity, $id): void |
|||
282 | { |
||||
283 | /** |
||||
284 | * @var RepositoryInterface $repository |
||||
285 | * @var string $key |
||||
286 | */ |
||||
287 | 49 | list($repository, $key) = $this->getForeignInfos(); |
|||
288 | |||||
289 | 49 | if ($repository->entityClass() === get_class($entity)) { |
|||
290 | 30 | $repository->mapper()->hydrateOne($entity, $key, $id); |
|||
291 | |||||
292 | 30 | if ($this->isPolymorphic()) { |
|||
293 | 11 | $repository->mapper()->hydrateOne($entity, $this->discriminator, $this->discriminatorValue); |
|||
294 | } |
||||
295 | } |
||||
296 | } |
||||
297 | } |
||||
298 |