summaryrefslogtreecommitdiff
path: root/vendor/doctrine/orm/src/Persisters/Collection/OneToManyPersister.php
diff options
context:
space:
mode:
authorpolo <ordipolo@gmx.fr>2024-08-13 23:45:21 +0200
committerpolo <ordipolo@gmx.fr>2024-08-13 23:45:21 +0200
commitbf6655a534a6775d30cafa67bd801276bda1d98d (patch)
treec6381e3f6c81c33eab72508f410b165ba05f7e9c /vendor/doctrine/orm/src/Persisters/Collection/OneToManyPersister.php
parent94d67a4b51f8e62e7d518cce26a526ae1ec48278 (diff)
downloadAppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.zip
VERSION 0.2 doctrine ORM et entités
Diffstat (limited to 'vendor/doctrine/orm/src/Persisters/Collection/OneToManyPersister.php')
-rw-r--r--vendor/doctrine/orm/src/Persisters/Collection/OneToManyPersister.php264
1 files changed, 264 insertions, 0 deletions
diff --git a/vendor/doctrine/orm/src/Persisters/Collection/OneToManyPersister.php b/vendor/doctrine/orm/src/Persisters/Collection/OneToManyPersister.php
new file mode 100644
index 0000000..0727b1f
--- /dev/null
+++ b/vendor/doctrine/orm/src/Persisters/Collection/OneToManyPersister.php
@@ -0,0 +1,264 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Persisters\Collection;
6
7use BadMethodCallException;
8use Doctrine\Common\Collections\Criteria;
9use Doctrine\DBAL\Exception as DBALException;
10use Doctrine\DBAL\Types\Type;
11use Doctrine\ORM\EntityNotFoundException;
12use Doctrine\ORM\Mapping\MappingException;
13use Doctrine\ORM\Mapping\OneToManyAssociationMapping;
14use Doctrine\ORM\PersistentCollection;
15use Doctrine\ORM\Utility\PersisterHelper;
16
17use function array_reverse;
18use function array_values;
19use function assert;
20use function implode;
21use function is_int;
22use function is_string;
23
24/**
25 * Persister for one-to-many collections.
26 */
27class OneToManyPersister extends AbstractCollectionPersister
28{
29 public function delete(PersistentCollection $collection): void
30 {
31 // The only valid case here is when you have weak entities. In this
32 // scenario, you have @OneToMany with orphanRemoval=true, and replacing
33 // the entire collection with a new would trigger this operation.
34 $mapping = $this->getMapping($collection);
35
36 if (! $mapping->orphanRemoval) {
37 // Handling non-orphan removal should never happen, as @OneToMany
38 // can only be inverse side. For owning side one to many, it is
39 // required to have a join table, which would classify as a ManyToManyPersister.
40 return;
41 }
42
43 $targetClass = $this->em->getClassMetadata($mapping->targetEntity);
44
45 $targetClass->isInheritanceTypeJoined()
46 ? $this->deleteJoinedEntityCollection($collection)
47 : $this->deleteEntityCollection($collection);
48 }
49
50 public function update(PersistentCollection $collection): void
51 {
52 // This can never happen. One to many can only be inverse side.
53 // For owning side one to many, it is required to have a join table,
54 // then classifying it as a ManyToManyPersister.
55 return;
56 }
57
58 public function get(PersistentCollection $collection, mixed $index): object|null
59 {
60 $mapping = $this->getMapping($collection);
61
62 if (! $mapping->isIndexed()) {
63 throw new BadMethodCallException('Selecting a collection by index is only supported on indexed collections.');
64 }
65
66 $persister = $this->uow->getEntityPersister($mapping->targetEntity);
67
68 return $persister->load(
69 [
70 $mapping->mappedBy => $collection->getOwner(),
71 $mapping->indexBy() => $index,
72 ],
73 null,
74 $mapping,
75 [],
76 null,
77 1,
78 );
79 }
80
81 public function count(PersistentCollection $collection): int
82 {
83 $mapping = $this->getMapping($collection);
84 $persister = $this->uow->getEntityPersister($mapping->targetEntity);
85
86 // only works with single id identifier entities. Will throw an
87 // exception in Entity Persisters if that is not the case for the
88 // 'mappedBy' field.
89 $criteria = new Criteria(Criteria::expr()->eq($mapping->mappedBy, $collection->getOwner()));
90
91 return $persister->count($criteria);
92 }
93
94 /**
95 * {@inheritDoc}
96 */
97 public function slice(PersistentCollection $collection, int $offset, int|null $length = null): array
98 {
99 $mapping = $this->getMapping($collection);
100 $persister = $this->uow->getEntityPersister($mapping->targetEntity);
101
102 return $persister->getOneToManyCollection($mapping, $collection->getOwner(), $offset, $length);
103 }
104
105 public function containsKey(PersistentCollection $collection, mixed $key): bool
106 {
107 $mapping = $this->getMapping($collection);
108
109 if (! $mapping->isIndexed()) {
110 throw new BadMethodCallException('Selecting a collection by index is only supported on indexed collections.');
111 }
112
113 $persister = $this->uow->getEntityPersister($mapping->targetEntity);
114
115 // only works with single id identifier entities. Will throw an
116 // exception in Entity Persisters if that is not the case for the
117 // 'mappedBy' field.
118 $criteria = new Criteria();
119
120 $criteria->andWhere(Criteria::expr()->eq($mapping->mappedBy, $collection->getOwner()));
121 $criteria->andWhere(Criteria::expr()->eq($mapping->indexBy(), $key));
122
123 return (bool) $persister->count($criteria);
124 }
125
126 public function contains(PersistentCollection $collection, object $element): bool
127 {
128 if (! $this->isValidEntityState($element)) {
129 return false;
130 }
131
132 $mapping = $this->getMapping($collection);
133 $persister = $this->uow->getEntityPersister($mapping->targetEntity);
134
135 // only works with single id identifier entities. Will throw an
136 // exception in Entity Persisters if that is not the case for the
137 // 'mappedBy' field.
138 $criteria = new Criteria(Criteria::expr()->eq($mapping->mappedBy, $collection->getOwner()));
139
140 return $persister->exists($element, $criteria);
141 }
142
143 /**
144 * {@inheritDoc}
145 */
146 public function loadCriteria(PersistentCollection $collection, Criteria $criteria): array
147 {
148 throw new BadMethodCallException('Filtering a collection by Criteria is not supported by this CollectionPersister.');
149 }
150
151 /**
152 * @throws DBALException
153 * @throws EntityNotFoundException
154 * @throws MappingException
155 */
156 private function deleteEntityCollection(PersistentCollection $collection): int
157 {
158 $mapping = $this->getMapping($collection);
159 $identifier = $this->uow->getEntityIdentifier($collection->getOwner());
160 $sourceClass = $this->em->getClassMetadata($mapping->sourceEntity);
161 $targetClass = $this->em->getClassMetadata($mapping->targetEntity);
162 $columns = [];
163 $parameters = [];
164 $types = [];
165
166 foreach ($this->em->getMetadataFactory()->getOwningSide($mapping)->joinColumns as $joinColumn) {
167 $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
168 $parameters[] = $identifier[$sourceClass->getFieldForColumn($joinColumn->referencedColumnName)];
169 $types[] = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $sourceClass, $this->em);
170 }
171
172 $statement = 'DELETE FROM ' . $this->quoteStrategy->getTableName($targetClass, $this->platform)
173 . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
174
175 if ($targetClass->isInheritanceTypeSingleTable()) {
176 $discriminatorColumn = $targetClass->getDiscriminatorColumn();
177 $statement .= ' AND ' . $discriminatorColumn->name . ' = ?';
178 $parameters[] = $targetClass->discriminatorValue;
179 $types[] = $discriminatorColumn->type;
180 }
181
182 $numAffected = $this->conn->executeStatement($statement, $parameters, $types);
183
184 assert(is_int($numAffected));
185
186 return $numAffected;
187 }
188
189 /**
190 * Delete Class Table Inheritance entities.
191 * A temporary table is needed to keep IDs to be deleted in both parent and child class' tables.
192 *
193 * Thanks Steve Ebersole (Hibernate) for idea on how to tackle reliably this scenario, we owe him a beer! =)
194 *
195 * @throws DBALException
196 */
197 private function deleteJoinedEntityCollection(PersistentCollection $collection): int
198 {
199 $mapping = $this->getMapping($collection);
200 $sourceClass = $this->em->getClassMetadata($mapping->sourceEntity);
201 $targetClass = $this->em->getClassMetadata($mapping->targetEntity);
202 $rootClass = $this->em->getClassMetadata($targetClass->rootEntityName);
203
204 // 1) Build temporary table DDL
205 $tempTable = $this->platform->getTemporaryTableName($rootClass->getTemporaryIdTableName());
206 $idColumnNames = $rootClass->getIdentifierColumnNames();
207 $idColumnList = implode(', ', $idColumnNames);
208 $columnDefinitions = [];
209
210 foreach ($idColumnNames as $idColumnName) {
211 $columnDefinitions[$idColumnName] = [
212 'name' => $idColumnName,
213 'notnull' => true,
214 'type' => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $this->em)),
215 ];
216 }
217
218 $statement = $this->platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable
219 . ' (' . $this->platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
220
221 $this->conn->executeStatement($statement);
222
223 // 2) Build insert table records into temporary table
224 $query = $this->em->createQuery(
225 ' SELECT t0.' . implode(', t0.', $rootClass->getIdentifierFieldNames())
226 . ' FROM ' . $targetClass->name . ' t0 WHERE t0.' . $mapping->mappedBy . ' = :owner',
227 )->setParameter('owner', $collection->getOwner());
228
229 $sql = $query->getSQL();
230 assert(is_string($sql));
231 $statement = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ') ' . $sql;
232 $parameters = array_values($sourceClass->getIdentifierValues($collection->getOwner()));
233 $numDeleted = $this->conn->executeStatement($statement, $parameters);
234
235 // 3) Delete records on each table in the hierarchy
236 $classNames = [...$targetClass->parentClasses, ...[$targetClass->name], ...$targetClass->subClasses];
237
238 foreach (array_reverse($classNames) as $className) {
239 $tableName = $this->quoteStrategy->getTableName($this->em->getClassMetadata($className), $this->platform);
240 $statement = 'DELETE FROM ' . $tableName . ' WHERE (' . $idColumnList . ')'
241 . ' IN (SELECT ' . $idColumnList . ' FROM ' . $tempTable . ')';
242
243 $this->conn->executeStatement($statement);
244 }
245
246 // 4) Drop temporary table
247 $statement = $this->platform->getDropTemporaryTableSQL($tempTable);
248
249 $this->conn->executeStatement($statement);
250
251 assert(is_int($numDeleted));
252
253 return $numDeleted;
254 }
255
256 private function getMapping(PersistentCollection $collection): OneToManyAssociationMapping
257 {
258 $mapping = $collection->getMapping();
259
260 assert($mapping->isOneToMany());
261
262 return $mapping;
263 }
264}