PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.35.1
MailPoet – Newsletters, Email Marketing, and Automation v5.35.1
5.36.0 5.35.1 5.35.0 5.34.3 5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / vendor-prefixed / doctrine / orm / src / Persisters / Collection / ManyToManyPersister.php
mailpoet / vendor-prefixed / doctrine / orm / src / Persisters / Collection Last commit date
AbstractCollectionPersister.php 9 months ago CollectionPersister.php 9 months ago ManyToManyPersister.php 9 months ago OneToManyPersister.php 9 months ago index.php 9 months ago
ManyToManyPersister.php
437 lines
1 <?php
2 declare (strict_types=1);
3 namespace MailPoetVendor\Doctrine\ORM\Persisters\Collection;
4 if (!defined('ABSPATH')) exit;
5 use BadMethodCallException;
6 use MailPoetVendor\Doctrine\Common\Collections\Criteria;
7 use MailPoetVendor\Doctrine\Common\Collections\Expr\Comparison;
8 use MailPoetVendor\Doctrine\DBAL\Exception as DBALException;
9 use MailPoetVendor\Doctrine\ORM\Internal\CriteriaOrderings;
10 use MailPoetVendor\Doctrine\ORM\Mapping\ClassMetadata;
11 use MailPoetVendor\Doctrine\ORM\PersistentCollection;
12 use MailPoetVendor\Doctrine\ORM\Persisters\SqlValueVisitor;
13 use MailPoetVendor\Doctrine\ORM\Query;
14 use MailPoetVendor\Doctrine\ORM\Utility\PersisterHelper;
15 use function array_fill;
16 use function array_merge;
17 use function array_pop;
18 use function count;
19 use function get_class;
20 use function implode;
21 use function in_array;
22 use function reset;
23 use function sprintf;
24 class ManyToManyPersister extends AbstractCollectionPersister
25 {
26 use CriteriaOrderings;
27 public function delete(PersistentCollection $collection)
28 {
29 $mapping = $collection->getMapping();
30 if (!$mapping['isOwningSide']) {
31 return;
32 // ignore inverse side
33 }
34 $types = [];
35 $class = $this->em->getClassMetadata($mapping['sourceEntity']);
36 foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
37 $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em);
38 }
39 $this->conn->executeStatement($this->getDeleteSQL($collection), $this->getDeleteSQLParameters($collection), $types);
40 }
41 public function update(PersistentCollection $collection)
42 {
43 $mapping = $collection->getMapping();
44 if (!$mapping['isOwningSide']) {
45 return;
46 // ignore inverse side
47 }
48 [$deleteSql, $deleteTypes] = $this->getDeleteRowSQL($collection);
49 [$insertSql, $insertTypes] = $this->getInsertRowSQL($collection);
50 foreach ($collection->getDeleteDiff() as $element) {
51 $this->conn->executeStatement($deleteSql, $this->getDeleteRowSQLParameters($collection, $element), $deleteTypes);
52 }
53 foreach ($collection->getInsertDiff() as $element) {
54 $this->conn->executeStatement($insertSql, $this->getInsertRowSQLParameters($collection, $element), $insertTypes);
55 }
56 }
57 public function get(PersistentCollection $collection, $index)
58 {
59 $mapping = $collection->getMapping();
60 if (!isset($mapping['indexBy'])) {
61 throw new BadMethodCallException('Selecting a collection by index is only supported on indexed collections.');
62 }
63 $persister = $this->uow->getEntityPersister($mapping['targetEntity']);
64 $mappedKey = $mapping['isOwningSide'] ? $mapping['inversedBy'] : $mapping['mappedBy'];
65 return $persister->load([$mappedKey => $collection->getOwner(), $mapping['indexBy'] => $index], null, $mapping, [], 0, 1);
66 }
67 public function count(PersistentCollection $collection)
68 {
69 $conditions = [];
70 $params = [];
71 $types = [];
72 $mapping = $collection->getMapping();
73 $id = $this->uow->getEntityIdentifier($collection->getOwner());
74 $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
75 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
76 $association = !$mapping['isOwningSide'] ? $targetClass->associationMappings[$mapping['mappedBy']] : $mapping;
77 $joinTableName = $this->quoteStrategy->getJoinTableName($association, $sourceClass, $this->platform);
78 $joinColumns = !$mapping['isOwningSide'] ? $association['joinTable']['inverseJoinColumns'] : $association['joinTable']['joinColumns'];
79 foreach ($joinColumns as $joinColumn) {
80 $columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $sourceClass, $this->platform);
81 $referencedName = $joinColumn['referencedColumnName'];
82 $conditions[] = 't.' . $columnName . ' = ?';
83 $params[] = $id[$sourceClass->getFieldForColumn($referencedName)];
84 $types[] = PersisterHelper::getTypeOfColumn($referencedName, $sourceClass, $this->em);
85 }
86 [$joinTargetEntitySQL, $filterSql] = $this->getFilterSql($mapping);
87 if ($filterSql) {
88 $conditions[] = $filterSql;
89 }
90 // If there is a provided criteria, make part of conditions
91 // @todo Fix this. Current SQL returns something like:
92 $sql = 'SELECT COUNT(*)' . ' FROM ' . $joinTableName . ' t' . $joinTargetEntitySQL . ' WHERE ' . implode(' AND ', $conditions);
93 return (int) $this->conn->fetchOne($sql, $params, $types);
94 }
95 public function slice(PersistentCollection $collection, $offset, $length = null)
96 {
97 $mapping = $collection->getMapping();
98 $persister = $this->uow->getEntityPersister($mapping['targetEntity']);
99 return $persister->getManyToManyCollection($mapping, $collection->getOwner(), $offset, $length);
100 }
101 public function containsKey(PersistentCollection $collection, $key)
102 {
103 $mapping = $collection->getMapping();
104 if (!isset($mapping['indexBy'])) {
105 throw new BadMethodCallException('Selecting a collection by index is only supported on indexed collections.');
106 }
107 [$quotedJoinTable, $whereClauses, $params, $types] = $this->getJoinTableRestrictionsWithKey($collection, (string) $key, \true);
108 $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
109 return (bool) $this->conn->fetchOne($sql, $params, $types);
110 }
111 public function contains(PersistentCollection $collection, $element)
112 {
113 if (!$this->isValidEntityState($element)) {
114 return \false;
115 }
116 [$quotedJoinTable, $whereClauses, $params, $types] = $this->getJoinTableRestrictions($collection, $element, \true);
117 $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
118 return (bool) $this->conn->fetchOne($sql, $params, $types);
119 }
120 public function loadCriteria(PersistentCollection $collection, Criteria $criteria)
121 {
122 $mapping = $collection->getMapping();
123 $owner = $collection->getOwner();
124 $ownerMetadata = $this->em->getClassMetadata(get_class($owner));
125 $id = $this->uow->getEntityIdentifier($owner);
126 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
127 $onConditions = $this->getOnConditionSQL($mapping);
128 $whereClauses = $params = [];
129 $paramTypes = [];
130 if (!$mapping['isOwningSide']) {
131 $associationSourceClass = $targetClass;
132 $mapping = $targetClass->associationMappings[$mapping['mappedBy']];
133 $sourceRelationMode = 'relationToTargetKeyColumns';
134 } else {
135 $associationSourceClass = $ownerMetadata;
136 $sourceRelationMode = 'relationToSourceKeyColumns';
137 }
138 foreach ($mapping[$sourceRelationMode] as $key => $value) {
139 $whereClauses[] = sprintf('t.%s = ?', $key);
140 $params[] = $ownerMetadata->containsForeignIdentifier ? $id[$ownerMetadata->getFieldForColumn($value)] : $id[$ownerMetadata->fieldNames[$value]];
141 $paramTypes[] = PersisterHelper::getTypeOfColumn($value, $ownerMetadata, $this->em);
142 }
143 $parameters = $this->expandCriteriaParameters($criteria);
144 foreach ($parameters as $parameter) {
145 [$name, $value, $operator] = $parameter;
146 $field = $this->quoteStrategy->getColumnName($name, $targetClass, $this->platform);
147 if ($value === null && ($operator === Comparison::EQ || $operator === Comparison::NEQ)) {
148 $whereClauses[] = sprintf('te.%s %s NULL', $field, $operator === Comparison::EQ ? 'IS' : 'IS NOT');
149 } elseif ($operator === Comparison::IN || $operator === Comparison::NIN) {
150 $whereClauses[] = sprintf('te.%s %s (%s)', $field, $operator === Comparison::IN ? 'IN' : 'NOT IN', implode(', ', array_fill(0, count($value), '?')));
151 foreach ($value as $item) {
152 $params = array_merge($params, PersisterHelper::convertToParameterValue($item, $this->em));
153 $paramTypes = array_merge($paramTypes, PersisterHelper::inferParameterTypes($name, $item, $targetClass, $this->em));
154 }
155 } else {
156 $whereClauses[] = sprintf('te.%s %s ?', $field, $operator);
157 $params = array_merge($params, PersisterHelper::convertToParameterValue($value, $this->em));
158 $paramTypes = array_merge($paramTypes, PersisterHelper::inferParameterTypes($name, $value, $targetClass, $this->em));
159 }
160 }
161 $tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);
162 $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform);
163 $rsm = new Query\ResultSetMappingBuilder($this->em);
164 $rsm->addRootEntityFromClassMetadata($targetClass->name, 'te');
165 $sql = 'SELECT ' . $rsm->generateSelectClause() . ' FROM ' . $tableName . ' te' . ' JOIN ' . $joinTable . ' t ON' . implode(' AND ', $onConditions) . ' WHERE ' . implode(' AND ', $whereClauses);
166 $sql .= $this->getOrderingSql($criteria, $targetClass);
167 $sql .= $this->getLimitSql($criteria);
168 $stmt = $this->conn->executeQuery($sql, $params, $paramTypes);
169 return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm);
170 }
171 public function getFilterSql($mapping)
172 {
173 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
174 $rootClass = $this->em->getClassMetadata($targetClass->rootEntityName);
175 $filterSql = $this->generateFilterConditionSQL($rootClass, 'te');
176 if ($filterSql === '') {
177 return ['', ''];
178 }
179 // A join is needed if there is filtering on the target entity
180 $tableName = $this->quoteStrategy->getTableName($rootClass, $this->platform);
181 $joinSql = ' JOIN ' . $tableName . ' te' . ' ON' . implode(' AND ', $this->getOnConditionSQL($mapping));
182 return [$joinSql, $filterSql];
183 }
184 protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
185 {
186 $filterClauses = [];
187 foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
188 $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias);
189 if ($filterExpr) {
190 $filterClauses[] = '(' . $filterExpr . ')';
191 }
192 }
193 return $filterClauses ? '(' . implode(' AND ', $filterClauses) . ')' : '';
194 }
195 protected function getOnConditionSQL($mapping)
196 {
197 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
198 $association = !$mapping['isOwningSide'] ? $targetClass->associationMappings[$mapping['mappedBy']] : $mapping;
199 $joinColumns = $mapping['isOwningSide'] ? $association['joinTable']['inverseJoinColumns'] : $association['joinTable']['joinColumns'];
200 $conditions = [];
201 foreach ($joinColumns as $joinColumn) {
202 $joinColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
203 $refColumnName = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
204 $conditions[] = ' t.' . $joinColumnName . ' = te.' . $refColumnName;
205 }
206 return $conditions;
207 }
208 protected function getDeleteSQL(PersistentCollection $collection)
209 {
210 $columns = [];
211 $mapping = $collection->getMapping();
212 $class = $this->em->getClassMetadata(get_class($collection->getOwner()));
213 $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform);
214 foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
215 $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
216 }
217 return 'DELETE FROM ' . $joinTable . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
218 }
219 protected function getDeleteSQLParameters(PersistentCollection $collection)
220 {
221 $mapping = $collection->getMapping();
222 $identifier = $this->uow->getEntityIdentifier($collection->getOwner());
223 // Optimization for single column identifier
224 if (count($mapping['relationToSourceKeyColumns']) === 1) {
225 return [reset($identifier)];
226 }
227 // Composite identifier
228 $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
229 $params = [];
230 foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) {
231 $params[] = isset($sourceClass->fieldNames[$refColumnName]) ? $identifier[$sourceClass->fieldNames[$refColumnName]] : $identifier[$sourceClass->getFieldForColumn($refColumnName)];
232 }
233 return $params;
234 }
235 protected function getDeleteRowSQL(PersistentCollection $collection)
236 {
237 $mapping = $collection->getMapping();
238 $class = $this->em->getClassMetadata($mapping['sourceEntity']);
239 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
240 $columns = [];
241 $types = [];
242 foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
243 $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
244 $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em);
245 }
246 foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
247 $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
248 $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
249 }
250 return ['DELETE FROM ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform) . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?', $types];
251 }
252 protected function getDeleteRowSQLParameters(PersistentCollection $collection, $element)
253 {
254 return $this->collectJoinTableColumnParameters($collection, $element);
255 }
256 protected function getInsertRowSQL(PersistentCollection $collection)
257 {
258 $columns = [];
259 $types = [];
260 $mapping = $collection->getMapping();
261 $class = $this->em->getClassMetadata($mapping['sourceEntity']);
262 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
263 foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
264 $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
265 $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em);
266 }
267 foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
268 $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
269 $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
270 }
271 return ['INSERT INTO ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform) . ' (' . implode(', ', $columns) . ')' . ' VALUES' . ' (' . implode(', ', array_fill(0, count($columns), '?')) . ')', $types];
272 }
273 protected function getInsertRowSQLParameters(PersistentCollection $collection, $element)
274 {
275 return $this->collectJoinTableColumnParameters($collection, $element);
276 }
277 private function collectJoinTableColumnParameters(PersistentCollection $collection, $element) : array
278 {
279 $params = [];
280 $mapping = $collection->getMapping();
281 $isComposite = count($mapping['joinTableColumns']) > 2;
282 $identifier1 = $this->uow->getEntityIdentifier($collection->getOwner());
283 $identifier2 = $this->uow->getEntityIdentifier($element);
284 $class1 = $class2 = null;
285 if ($isComposite) {
286 $class1 = $this->em->getClassMetadata(get_class($collection->getOwner()));
287 $class2 = $collection->getTypeClass();
288 }
289 foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
290 $isRelationToSource = isset($mapping['relationToSourceKeyColumns'][$joinTableColumn]);
291 if (!$isComposite) {
292 $params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2);
293 continue;
294 }
295 if ($isRelationToSource) {
296 $params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
297 continue;
298 }
299 $params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
300 }
301 return $params;
302 }
303 private function getJoinTableRestrictionsWithKey(PersistentCollection $collection, string $key, bool $addFilters) : array
304 {
305 $filterMapping = $collection->getMapping();
306 $mapping = $filterMapping;
307 $indexBy = $mapping['indexBy'];
308 $id = $this->uow->getEntityIdentifier($collection->getOwner());
309 $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
310 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
311 if (!$mapping['isOwningSide']) {
312 $associationSourceClass = $this->em->getClassMetadata($mapping['targetEntity']);
313 $mapping = $associationSourceClass->associationMappings[$mapping['mappedBy']];
314 $joinColumns = $mapping['joinTable']['joinColumns'];
315 $sourceRelationMode = 'relationToTargetKeyColumns';
316 $targetRelationMode = 'relationToSourceKeyColumns';
317 } else {
318 $associationSourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
319 $joinColumns = $mapping['joinTable']['inverseJoinColumns'];
320 $sourceRelationMode = 'relationToSourceKeyColumns';
321 $targetRelationMode = 'relationToTargetKeyColumns';
322 }
323 $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform) . ' t';
324 $whereClauses = [];
325 $params = [];
326 $types = [];
327 $joinNeeded = !in_array($indexBy, $targetClass->identifier, \true);
328 if ($joinNeeded) {
329 // extra join needed if indexBy is not a @id
330 $joinConditions = [];
331 foreach ($joinColumns as $joinTableColumn) {
332 $joinConditions[] = 't.' . $joinTableColumn['name'] . ' = tr.' . $joinTableColumn['referencedColumnName'];
333 }
334 $tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);
335 $quotedJoinTable .= ' JOIN ' . $tableName . ' tr ON ' . implode(' AND ', $joinConditions);
336 $columnName = $targetClass->getColumnName($indexBy);
337 $whereClauses[] = 'tr.' . $columnName . ' = ?';
338 $params[] = $key;
339 $types[] = PersisterHelper::getTypeOfColumn($columnName, $targetClass, $this->em);
340 }
341 foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
342 if (isset($mapping[$sourceRelationMode][$joinTableColumn])) {
343 $column = $mapping[$sourceRelationMode][$joinTableColumn];
344 $whereClauses[] = 't.' . $joinTableColumn . ' = ?';
345 $params[] = $sourceClass->containsForeignIdentifier ? $id[$sourceClass->getFieldForColumn($column)] : $id[$sourceClass->fieldNames[$column]];
346 $types[] = PersisterHelper::getTypeOfColumn($column, $sourceClass, $this->em);
347 } elseif (!$joinNeeded) {
348 $column = $mapping[$targetRelationMode][$joinTableColumn];
349 $whereClauses[] = 't.' . $joinTableColumn . ' = ?';
350 $params[] = $key;
351 $types[] = PersisterHelper::getTypeOfColumn($column, $targetClass, $this->em);
352 }
353 }
354 if ($addFilters) {
355 [$joinTargetEntitySQL, $filterSql] = $this->getFilterSql($filterMapping);
356 if ($filterSql) {
357 $quotedJoinTable .= ' ' . $joinTargetEntitySQL;
358 $whereClauses[] = $filterSql;
359 }
360 }
361 return [$quotedJoinTable, $whereClauses, $params, $types];
362 }
363 private function getJoinTableRestrictions(PersistentCollection $collection, $element, bool $addFilters) : array
364 {
365 $filterMapping = $collection->getMapping();
366 $mapping = $filterMapping;
367 if (!$mapping['isOwningSide']) {
368 $sourceClass = $this->em->getClassMetadata($mapping['targetEntity']);
369 $targetClass = $this->em->getClassMetadata($mapping['sourceEntity']);
370 $sourceId = $this->uow->getEntityIdentifier($element);
371 $targetId = $this->uow->getEntityIdentifier($collection->getOwner());
372 $mapping = $sourceClass->associationMappings[$mapping['mappedBy']];
373 } else {
374 $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
375 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
376 $sourceId = $this->uow->getEntityIdentifier($collection->getOwner());
377 $targetId = $this->uow->getEntityIdentifier($element);
378 }
379 $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform);
380 $whereClauses = [];
381 $params = [];
382 $types = [];
383 foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
384 $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?';
385 if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
386 $targetColumn = $mapping['relationToTargetKeyColumns'][$joinTableColumn];
387 $params[] = $targetId[$targetClass->getFieldForColumn($targetColumn)];
388 $types[] = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em);
389 continue;
390 }
391 // relationToSourceKeyColumns
392 $targetColumn = $mapping['relationToSourceKeyColumns'][$joinTableColumn];
393 $params[] = $sourceId[$sourceClass->getFieldForColumn($targetColumn)];
394 $types[] = PersisterHelper::getTypeOfColumn($targetColumn, $sourceClass, $this->em);
395 }
396 if ($addFilters) {
397 $quotedJoinTable .= ' t';
398 [$joinTargetEntitySQL, $filterSql] = $this->getFilterSql($filterMapping);
399 if ($filterSql) {
400 $quotedJoinTable .= ' ' . $joinTargetEntitySQL;
401 $whereClauses[] = $filterSql;
402 }
403 }
404 return [$quotedJoinTable, $whereClauses, $params, $types];
405 }
406 private function expandCriteriaParameters(Criteria $criteria) : array
407 {
408 $expression = $criteria->getWhereExpression();
409 if ($expression === null) {
410 return [];
411 }
412 $valueVisitor = new SqlValueVisitor();
413 $valueVisitor->dispatch($expression);
414 [, $types] = $valueVisitor->getParamsAndTypes();
415 return $types;
416 }
417 private function getOrderingSql(Criteria $criteria, ClassMetadata $targetClass) : string
418 {
419 $orderings = self::getCriteriaOrderings($criteria);
420 if ($orderings) {
421 $orderBy = [];
422 foreach ($orderings as $name => $direction) {
423 $field = $this->quoteStrategy->getColumnName($name, $targetClass, $this->platform);
424 $orderBy[] = $field . ' ' . $direction;
425 }
426 return ' ORDER BY ' . implode(', ', $orderBy);
427 }
428 return '';
429 }
430 private function getLimitSql(Criteria $criteria) : string
431 {
432 $limit = $criteria->getMaxResults();
433 $offset = $criteria->getFirstResult();
434 return $this->platform->modifyLimitQuery('', $limit, $offset ?? 0);
435 }
436 }
437