These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /* |
||
3 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||
4 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||
5 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||
6 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||
7 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||
8 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||
9 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||
10 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||
11 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
12 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||
13 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
14 | * |
||
15 | * This software consists of voluntary contributions made by many individuals |
||
16 | * and is licensed under the MIT license. For more information, see |
||
17 | * <http://www.doctrine-project.org>. |
||
18 | */ |
||
19 | |||
20 | namespace Doctrine\ORM\Persisters\Entity; |
||
21 | |||
22 | use Doctrine\Common\Collections\Criteria; |
||
23 | use Doctrine\Common\Collections\Expr\Comparison; |
||
24 | use Doctrine\Common\Util\ClassUtils; |
||
25 | use Doctrine\DBAL\Connection; |
||
26 | use Doctrine\DBAL\LockMode; |
||
27 | use Doctrine\DBAL\Types\Type; |
||
28 | use Doctrine\ORM\EntityManagerInterface; |
||
29 | use Doctrine\ORM\Mapping\ClassMetadata; |
||
30 | use Doctrine\ORM\Mapping\MappingException; |
||
31 | use Doctrine\ORM\OptimisticLockException; |
||
32 | use Doctrine\ORM\ORMException; |
||
33 | use Doctrine\ORM\PersistentCollection; |
||
34 | use Doctrine\ORM\Persisters\SqlExpressionVisitor; |
||
35 | use Doctrine\ORM\Persisters\SqlValueVisitor; |
||
36 | use Doctrine\ORM\Query; |
||
37 | use Doctrine\ORM\UnitOfWork; |
||
38 | use Doctrine\ORM\Utility\IdentifierFlattener; |
||
39 | use Doctrine\ORM\Utility\PersisterHelper; |
||
40 | |||
41 | /** |
||
42 | * A BasicEntityPersister maps an entity to a single table in a relational database. |
||
43 | * |
||
44 | * A persister is always responsible for a single entity type. |
||
45 | * |
||
46 | * EntityPersisters are used during a UnitOfWork to apply any changes to the persistent |
||
47 | * state of entities onto a relational database when the UnitOfWork is committed, |
||
48 | * as well as for basic querying of entities and their associations (not DQL). |
||
49 | * |
||
50 | * The persisting operations that are invoked during a commit of a UnitOfWork to |
||
51 | * persist the persistent entity state are: |
||
52 | * |
||
53 | * - {@link addInsert} : To schedule an entity for insertion. |
||
54 | * - {@link executeInserts} : To execute all scheduled insertions. |
||
55 | * - {@link update} : To update the persistent state of an entity. |
||
56 | * - {@link delete} : To delete the persistent state of an entity. |
||
57 | * |
||
58 | * As can be seen from the above list, insertions are batched and executed all at once |
||
59 | * for increased efficiency. |
||
60 | * |
||
61 | * The querying operations invoked during a UnitOfWork, either through direct find |
||
62 | * requests or lazy-loading, are the following: |
||
63 | * |
||
64 | * - {@link load} : Loads (the state of) a single, managed entity. |
||
65 | * - {@link loadAll} : Loads multiple, managed entities. |
||
66 | * - {@link loadOneToOneEntity} : Loads a one/many-to-one entity association (lazy-loading). |
||
67 | * - {@link loadOneToManyCollection} : Loads a one-to-many entity association (lazy-loading). |
||
68 | * - {@link loadManyToManyCollection} : Loads a many-to-many entity association (lazy-loading). |
||
69 | * |
||
70 | * The BasicEntityPersister implementation provides the default behavior for |
||
71 | * persisting and querying entities that are mapped to a single database table. |
||
72 | * |
||
73 | * Subclasses can be created to provide custom persisting and querying strategies, |
||
74 | * i.e. spanning multiple tables. |
||
75 | * |
||
76 | * @author Roman Borschel <[email protected]> |
||
77 | * @author Giorgio Sironi <[email protected]> |
||
78 | * @author Benjamin Eberlei <[email protected]> |
||
79 | * @author Alexander <[email protected]> |
||
80 | * @author Fabio B. Silva <[email protected]> |
||
81 | * @author Rob Caiger <[email protected]> |
||
82 | * @since 2.0 |
||
83 | */ |
||
84 | class BasicEntityPersister implements EntityPersister |
||
85 | { |
||
86 | /** |
||
87 | * @var array |
||
88 | */ |
||
89 | static private $comparisonMap = [ |
||
90 | Comparison::EQ => '= %s', |
||
91 | Comparison::IS => '= %s', |
||
92 | Comparison::NEQ => '!= %s', |
||
93 | Comparison::GT => '> %s', |
||
94 | Comparison::GTE => '>= %s', |
||
95 | Comparison::LT => '< %s', |
||
96 | Comparison::LTE => '<= %s', |
||
97 | Comparison::IN => 'IN (%s)', |
||
98 | Comparison::NIN => 'NOT IN (%s)', |
||
99 | Comparison::CONTAINS => 'LIKE %s', |
||
100 | Comparison::STARTS_WITH => 'LIKE %s', |
||
101 | Comparison::ENDS_WITH => 'LIKE %s', |
||
102 | ]; |
||
103 | |||
104 | /** |
||
105 | * Metadata object that describes the mapping of the mapped entity class. |
||
106 | * |
||
107 | * @var \Doctrine\ORM\Mapping\ClassMetadata |
||
108 | */ |
||
109 | protected $class; |
||
110 | |||
111 | /** |
||
112 | * The underlying DBAL Connection of the used EntityManager. |
||
113 | * |
||
114 | * @var \Doctrine\DBAL\Connection $conn |
||
115 | */ |
||
116 | protected $conn; |
||
117 | |||
118 | /** |
||
119 | * The database platform. |
||
120 | * |
||
121 | * @var \Doctrine\DBAL\Platforms\AbstractPlatform |
||
122 | */ |
||
123 | protected $platform; |
||
124 | |||
125 | /** |
||
126 | * The EntityManager instance. |
||
127 | * |
||
128 | * @var EntityManagerInterface |
||
129 | */ |
||
130 | protected $em; |
||
131 | |||
132 | /** |
||
133 | * Queued inserts. |
||
134 | * |
||
135 | * @var array |
||
136 | */ |
||
137 | protected $queuedInserts = []; |
||
138 | |||
139 | /** |
||
140 | * The map of column names to DBAL mapping types of all prepared columns used |
||
141 | * when INSERTing or UPDATEing an entity. |
||
142 | * |
||
143 | * @var array |
||
144 | * |
||
145 | * @see prepareInsertData($entity) |
||
146 | * @see prepareUpdateData($entity) |
||
147 | */ |
||
148 | protected $columnTypes = []; |
||
149 | |||
150 | /** |
||
151 | * The map of quoted column names. |
||
152 | * |
||
153 | * @var array |
||
154 | * |
||
155 | * @see prepareInsertData($entity) |
||
156 | * @see prepareUpdateData($entity) |
||
157 | */ |
||
158 | protected $quotedColumns = []; |
||
159 | |||
160 | /** |
||
161 | * The INSERT SQL statement used for entities handled by this persister. |
||
162 | * This SQL is only generated once per request, if at all. |
||
163 | * |
||
164 | * @var string |
||
165 | */ |
||
166 | private $insertSql; |
||
167 | |||
168 | /** |
||
169 | * The quote strategy. |
||
170 | * |
||
171 | * @var \Doctrine\ORM\Mapping\QuoteStrategy |
||
172 | */ |
||
173 | protected $quoteStrategy; |
||
174 | |||
175 | /** |
||
176 | * The IdentifierFlattener used for manipulating identifiers |
||
177 | * |
||
178 | * @var \Doctrine\ORM\Utility\IdentifierFlattener |
||
179 | */ |
||
180 | private $identifierFlattener; |
||
181 | |||
182 | /** |
||
183 | * @var CachedPersisterContext |
||
184 | */ |
||
185 | protected $currentPersisterContext; |
||
186 | |||
187 | /** |
||
188 | * @var CachedPersisterContext |
||
189 | */ |
||
190 | private $limitsHandlingContext; |
||
191 | |||
192 | /** |
||
193 | * @var CachedPersisterContext |
||
194 | */ |
||
195 | private $noLimitsContext; |
||
196 | |||
197 | /** |
||
198 | * Initializes a new <tt>BasicEntityPersister</tt> that uses the given EntityManager |
||
199 | * and persists instances of the class described by the given ClassMetadata descriptor. |
||
200 | * |
||
201 | * @param EntityManagerInterface $em |
||
202 | * @param ClassMetadata $class |
||
203 | */ |
||
204 | 1136 | public function __construct(EntityManagerInterface $em, ClassMetadata $class) |
|
205 | { |
||
206 | 1136 | $this->em = $em; |
|
207 | 1136 | $this->class = $class; |
|
208 | 1136 | $this->conn = $em->getConnection(); |
|
209 | 1136 | $this->platform = $this->conn->getDatabasePlatform(); |
|
210 | 1136 | $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); |
|
211 | 1136 | $this->identifierFlattener = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory()); |
|
212 | 1136 | $this->noLimitsContext = $this->currentPersisterContext = new CachedPersisterContext( |
|
213 | 1136 | $class, |
|
214 | 1136 | new Query\ResultSetMapping(), |
|
215 | 1136 | false |
|
216 | ); |
||
217 | 1136 | $this->limitsHandlingContext = new CachedPersisterContext( |
|
218 | 1136 | $class, |
|
219 | 1136 | new Query\ResultSetMapping(), |
|
220 | 1136 | true |
|
221 | ); |
||
222 | 1136 | } |
|
223 | |||
224 | /** |
||
225 | * {@inheritdoc} |
||
226 | */ |
||
227 | 17 | public function getClassMetadata() |
|
228 | { |
||
229 | 17 | return $this->class; |
|
230 | } |
||
231 | |||
232 | /** |
||
233 | * {@inheritdoc} |
||
234 | */ |
||
235 | 10 | public function getResultSetMapping() |
|
236 | { |
||
237 | 10 | return $this->currentPersisterContext->rsm; |
|
238 | } |
||
239 | |||
240 | /** |
||
241 | * {@inheritdoc} |
||
242 | */ |
||
243 | 1022 | public function addInsert($entity) |
|
244 | { |
||
245 | 1022 | $this->queuedInserts[spl_object_hash($entity)] = $entity; |
|
246 | 1022 | } |
|
247 | |||
248 | /** |
||
249 | * {@inheritdoc} |
||
250 | */ |
||
251 | 93 | public function getInserts() |
|
252 | { |
||
253 | 93 | return $this->queuedInserts; |
|
254 | } |
||
255 | |||
256 | /** |
||
257 | * {@inheritdoc} |
||
258 | */ |
||
259 | 1003 | public function executeInserts() |
|
260 | { |
||
261 | 1003 | if ( ! $this->queuedInserts) { |
|
0 ignored issues
–
show
|
|||
262 | 640 | return []; |
|
263 | } |
||
264 | |||
265 | 954 | $postInsertIds = []; |
|
266 | 954 | $idGenerator = $this->class->idGenerator; |
|
267 | 954 | $isPostInsertId = $idGenerator->isPostInsertGenerator(); |
|
268 | |||
269 | 954 | $stmt = $this->conn->prepare($this->getInsertSQL()); |
|
270 | 954 | $tableName = $this->class->getTableName(); |
|
271 | |||
272 | 954 | foreach ($this->queuedInserts as $entity) { |
|
273 | 954 | $insertData = $this->prepareInsertData($entity); |
|
274 | |||
275 | 954 | if (isset($insertData[$tableName])) { |
|
276 | 927 | $paramIndex = 1; |
|
277 | |||
278 | 927 | foreach ($insertData[$tableName] as $column => $value) { |
|
279 | 927 | $stmt->bindValue($paramIndex++, $value, $this->columnTypes[$column]); |
|
280 | } |
||
281 | } |
||
282 | |||
283 | 954 | $stmt->execute(); |
|
284 | |||
285 | 953 | View Code Duplication | if ($isPostInsertId) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
286 | 865 | $generatedId = $idGenerator->generate($this->em, $entity); |
|
0 ignored issues
–
show
$this->em of type object<Doctrine\ORM\EntityManagerInterface> is not a sub-type of object<Doctrine\ORM\EntityManager> . It seems like you assume a concrete implementation of the interface Doctrine\ORM\EntityManagerInterface to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
287 | $id = [ |
||
288 | 865 | $this->class->identifier[0] => $generatedId |
|
289 | ]; |
||
290 | 865 | $postInsertIds[] = [ |
|
291 | 865 | 'generatedId' => $generatedId, |
|
292 | 865 | 'entity' => $entity, |
|
293 | ]; |
||
294 | } else { |
||
295 | 261 | $id = $this->class->getIdentifierValues($entity); |
|
296 | } |
||
297 | |||
298 | 953 | if ($this->class->isVersioned) { |
|
299 | 953 | $this->assignDefaultVersionValue($entity, $id); |
|
300 | } |
||
301 | } |
||
302 | |||
303 | 953 | $stmt->closeCursor(); |
|
304 | 953 | $this->queuedInserts = []; |
|
305 | |||
306 | 953 | return $postInsertIds; |
|
307 | } |
||
308 | |||
309 | /** |
||
310 | * Retrieves the default version value which was created |
||
311 | * by the preceding INSERT statement and assigns it back in to the |
||
312 | * entities version field. |
||
313 | * |
||
314 | * @param object $entity |
||
315 | * @param array $id |
||
316 | * |
||
317 | * @return void |
||
318 | */ |
||
319 | 200 | protected function assignDefaultVersionValue($entity, array $id) |
|
320 | { |
||
321 | 200 | $value = $this->fetchVersionValue($this->class, $id); |
|
322 | |||
323 | 200 | $this->class->setFieldValue($entity, $this->class->versionField, $value); |
|
324 | 200 | } |
|
325 | |||
326 | /** |
||
327 | * Fetches the current version value of a versioned entity. |
||
328 | * |
||
329 | * @param \Doctrine\ORM\Mapping\ClassMetadata $versionedClass |
||
330 | * @param array $id |
||
331 | * |
||
332 | * @return mixed |
||
333 | */ |
||
334 | 209 | protected function fetchVersionValue($versionedClass, array $id) |
|
335 | { |
||
336 | 209 | $versionField = $versionedClass->versionField; |
|
337 | 209 | $fieldMapping = $versionedClass->fieldMappings[$versionField]; |
|
338 | 209 | $tableName = $this->quoteStrategy->getTableName($versionedClass, $this->platform); |
|
339 | 209 | $identifier = $this->quoteStrategy->getIdentifierColumnNames($versionedClass, $this->platform); |
|
340 | 209 | $columnName = $this->quoteStrategy->getColumnName($versionField, $versionedClass, $this->platform); |
|
341 | |||
342 | // FIXME: Order with composite keys might not be correct |
||
343 | 209 | $sql = 'SELECT ' . $columnName |
|
344 | 209 | . ' FROM ' . $tableName |
|
345 | 209 | . ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?'; |
|
346 | |||
347 | |||
348 | 209 | $flatId = $this->identifierFlattener->flattenIdentifier($versionedClass, $id); |
|
349 | |||
350 | 209 | $value = $this->conn->fetchColumn( |
|
351 | 209 | $sql, |
|
352 | 209 | array_values($flatId), |
|
353 | 209 | 0, |
|
354 | 209 | $this->extractIdentifierTypes($id, $versionedClass) |
|
355 | ); |
||
356 | |||
357 | 209 | return Type::getType($fieldMapping['type'])->convertToPHPValue($value, $this->platform); |
|
358 | } |
||
359 | |||
360 | 209 | private function extractIdentifierTypes(array $id, ClassMetadata $versionedClass) : array |
|
361 | { |
||
362 | 209 | $types = []; |
|
363 | |||
364 | 209 | foreach ($id as $field => $value) { |
|
365 | 209 | $types = array_merge($types, $this->getTypes($field, $value, $versionedClass)); |
|
366 | } |
||
367 | |||
368 | 209 | return $types; |
|
369 | } |
||
370 | |||
371 | /** |
||
372 | * {@inheritdoc} |
||
373 | */ |
||
374 | 97 | public function update($entity) |
|
375 | { |
||
376 | 97 | $tableName = $this->class->getTableName(); |
|
377 | 97 | $updateData = $this->prepareUpdateData($entity); |
|
378 | |||
379 | 97 | if ( ! isset($updateData[$tableName]) || ! ($data = $updateData[$tableName])) { |
|
380 | 8 | return; |
|
381 | } |
||
382 | |||
383 | 89 | $isVersioned = $this->class->isVersioned; |
|
384 | 89 | $quotedTableName = $this->quoteStrategy->getTableName($this->class, $this->platform); |
|
385 | |||
386 | 89 | $this->updateTable($entity, $quotedTableName, $data, $isVersioned); |
|
387 | |||
388 | 87 | if ($isVersioned) { |
|
389 | 12 | $id = $this->em->getUnitOfWork()->getEntityIdentifier($entity); |
|
390 | |||
391 | 12 | $this->assignDefaultVersionValue($entity, $id); |
|
392 | } |
||
393 | 87 | } |
|
394 | |||
395 | /** |
||
396 | * Performs an UPDATE statement for an entity on a specific table. |
||
397 | * The UPDATE can optionally be versioned, which requires the entity to have a version field. |
||
398 | * |
||
399 | * @param object $entity The entity object being updated. |
||
400 | * @param string $quotedTableName The quoted name of the table to apply the UPDATE on. |
||
401 | * @param array $updateData The map of columns to update (column => value). |
||
402 | * @param boolean $versioned Whether the UPDATE should be versioned. |
||
403 | * |
||
404 | * @return void |
||
405 | * |
||
406 | * @throws \Doctrine\ORM\ORMException |
||
407 | * @throws \Doctrine\ORM\OptimisticLockException |
||
408 | */ |
||
409 | 120 | protected final function updateTable($entity, $quotedTableName, array $updateData, $versioned = false) |
|
0 ignored issues
–
show
|
|||
410 | { |
||
411 | 120 | $set = []; |
|
412 | 120 | $types = []; |
|
413 | 120 | $params = []; |
|
414 | |||
415 | 120 | foreach ($updateData as $columnName => $value) { |
|
416 | 120 | $placeholder = '?'; |
|
417 | 120 | $column = $columnName; |
|
418 | |||
419 | switch (true) { |
||
420 | 120 | case isset($this->class->fieldNames[$columnName]): |
|
421 | 60 | $fieldName = $this->class->fieldNames[$columnName]; |
|
422 | 60 | $column = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform); |
|
423 | |||
424 | 60 | View Code Duplication | if (isset($this->class->fieldMappings[$fieldName]['requireSQLConversion'])) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
425 | 3 | $type = Type::getType($this->columnTypes[$columnName]); |
|
426 | 3 | $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform); |
|
427 | } |
||
428 | |||
429 | 60 | break; |
|
430 | |||
431 | 62 | case isset($this->quotedColumns[$columnName]): |
|
432 | 62 | $column = $this->quotedColumns[$columnName]; |
|
433 | |||
434 | 62 | break; |
|
435 | } |
||
436 | |||
437 | 120 | $params[] = $value; |
|
438 | 120 | $set[] = $column . ' = ' . $placeholder; |
|
439 | 120 | $types[] = $this->columnTypes[$columnName]; |
|
440 | } |
||
441 | |||
442 | 120 | $where = []; |
|
443 | 120 | $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); |
|
444 | |||
445 | 120 | foreach ($this->class->identifier as $idField) { |
|
446 | 120 | if ( ! isset($this->class->associationMappings[$idField])) { |
|
447 | 117 | $params[] = $identifier[$idField]; |
|
448 | 117 | $types[] = $this->class->fieldMappings[$idField]['type']; |
|
449 | 117 | $where[] = $this->quoteStrategy->getColumnName($idField, $this->class, $this->platform); |
|
450 | |||
451 | 117 | continue; |
|
452 | } |
||
453 | |||
454 | 4 | $params[] = $identifier[$idField]; |
|
455 | 4 | $where[] = $this->class->associationMappings[$idField]['joinColumns'][0]['name']; |
|
456 | 4 | $targetMapping = $this->em->getClassMetadata($this->class->associationMappings[$idField]['targetEntity']); |
|
457 | |||
458 | switch (true) { |
||
459 | 4 | View Code Duplication | case (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])): |
0 ignored issues
–
show
Accessing
fieldMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() Accessing
identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
460 | 3 | $types[] = $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type']; |
|
0 ignored issues
–
show
Accessing
fieldMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() Accessing
identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
461 | 3 | break; |
|
462 | |||
463 | 1 | View Code Duplication | case (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])): |
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() Accessing
identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
464 | 1 | $types[] = $targetMapping->associationMappings[$targetMapping->identifier[0]]['type']; |
|
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() Accessing
identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
465 | 1 | break; |
|
466 | |||
467 | default: |
||
468 | 4 | throw ORMException::unrecognizedField($targetMapping->identifier[0]); |
|
0 ignored issues
–
show
Accessing
identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
469 | } |
||
470 | |||
471 | } |
||
472 | |||
473 | 120 | if ($versioned) { |
|
474 | 18 | $versionField = $this->class->versionField; |
|
475 | 18 | $versionFieldType = $this->class->fieldMappings[$versionField]['type']; |
|
476 | 18 | $versionColumn = $this->quoteStrategy->getColumnName($versionField, $this->class, $this->platform); |
|
477 | |||
478 | 18 | $where[] = $versionColumn; |
|
479 | 18 | $types[] = $this->class->fieldMappings[$versionField]['type']; |
|
480 | 18 | $params[] = $this->class->reflFields[$versionField]->getValue($entity); |
|
481 | |||
482 | switch ($versionFieldType) { |
||
483 | 18 | case Type::SMALLINT: |
|
484 | 18 | case Type::INTEGER: |
|
485 | 1 | case Type::BIGINT: |
|
486 | 17 | $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1'; |
|
487 | 17 | break; |
|
488 | |||
489 | 1 | case Type::DATETIME: |
|
490 | 1 | $set[] = $versionColumn . ' = CURRENT_TIMESTAMP'; |
|
491 | 1 | break; |
|
492 | } |
||
493 | } |
||
494 | |||
495 | 120 | $sql = 'UPDATE ' . $quotedTableName |
|
496 | 120 | . ' SET ' . implode(', ', $set) |
|
497 | 120 | . ' WHERE ' . implode(' = ? AND ', $where) . ' = ?'; |
|
498 | |||
499 | 120 | $result = $this->conn->executeUpdate($sql, $params, $types); |
|
500 | |||
501 | 120 | if ($versioned && ! $result) { |
|
502 | 4 | throw OptimisticLockException::lockFailed($entity); |
|
503 | } |
||
504 | 117 | } |
|
505 | |||
506 | /** |
||
507 | * @todo Add check for platform if it supports foreign keys/cascading. |
||
508 | * |
||
509 | * @param array $identifier |
||
510 | * |
||
511 | * @return void |
||
512 | */ |
||
513 | 65 | protected function deleteJoinTableRecords($identifier) |
|
514 | { |
||
515 | 65 | foreach ($this->class->associationMappings as $mapping) { |
|
516 | 49 | if ($mapping['type'] !== ClassMetadata::MANY_TO_MANY) { |
|
517 | 48 | continue; |
|
518 | } |
||
519 | |||
520 | // @Todo this only covers scenarios with no inheritance or of the same level. Is there something |
||
521 | // like self-referential relationship between different levels of an inheritance hierarchy? I hope not! |
||
522 | 24 | $selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']); |
|
523 | 24 | $class = $this->class; |
|
524 | 24 | $association = $mapping; |
|
525 | 24 | $otherColumns = []; |
|
526 | 24 | $otherKeys = []; |
|
527 | 24 | $keys = []; |
|
528 | |||
529 | 24 | View Code Duplication | if ( ! $mapping['isOwningSide']) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
530 | 6 | $class = $this->em->getClassMetadata($mapping['targetEntity']); |
|
531 | 6 | $association = $class->associationMappings[$mapping['mappedBy']]; |
|
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
532 | } |
||
533 | |||
534 | 24 | $joinColumns = $mapping['isOwningSide'] |
|
535 | 20 | ? $association['joinTable']['joinColumns'] |
|
536 | 24 | : $association['joinTable']['inverseJoinColumns']; |
|
537 | |||
538 | |||
539 | 24 | if ($selfReferential) { |
|
540 | 1 | $otherColumns = (! $mapping['isOwningSide']) |
|
541 | ? $association['joinTable']['joinColumns'] |
||
542 | 1 | : $association['joinTable']['inverseJoinColumns']; |
|
543 | } |
||
544 | |||
545 | 24 | foreach ($joinColumns as $joinColumn) { |
|
546 | 24 | $keys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); |
|
0 ignored issues
–
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata> . It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
547 | } |
||
548 | |||
549 | 24 | foreach ($otherColumns as $joinColumn) { |
|
550 | 1 | $otherKeys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); |
|
0 ignored issues
–
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata> . It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
551 | } |
||
552 | |||
553 | 24 | if (isset($mapping['isOnDeleteCascade'])) { |
|
554 | 5 | continue; |
|
555 | } |
||
556 | |||
557 | 20 | $joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform); |
|
558 | |||
559 | 20 | $this->conn->delete($joinTableName, array_combine($keys, $identifier)); |
|
560 | |||
561 | 20 | if ($selfReferential) { |
|
562 | 20 | $this->conn->delete($joinTableName, array_combine($otherKeys, $identifier)); |
|
563 | } |
||
564 | } |
||
565 | 65 | } |
|
566 | |||
567 | /** |
||
568 | * {@inheritdoc} |
||
569 | */ |
||
570 | 61 | public function delete($entity) |
|
571 | { |
||
572 | 61 | $self = $this; |
|
573 | 61 | $class = $this->class; |
|
574 | 61 | $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); |
|
575 | 61 | $tableName = $this->quoteStrategy->getTableName($class, $this->platform); |
|
576 | 61 | $idColumns = $this->quoteStrategy->getIdentifierColumnNames($class, $this->platform); |
|
577 | 61 | $id = array_combine($idColumns, $identifier); |
|
578 | 61 | $types = array_map(function ($identifier) use ($class, $self) { |
|
579 | 61 | if (isset($class->fieldMappings[$identifier])) { |
|
580 | 59 | return $class->fieldMappings[$identifier]['type']; |
|
581 | } |
||
582 | |||
583 | 5 | $targetMapping = $self->em->getClassMetadata($class->associationMappings[$identifier]['targetEntity']); |
|
584 | |||
585 | 5 | if (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])) { |
|
0 ignored issues
–
show
Accessing
fieldMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() Accessing
identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
586 | 4 | return $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type']; |
|
0 ignored issues
–
show
Accessing
fieldMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() Accessing
identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
587 | } |
||
588 | |||
589 | 1 | if (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])) { |
|
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() Accessing
identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
590 | 1 | return $targetMapping->associationMappings[$targetMapping->identifier[0]]['type']; |
|
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() Accessing
identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
591 | } |
||
592 | |||
593 | throw ORMException::unrecognizedField($targetMapping->identifier[0]); |
||
0 ignored issues
–
show
Accessing
identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
594 | 61 | }, $class->identifier); |
|
595 | |||
596 | 61 | $this->deleteJoinTableRecords($identifier); |
|
597 | |||
598 | 61 | return (bool) $this->conn->delete($tableName, $id, $types); |
|
599 | } |
||
600 | |||
601 | /** |
||
602 | * Prepares the changeset of an entity for database insertion (UPDATE). |
||
603 | * |
||
604 | * The changeset is obtained from the currently running UnitOfWork. |
||
605 | * |
||
606 | * During this preparation the array that is passed as the second parameter is filled with |
||
607 | * <columnName> => <value> pairs, grouped by table name. |
||
608 | * |
||
609 | * Example: |
||
610 | * <code> |
||
611 | * array( |
||
612 | * 'foo_table' => array('column1' => 'value1', 'column2' => 'value2', ...), |
||
613 | * 'bar_table' => array('columnX' => 'valueX', 'columnY' => 'valueY', ...), |
||
614 | * ... |
||
615 | * ) |
||
616 | * </code> |
||
617 | * |
||
618 | * @param object $entity The entity for which to prepare the data. |
||
619 | * |
||
620 | * @return array The prepared data. |
||
621 | */ |
||
622 | 1027 | protected function prepareUpdateData($entity) |
|
623 | { |
||
624 | 1027 | $versionField = null; |
|
625 | 1027 | $result = []; |
|
626 | 1027 | $uow = $this->em->getUnitOfWork(); |
|
627 | |||
628 | 1027 | if (($versioned = $this->class->isVersioned) != false) { |
|
0 ignored issues
–
show
|
|||
629 | 213 | $versionField = $this->class->versionField; |
|
630 | } |
||
631 | |||
632 | 1027 | foreach ($uow->getEntityChangeSet($entity) as $field => $change) { |
|
633 | 996 | if (isset($versionField) && $versionField == $field) { |
|
634 | continue; |
||
635 | } |
||
636 | |||
637 | 996 | if (isset($this->class->embeddedClasses[$field])) { |
|
638 | 9 | continue; |
|
639 | } |
||
640 | |||
641 | 996 | $newVal = $change[1]; |
|
642 | |||
643 | 996 | if ( ! isset($this->class->associationMappings[$field])) { |
|
644 | 958 | $fieldMapping = $this->class->fieldMappings[$field]; |
|
645 | 958 | $columnName = $fieldMapping['columnName']; |
|
646 | |||
647 | 958 | $this->columnTypes[$columnName] = $fieldMapping['type']; |
|
648 | |||
649 | 958 | $result[$this->getOwningTable($field)][$columnName] = $newVal; |
|
650 | |||
651 | 958 | continue; |
|
652 | } |
||
653 | |||
654 | 851 | $assoc = $this->class->associationMappings[$field]; |
|
655 | |||
656 | // Only owning side of x-1 associations can have a FK column. |
||
657 | 851 | if ( ! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE)) { |
|
658 | 8 | continue; |
|
659 | } |
||
660 | |||
661 | 851 | if ($newVal !== null) { |
|
662 | 629 | $oid = spl_object_hash($newVal); |
|
663 | |||
664 | 629 | if (isset($this->queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) { |
|
665 | // The associated entity $newVal is not yet persisted, so we must |
||
666 | // set $newVal = null, in order to insert a null value and schedule an |
||
667 | // extra update on the UnitOfWork. |
||
668 | 43 | $uow->scheduleExtraUpdate($entity, [$field => [null, $newVal]]); |
|
669 | |||
670 | 43 | $newVal = null; |
|
671 | } |
||
672 | } |
||
673 | |||
674 | 851 | $newValId = null; |
|
675 | |||
676 | 851 | if ($newVal !== null) { |
|
677 | 629 | $newValId = $uow->getEntityIdentifier($newVal); |
|
678 | } |
||
679 | |||
680 | 851 | $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); |
|
681 | 851 | $owningTable = $this->getOwningTable($field); |
|
682 | |||
683 | 851 | foreach ($assoc['joinColumns'] as $joinColumn) { |
|
684 | 851 | $sourceColumn = $joinColumn['name']; |
|
685 | 851 | $targetColumn = $joinColumn['referencedColumnName']; |
|
686 | 851 | $quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); |
|
687 | |||
688 | 851 | $this->quotedColumns[$sourceColumn] = $quotedColumn; |
|
689 | 851 | $this->columnTypes[$sourceColumn] = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em); |
|
0 ignored issues
–
show
$targetClass of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata> . It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
690 | 851 | $result[$owningTable][$sourceColumn] = $newValId |
|
691 | 629 | ? $newValId[$targetClass->getFieldForColumn($targetColumn)] |
|
692 | 851 | : null; |
|
693 | } |
||
694 | } |
||
695 | |||
696 | 1027 | return $result; |
|
697 | } |
||
698 | |||
699 | /** |
||
700 | * Prepares the data changeset of a managed entity for database insertion (initial INSERT). |
||
701 | * The changeset of the entity is obtained from the currently running UnitOfWork. |
||
702 | * |
||
703 | * The default insert data preparation is the same as for updates. |
||
704 | * |
||
705 | * @param object $entity The entity for which to prepare the data. |
||
706 | * |
||
707 | * @return array The prepared data for the tables to update. |
||
708 | * |
||
709 | * @see prepareUpdateData |
||
710 | */ |
||
711 | 1022 | protected function prepareInsertData($entity) |
|
712 | { |
||
713 | 1022 | return $this->prepareUpdateData($entity); |
|
714 | } |
||
715 | |||
716 | /** |
||
717 | * {@inheritdoc} |
||
718 | */ |
||
719 | 926 | public function getOwningTable($fieldName) |
|
720 | { |
||
721 | 926 | return $this->class->getTableName(); |
|
722 | } |
||
723 | |||
724 | /** |
||
725 | * {@inheritdoc} |
||
726 | */ |
||
727 | 509 | public function load(array $criteria, $entity = null, $assoc = null, array $hints = [], $lockMode = null, $limit = null, array $orderBy = null) |
|
728 | { |
||
729 | 509 | $this->switchPersisterContext(null, $limit); |
|
730 | |||
731 | 509 | $sql = $this->getSelectSQL($criteria, $assoc, $lockMode, $limit, null, $orderBy); |
|
732 | 508 | list($params, $types) = $this->expandParameters($criteria); |
|
733 | 508 | $stmt = $this->conn->executeQuery($sql, $params, $types); |
|
734 | |||
735 | 508 | View Code Duplication | if ($entity !== null) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
736 | 72 | $hints[Query::HINT_REFRESH] = true; |
|
737 | 72 | $hints[Query::HINT_REFRESH_ENTITY] = $entity; |
|
738 | } |
||
739 | |||
740 | 508 | $hydrator = $this->em->newHydrator($this->currentPersisterContext->selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); |
|
741 | 508 | $entities = $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, $hints); |
|
742 | |||
743 | 508 | return $entities ? $entities[0] : null; |
|
744 | } |
||
745 | |||
746 | /** |
||
747 | * {@inheritdoc} |
||
748 | */ |
||
749 | 433 | public function loadById(array $identifier, $entity = null) |
|
750 | { |
||
751 | 433 | return $this->load($identifier, $entity); |
|
752 | } |
||
753 | |||
754 | /** |
||
755 | * {@inheritdoc} |
||
756 | */ |
||
757 | 96 | public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = []) |
|
758 | { |
||
759 | 96 | if (($foundEntity = $this->em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) != false) { |
|
0 ignored issues
–
show
|
|||
760 | return $foundEntity; |
||
761 | } |
||
762 | |||
763 | 96 | $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); |
|
764 | |||
765 | 96 | if ($assoc['isOwningSide']) { |
|
766 | 32 | $isInverseSingleValued = $assoc['inversedBy'] && ! $targetClass->isCollectionValuedAssociation($assoc['inversedBy']); |
|
767 | |||
768 | // Mark inverse side as fetched in the hints, otherwise the UoW would |
||
769 | // try to load it in a separate query (remember: to-one inverse sides can not be lazy). |
||
770 | 32 | $hints = []; |
|
771 | |||
772 | 32 | if ($isInverseSingleValued) { |
|
773 | $hints['fetched']["r"][$assoc['inversedBy']] = true; |
||
774 | } |
||
775 | |||
776 | /* cascade read-only status |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
49% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
777 | if ($this->em->getUnitOfWork()->isReadOnly($sourceEntity)) { |
||
778 | $hints[Query::HINT_READ_ONLY] = true; |
||
779 | } |
||
780 | */ |
||
781 | |||
782 | 32 | $targetEntity = $this->load($identifier, null, $assoc, $hints); |
|
783 | |||
784 | // Complete bidirectional association, if necessary |
||
785 | 32 | if ($targetEntity !== null && $isInverseSingleValued) { |
|
786 | $targetClass->reflFields[$assoc['inversedBy']]->setValue($targetEntity, $sourceEntity); |
||
0 ignored issues
–
show
Accessing
reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
787 | } |
||
788 | |||
789 | 32 | return $targetEntity; |
|
790 | } |
||
791 | |||
792 | 64 | $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']); |
|
793 | 64 | $owningAssoc = $targetClass->getAssociationMapping($assoc['mappedBy']); |
|
794 | |||
795 | // TRICKY: since the association is specular source and target are flipped |
||
796 | 64 | foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { |
|
797 | 64 | if ( ! isset($sourceClass->fieldNames[$sourceKeyColumn])) { |
|
0 ignored issues
–
show
Accessing
fieldNames on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
798 | throw MappingException::joinColumnMustPointToMappedField( |
||
799 | $sourceClass->name, $sourceKeyColumn |
||
0 ignored issues
–
show
Accessing
name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
800 | ); |
||
801 | } |
||
802 | |||
803 | // unset the old value and set the new sql aliased value here. By definition |
||
804 | // unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method. |
||
805 | 64 | $identifier[$targetClass->getFieldForColumn($targetKeyColumn)] = |
|
806 | 64 | $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); |
|
0 ignored issues
–
show
Accessing
reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() Accessing
fieldNames on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
807 | |||
808 | 64 | unset($identifier[$targetKeyColumn]); |
|
809 | } |
||
810 | |||
811 | 64 | $targetEntity = $this->load($identifier, null, $assoc); |
|
812 | |||
813 | 64 | if ($targetEntity !== null) { |
|
814 | 16 | $targetClass->setFieldValue($targetEntity, $assoc['mappedBy'], $sourceEntity); |
|
815 | } |
||
816 | |||
817 | 64 | return $targetEntity; |
|
818 | } |
||
819 | |||
820 | /** |
||
821 | * {@inheritdoc} |
||
822 | */ |
||
823 | 17 | public function refresh(array $id, $entity, $lockMode = null) |
|
824 | { |
||
825 | 17 | $sql = $this->getSelectSQL($id, null, $lockMode); |
|
826 | 17 | list($params, $types) = $this->expandParameters($id); |
|
827 | 17 | $stmt = $this->conn->executeQuery($sql, $params, $types); |
|
828 | |||
829 | 17 | $hydrator = $this->em->newHydrator(Query::HYDRATE_OBJECT); |
|
830 | 17 | $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, [Query::HINT_REFRESH => true]); |
|
831 | 17 | } |
|
832 | |||
833 | /** |
||
834 | * {@inheritDoc} |
||
835 | */ |
||
836 | 46 | public function count($criteria = []) |
|
837 | { |
||
838 | 46 | $sql = $this->getCountSQL($criteria); |
|
839 | |||
840 | 46 | list($params, $types) = ($criteria instanceof Criteria) |
|
841 | 43 | ? $this->expandCriteriaParameters($criteria) |
|
842 | 46 | : $this->expandParameters($criteria); |
|
843 | |||
844 | 46 | return (int) $this->conn->executeQuery($sql, $params, $types)->fetchColumn(); |
|
845 | } |
||
846 | |||
847 | /** |
||
848 | * {@inheritdoc} |
||
849 | */ |
||
850 | 9 | public function loadCriteria(Criteria $criteria) |
|
851 | { |
||
852 | 9 | $orderBy = $criteria->getOrderings(); |
|
853 | 9 | $limit = $criteria->getMaxResults(); |
|
854 | 9 | $offset = $criteria->getFirstResult(); |
|
855 | 9 | $query = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy); |
|
856 | |||
857 | 7 | list($params, $types) = $this->expandCriteriaParameters($criteria); |
|
858 | |||
859 | 7 | $stmt = $this->conn->executeQuery($query, $params, $types); |
|
860 | 7 | $hydrator = $this->em->newHydrator(($this->currentPersisterContext->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); |
|
861 | |||
862 | 7 | return $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, [UnitOfWork::HINT_DEFEREAGERLOAD => true] |
|
863 | ); |
||
864 | } |
||
865 | |||
866 | /** |
||
867 | * {@inheritdoc} |
||
868 | */ |
||
869 | 56 | public function expandCriteriaParameters(Criteria $criteria) |
|
870 | { |
||
871 | 56 | $expression = $criteria->getWhereExpression(); |
|
872 | 56 | $sqlParams = []; |
|
873 | 56 | $sqlTypes = []; |
|
874 | |||
875 | 56 | if ($expression === null) { |
|
876 | 2 | return [$sqlParams, $sqlTypes]; |
|
877 | } |
||
878 | |||
879 | 55 | $valueVisitor = new SqlValueVisitor(); |
|
880 | |||
881 | 55 | $valueVisitor->dispatch($expression); |
|
882 | |||
883 | 55 | list($params, $types) = $valueVisitor->getParamsAndTypes(); |
|
884 | |||
885 | 55 | foreach ($params as $param) { |
|
886 | 51 | $sqlParams = array_merge($sqlParams, $this->getValues($param)); |
|
887 | } |
||
888 | |||
889 | 55 | foreach ($types as $type) { |
|
890 | 51 | list ($field, $value) = $type; |
|
891 | 51 | $sqlTypes = array_merge($sqlTypes, $this->getTypes($field, $value, $this->class)); |
|
892 | } |
||
893 | |||
894 | 55 | return [$sqlParams, $sqlTypes]; |
|
895 | } |
||
896 | |||
897 | /** |
||
898 | * {@inheritdoc} |
||
899 | */ |
||
900 | 70 | public function loadAll(array $criteria = [], array $orderBy = null, $limit = null, $offset = null) |
|
901 | { |
||
902 | 70 | $this->switchPersisterContext($offset, $limit); |
|
903 | |||
904 | 70 | $sql = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy); |
|
905 | 66 | list($params, $types) = $this->expandParameters($criteria); |
|
906 | 66 | $stmt = $this->conn->executeQuery($sql, $params, $types); |
|
907 | |||
908 | 66 | $hydrator = $this->em->newHydrator(($this->currentPersisterContext->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); |
|
909 | |||
910 | 66 | return $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, [UnitOfWork::HINT_DEFEREAGERLOAD => true] |
|
911 | ); |
||
912 | } |
||
913 | |||
914 | /** |
||
915 | * {@inheritdoc} |
||
916 | */ |
||
917 | 8 | View Code Duplication | public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) |
918 | { |
||
919 | 8 | $this->switchPersisterContext($offset, $limit); |
|
920 | |||
921 | 8 | $stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit); |
|
922 | |||
923 | 8 | return $this->loadArrayFromStatement($assoc, $stmt); |
|
0 ignored issues
–
show
$stmt of type object<Doctrine\DBAL\Driver\Statement> is not a sub-type of object<Doctrine\DBAL\Statement> . It seems like you assume a concrete implementation of the interface Doctrine\DBAL\Driver\Statement to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
924 | } |
||
925 | |||
926 | /** |
||
927 | * Loads an array of entities from a given DBAL statement. |
||
928 | * |
||
929 | * @param array $assoc |
||
930 | * @param \Doctrine\DBAL\Statement $stmt |
||
931 | * |
||
932 | * @return array |
||
933 | */ |
||
934 | 13 | View Code Duplication | private function loadArrayFromStatement($assoc, $stmt) |
935 | { |
||
936 | 13 | $rsm = $this->currentPersisterContext->rsm; |
|
937 | 13 | $hints = [UnitOfWork::HINT_DEFEREAGERLOAD => true]; |
|
938 | |||
939 | 13 | if (isset($assoc['indexBy'])) { |
|
940 | 7 | $rsm = clone ($this->currentPersisterContext->rsm); // this is necessary because the "default rsm" should be changed. |
|
941 | 7 | $rsm->addIndexBy('r', $assoc['indexBy']); |
|
942 | } |
||
943 | |||
944 | 13 | return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm, $hints); |
|
945 | } |
||
946 | |||
947 | /** |
||
948 | * Hydrates a collection from a given DBAL statement. |
||
949 | * |
||
950 | * @param array $assoc |
||
951 | * @param \Doctrine\DBAL\Statement $stmt |
||
952 | * @param PersistentCollection $coll |
||
953 | * |
||
954 | * @return array |
||
955 | */ |
||
956 | 144 | View Code Duplication | private function loadCollectionFromStatement($assoc, $stmt, $coll) |
957 | { |
||
958 | 144 | $rsm = $this->currentPersisterContext->rsm; |
|
959 | $hints = [ |
||
960 | 144 | UnitOfWork::HINT_DEFEREAGERLOAD => true, |
|
961 | 144 | 'collection' => $coll |
|
962 | ]; |
||
963 | |||
964 | 144 | if (isset($assoc['indexBy'])) { |
|
965 | 10 | $rsm = clone ($this->currentPersisterContext->rsm); // this is necessary because the "default rsm" should be changed. |
|
966 | 10 | $rsm->addIndexBy('r', $assoc['indexBy']); |
|
967 | } |
||
968 | |||
969 | 144 | return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm, $hints); |
|
970 | } |
||
971 | |||
972 | /** |
||
973 | * {@inheritdoc} |
||
974 | */ |
||
975 | 82 | public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) |
|
976 | { |
||
977 | 82 | $stmt = $this->getManyToManyStatement($assoc, $sourceEntity); |
|
978 | |||
979 | 82 | return $this->loadCollectionFromStatement($assoc, $stmt, $coll); |
|
0 ignored issues
–
show
$stmt of type object<Doctrine\DBAL\Driver\Statement> is not a sub-type of object<Doctrine\DBAL\Statement> . It seems like you assume a concrete implementation of the interface Doctrine\DBAL\Driver\Statement to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
980 | } |
||
981 | |||
982 | /** |
||
983 | * @param array $assoc |
||
984 | * @param object $sourceEntity |
||
985 | * @param int|null $offset |
||
986 | * @param int|null $limit |
||
987 | * |
||
988 | * @return \Doctrine\DBAL\Driver\Statement |
||
989 | * |
||
990 | * @throws \Doctrine\ORM\Mapping\MappingException |
||
991 | */ |
||
992 | 89 | private function getManyToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null) |
|
993 | { |
||
994 | 89 | $this->switchPersisterContext($offset, $limit); |
|
995 | |||
996 | 89 | $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']); |
|
997 | 89 | $class = $sourceClass; |
|
998 | 89 | $association = $assoc; |
|
999 | 89 | $criteria = []; |
|
1000 | 89 | $parameters = []; |
|
1001 | |||
1002 | 89 | View Code Duplication | if ( ! $assoc['isOwningSide']) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1003 | 12 | $class = $this->em->getClassMetadata($assoc['targetEntity']); |
|
1004 | 12 | $association = $class->associationMappings[$assoc['mappedBy']]; |
|
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1005 | } |
||
1006 | |||
1007 | 89 | $joinColumns = $assoc['isOwningSide'] |
|
1008 | 82 | ? $association['joinTable']['joinColumns'] |
|
1009 | 89 | : $association['joinTable']['inverseJoinColumns']; |
|
1010 | |||
1011 | 89 | $quotedJoinTable = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform); |
|
0 ignored issues
–
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata> . It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
1012 | |||
1013 | 89 | foreach ($joinColumns as $joinColumn) { |
|
1014 | 89 | $sourceKeyColumn = $joinColumn['referencedColumnName']; |
|
1015 | 89 | $quotedKeyColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); |
|
0 ignored issues
–
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata> . It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
1016 | |||
1017 | switch (true) { |
||
1018 | 89 | case $sourceClass->containsForeignIdentifier: |
|
0 ignored issues
–
show
Accessing
containsForeignIdentifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1019 | 4 | $field = $sourceClass->getFieldForColumn($sourceKeyColumn); |
|
1020 | 4 | $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); |
|
0 ignored issues
–
show
Accessing
reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1021 | |||
1022 | 4 | View Code Duplication | if (isset($sourceClass->associationMappings[$field])) { |
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1023 | 4 | $value = $this->em->getUnitOfWork()->getEntityIdentifier($value); |
|
1024 | 4 | $value = $value[$this->em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; |
|
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() Accessing
identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1025 | } |
||
1026 | |||
1027 | 4 | break; |
|
1028 | |||
1029 | 87 | case isset($sourceClass->fieldNames[$sourceKeyColumn]): |
|
0 ignored issues
–
show
Accessing
fieldNames on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1030 | 87 | $field = $sourceClass->fieldNames[$sourceKeyColumn]; |
|
0 ignored issues
–
show
Accessing
fieldNames on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1031 | 87 | $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); |
|
0 ignored issues
–
show
Accessing
reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1032 | |||
1033 | 87 | break; |
|
1034 | |||
1035 | default: |
||
1036 | throw MappingException::joinColumnMustPointToMappedField( |
||
1037 | $sourceClass->name, $sourceKeyColumn |
||
0 ignored issues
–
show
Accessing
name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1038 | ); |
||
1039 | } |
||
1040 | |||
1041 | 89 | $criteria[$quotedJoinTable . '.' . $quotedKeyColumn] = $value; |
|
1042 | 89 | $parameters[] = [ |
|
1043 | 89 | 'value' => $value, |
|
1044 | 89 | 'field' => $field, |
|
1045 | 89 | 'class' => $sourceClass, |
|
1046 | ]; |
||
1047 | } |
||
1048 | |||
1049 | 89 | $sql = $this->getSelectSQL($criteria, $assoc, null, $limit, $offset); |
|
1050 | 89 | list($params, $types) = $this->expandToManyParameters($parameters); |
|
1051 | |||
1052 | 89 | return $this->conn->executeQuery($sql, $params, $types); |
|
1053 | } |
||
1054 | |||
1055 | /** |
||
1056 | * {@inheritdoc} |
||
1057 | */ |
||
1058 | 561 | public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, array $orderBy = null) |
|
1059 | { |
||
1060 | 561 | $this->switchPersisterContext($offset, $limit); |
|
1061 | |||
1062 | 561 | $lockSql = ''; |
|
1063 | 561 | $joinSql = ''; |
|
1064 | 561 | $orderBySql = ''; |
|
1065 | |||
1066 | 561 | View Code Duplication | if ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1067 | 90 | $joinSql = $this->getSelectManyToManyJoinSQL($assoc); |
|
1068 | } |
||
1069 | |||
1070 | 561 | if (isset($assoc['orderBy'])) { |
|
1071 | 5 | $orderBy = $assoc['orderBy']; |
|
1072 | } |
||
1073 | |||
1074 | 561 | if ($orderBy) { |
|
1075 | 11 | $orderBySql = $this->getOrderBySQL($orderBy, $this->getSQLTableAlias($this->class->name)); |
|
1076 | } |
||
1077 | |||
1078 | 559 | $conditionSql = ($criteria instanceof Criteria) |
|
1079 | 9 | ? $this->getSelectConditionCriteriaSQL($criteria) |
|
1080 | 557 | : $this->getSelectConditionSQL($criteria, $assoc); |
|
1081 | |||
1082 | View Code Duplication | switch ($lockMode) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1083 | 554 | case LockMode::PESSIMISTIC_READ: |
|
1084 | $lockSql = ' ' . $this->platform->getReadLockSQL(); |
||
1085 | break; |
||
1086 | |||
1087 | 554 | case LockMode::PESSIMISTIC_WRITE: |
|
1088 | $lockSql = ' ' . $this->platform->getWriteLockSQL(); |
||
1089 | break; |
||
1090 | } |
||
1091 | |||
1092 | 554 | $columnList = $this->getSelectColumnsSQL(); |
|
1093 | 554 | $tableAlias = $this->getSQLTableAlias($this->class->name); |
|
1094 | 554 | $filterSql = $this->generateFilterConditionSQL($this->class, $tableAlias); |
|
1095 | 554 | $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); |
|
1096 | |||
1097 | 554 | if ('' !== $filterSql) { |
|
1098 | 12 | $conditionSql = $conditionSql |
|
1099 | 11 | ? $conditionSql . ' AND ' . $filterSql |
|
1100 | 12 | : $filterSql; |
|
1101 | } |
||
1102 | |||
1103 | 554 | $select = 'SELECT ' . $columnList; |
|
1104 | 554 | $from = ' FROM ' . $tableName . ' '. $tableAlias; |
|
1105 | 554 | $join = $this->currentPersisterContext->selectJoinSql . $joinSql; |
|
1106 | 554 | $where = ($conditionSql ? ' WHERE ' . $conditionSql : ''); |
|
1107 | 554 | $lock = $this->platform->appendLockHint($from, $lockMode); |
|
1108 | $query = $select |
||
1109 | 554 | . $lock |
|
1110 | 554 | . $join |
|
1111 | 554 | . $where |
|
1112 | 554 | . $orderBySql; |
|
1113 | |||
1114 | 554 | return $this->platform->modifyLimitQuery($query, $limit, $offset) . $lockSql; |
|
1115 | } |
||
1116 | |||
1117 | /** |
||
1118 | * {@inheritDoc} |
||
1119 | */ |
||
1120 | 41 | public function getCountSQL($criteria = []) |
|
1121 | { |
||
1122 | 41 | $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); |
|
1123 | 41 | $tableAlias = $this->getSQLTableAlias($this->class->name); |
|
1124 | |||
1125 | 41 | $conditionSql = ($criteria instanceof Criteria) |
|
1126 | 38 | ? $this->getSelectConditionCriteriaSQL($criteria) |
|
1127 | 41 | : $this->getSelectConditionSQL($criteria); |
|
1128 | |||
1129 | 41 | $filterSql = $this->generateFilterConditionSQL($this->class, $tableAlias); |
|
1130 | |||
1131 | 41 | if ('' !== $filterSql) { |
|
1132 | 2 | $conditionSql = $conditionSql |
|
1133 | 2 | ? $conditionSql . ' AND ' . $filterSql |
|
1134 | 2 | : $filterSql; |
|
1135 | } |
||
1136 | |||
1137 | $sql = 'SELECT COUNT(*) ' |
||
1138 | 41 | . 'FROM ' . $tableName . ' ' . $tableAlias |
|
1139 | 41 | . (empty($conditionSql) ? '' : ' WHERE ' . $conditionSql); |
|
1140 | |||
1141 | 41 | return $sql; |
|
1142 | } |
||
1143 | |||
1144 | /** |
||
1145 | * Gets the ORDER BY SQL snippet for ordered collections. |
||
1146 | * |
||
1147 | * @param array $orderBy |
||
1148 | * @param string $baseTableAlias |
||
1149 | * |
||
1150 | * @return string |
||
1151 | * |
||
1152 | * @throws \Doctrine\ORM\ORMException |
||
1153 | */ |
||
1154 | 12 | protected final function getOrderBySQL(array $orderBy, $baseTableAlias) |
|
0 ignored issues
–
show
|
|||
1155 | { |
||
1156 | 12 | $orderByList = []; |
|
1157 | |||
1158 | 12 | foreach ($orderBy as $fieldName => $orientation) { |
|
1159 | |||
1160 | 12 | $orientation = strtoupper(trim($orientation)); |
|
1161 | |||
1162 | 12 | if ($orientation != 'ASC' && $orientation != 'DESC') { |
|
1163 | 1 | throw ORMException::invalidOrientation($this->class->name, $fieldName); |
|
1164 | } |
||
1165 | |||
1166 | 11 | if (isset($this->class->fieldMappings[$fieldName])) { |
|
1167 | 9 | $tableAlias = isset($this->class->fieldMappings[$fieldName]['inherited']) |
|
1168 | ? $this->getSQLTableAlias($this->class->fieldMappings[$fieldName]['inherited']) |
||
1169 | 9 | : $baseTableAlias; |
|
1170 | |||
1171 | 9 | $columnName = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform); |
|
1172 | 9 | $orderByList[] = $tableAlias . '.' . $columnName . ' ' . $orientation; |
|
1173 | |||
1174 | 9 | continue; |
|
1175 | } |
||
1176 | |||
1177 | 2 | if (isset($this->class->associationMappings[$fieldName])) { |
|
1178 | |||
1179 | 2 | if ( ! $this->class->associationMappings[$fieldName]['isOwningSide']) { |
|
1180 | 1 | throw ORMException::invalidFindByInverseAssociation($this->class->name, $fieldName); |
|
1181 | } |
||
1182 | |||
1183 | 1 | $tableAlias = isset($this->class->associationMappings[$fieldName]['inherited']) |
|
1184 | ? $this->getSQLTableAlias($this->class->associationMappings[$fieldName]['inherited']) |
||
1185 | 1 | : $baseTableAlias; |
|
1186 | |||
1187 | 1 | foreach ($this->class->associationMappings[$fieldName]['joinColumns'] as $joinColumn) { |
|
1188 | 1 | $columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); |
|
1189 | 1 | $orderByList[] = $tableAlias . '.' . $columnName . ' ' . $orientation; |
|
1190 | } |
||
1191 | |||
1192 | 1 | continue; |
|
1193 | } |
||
1194 | |||
1195 | throw ORMException::unrecognizedField($fieldName); |
||
1196 | } |
||
1197 | |||
1198 | 10 | return ' ORDER BY ' . implode(', ', $orderByList); |
|
1199 | } |
||
1200 | |||
1201 | /** |
||
1202 | * Gets the SQL fragment with the list of columns to select when querying for |
||
1203 | * an entity in this persister. |
||
1204 | * |
||
1205 | * Subclasses should override this method to alter or change the select column |
||
1206 | * list SQL fragment. Note that in the implementation of BasicEntityPersister |
||
1207 | * the resulting SQL fragment is generated only once and cached in {@link selectColumnListSql}. |
||
1208 | * Subclasses may or may not do the same. |
||
1209 | * |
||
1210 | * @return string The SQL fragment. |
||
1211 | */ |
||
1212 | 555 | protected function getSelectColumnsSQL() |
|
1213 | { |
||
1214 | 555 | if ($this->currentPersisterContext->selectColumnListSql !== null) { |
|
1215 | 145 | return $this->currentPersisterContext->selectColumnListSql; |
|
1216 | } |
||
1217 | |||
1218 | 555 | $columnList = []; |
|
1219 | 555 | $this->currentPersisterContext->rsm->addEntityResult($this->class->name, 'r'); // r for root |
|
1220 | |||
1221 | // Add regular columns to select list |
||
1222 | 555 | foreach ($this->class->fieldNames as $field) { |
|
1223 | 554 | $columnList[] = $this->getSelectColumnSQL($field, $this->class); |
|
1224 | } |
||
1225 | |||
1226 | 555 | $this->currentPersisterContext->selectJoinSql = ''; |
|
1227 | 555 | $eagerAliasCounter = 0; |
|
1228 | |||
1229 | 555 | foreach ($this->class->associationMappings as $assocField => $assoc) { |
|
1230 | 492 | $assocColumnSQL = $this->getSelectColumnAssociationSQL($assocField, $assoc, $this->class); |
|
1231 | |||
1232 | 492 | if ($assocColumnSQL) { |
|
1233 | 414 | $columnList[] = $assocColumnSQL; |
|
1234 | } |
||
1235 | |||
1236 | 492 | $isAssocToOneInverseSide = $assoc['type'] & ClassMetadata::TO_ONE && ! $assoc['isOwningSide']; |
|
1237 | 492 | $isAssocFromOneEager = $assoc['type'] !== ClassMetadata::MANY_TO_MANY && $assoc['fetch'] === ClassMetadata::FETCH_EAGER; |
|
1238 | |||
1239 | 492 | if ( ! ($isAssocFromOneEager || $isAssocToOneInverseSide)) { |
|
1240 | 470 | continue; |
|
1241 | } |
||
1242 | |||
1243 | 190 | if ((($assoc['type'] & ClassMetadata::TO_MANY) > 0) && $this->currentPersisterContext->handlesLimits) { |
|
1244 | 3 | continue; |
|
1245 | } |
||
1246 | |||
1247 | 187 | $eagerEntity = $this->em->getClassMetadata($assoc['targetEntity']); |
|
1248 | |||
1249 | 187 | if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) { |
|
0 ignored issues
–
show
Accessing
inheritanceType on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1250 | 5 | continue; // now this is why you shouldn't use inheritance |
|
1251 | } |
||
1252 | |||
1253 | 182 | $assocAlias = 'e' . ($eagerAliasCounter++); |
|
1254 | 182 | $this->currentPersisterContext->rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField); |
|
1255 | |||
1256 | 182 | foreach ($eagerEntity->fieldNames as $field) { |
|
0 ignored issues
–
show
Accessing
fieldNames on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1257 | 180 | $columnList[] = $this->getSelectColumnSQL($field, $eagerEntity, $assocAlias); |
|
0 ignored issues
–
show
$eagerEntity of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata> . It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
1258 | } |
||
1259 | |||
1260 | 182 | foreach ($eagerEntity->associationMappings as $eagerAssocField => $eagerAssoc) { |
|
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1261 | 180 | $eagerAssocColumnSQL = $this->getSelectColumnAssociationSQL( |
|
1262 | 180 | $eagerAssocField, $eagerAssoc, $eagerEntity, $assocAlias |
|
0 ignored issues
–
show
$eagerEntity of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata> . It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
1263 | ); |
||
1264 | |||
1265 | 180 | if ($eagerAssocColumnSQL) { |
|
1266 | 180 | $columnList[] = $eagerAssocColumnSQL; |
|
1267 | } |
||
1268 | } |
||
1269 | |||
1270 | 182 | $association = $assoc; |
|
1271 | 182 | $joinCondition = []; |
|
1272 | |||
1273 | 182 | if (isset($assoc['indexBy'])) { |
|
1274 | 1 | $this->currentPersisterContext->rsm->addIndexBy($assocAlias, $assoc['indexBy']); |
|
1275 | } |
||
1276 | |||
1277 | 182 | View Code Duplication | if ( ! $assoc['isOwningSide']) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1278 | 176 | $eagerEntity = $this->em->getClassMetadata($assoc['targetEntity']); |
|
1279 | 176 | $association = $eagerEntity->getAssociationMapping($assoc['mappedBy']); |
|
1280 | } |
||
1281 | |||
1282 | 182 | $joinTableAlias = $this->getSQLTableAlias($eagerEntity->name, $assocAlias); |
|
0 ignored issues
–
show
Accessing
name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1283 | 182 | $joinTableName = $this->quoteStrategy->getTableName($eagerEntity, $this->platform); |
|
0 ignored issues
–
show
$eagerEntity of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata> . It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
1284 | |||
1285 | 182 | if ($assoc['isOwningSide']) { |
|
1286 | 12 | $tableAlias = $this->getSQLTableAlias($association['targetEntity'], $assocAlias); |
|
1287 | 12 | $this->currentPersisterContext->selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($association['joinColumns']); |
|
1288 | |||
1289 | 12 | View Code Duplication | foreach ($association['joinColumns'] as $joinColumn) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1290 | 12 | $sourceCol = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); |
|
1291 | 12 | $targetCol = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform); |
|
1292 | 12 | $joinCondition[] = $this->getSQLTableAlias($association['sourceEntity']) |
|
1293 | 12 | . '.' . $sourceCol . ' = ' . $tableAlias . '.' . $targetCol; |
|
1294 | } |
||
1295 | |||
1296 | // Add filter SQL |
||
1297 | 12 | if ($filterSql = $this->generateFilterConditionSQL($eagerEntity, $tableAlias)) { |
|
0 ignored issues
–
show
$eagerEntity of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata> . It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
1298 | 12 | $joinCondition[] = $filterSql; |
|
1299 | } |
||
1300 | |||
1301 | } else { |
||
1302 | |||
1303 | 176 | $this->currentPersisterContext->selectJoinSql .= ' LEFT JOIN'; |
|
1304 | |||
1305 | 176 | View Code Duplication | foreach ($association['joinColumns'] as $joinColumn) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1306 | 176 | $sourceCol = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); |
|
1307 | 176 | $targetCol = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform); |
|
1308 | |||
1309 | 176 | $joinCondition[] = $this->getSQLTableAlias($association['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = ' |
|
1310 | 176 | . $this->getSQLTableAlias($association['targetEntity']) . '.' . $targetCol; |
|
1311 | } |
||
1312 | } |
||
1313 | |||
1314 | 182 | $this->currentPersisterContext->selectJoinSql .= ' ' . $joinTableName . ' ' . $joinTableAlias . ' ON '; |
|
1315 | 182 | $this->currentPersisterContext->selectJoinSql .= implode(' AND ', $joinCondition); |
|
1316 | } |
||
1317 | |||
1318 | 555 | $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList); |
|
1319 | |||
1320 | 555 | return $this->currentPersisterContext->selectColumnListSql; |
|
1321 | } |
||
1322 | |||
1323 | /** |
||
1324 | * Gets the SQL join fragment used when selecting entities from an association. |
||
1325 | * |
||
1326 | * @param string $field |
||
1327 | * @param array $assoc |
||
1328 | * @param ClassMetadata $class |
||
1329 | * @param string $alias |
||
1330 | * |
||
1331 | * @return string |
||
1332 | */ |
||
1333 | 492 | protected function getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r') |
|
0 ignored issues
–
show
|
|||
1334 | { |
||
1335 | 492 | if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) ) { |
|
1336 | 394 | return ''; |
|
1337 | } |
||
1338 | |||
1339 | 431 | $columnList = []; |
|
1340 | 431 | $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); |
|
1341 | 431 | $isIdentifier = isset($assoc['id']) && $assoc['id'] === true; |
|
1342 | 431 | $sqlTableAlias = $this->getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias)); |
|
1343 | |||
1344 | 431 | foreach ($assoc['joinColumns'] as $joinColumn) { |
|
1345 | 431 | $quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); |
|
1346 | 431 | $resultColumnName = $this->getSQLColumnAlias($joinColumn['name']); |
|
1347 | 431 | $type = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); |
|
0 ignored issues
–
show
$targetClass of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata> . It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
1348 | |||
1349 | 431 | $this->currentPersisterContext->rsm->addMetaResult($alias, $resultColumnName, $joinColumn['name'], $isIdentifier, $type); |
|
1350 | |||
1351 | 431 | $columnList[] = sprintf('%s.%s AS %s', $sqlTableAlias, $quotedColumn, $resultColumnName); |
|
1352 | } |
||
1353 | |||
1354 | 431 | return implode(', ', $columnList); |
|
1355 | } |
||
1356 | |||
1357 | /** |
||
1358 | * Gets the SQL join fragment used when selecting entities from a |
||
1359 | * many-to-many association. |
||
1360 | * |
||
1361 | * @param array $manyToMany |
||
1362 | * |
||
1363 | * @return string |
||
1364 | */ |
||
1365 | 92 | protected function getSelectManyToManyJoinSQL(array $manyToMany) |
|
1366 | { |
||
1367 | 92 | $conditions = []; |
|
1368 | 92 | $association = $manyToMany; |
|
1369 | 92 | $sourceTableAlias = $this->getSQLTableAlias($this->class->name); |
|
1370 | |||
1371 | 92 | View Code Duplication | if ( ! $manyToMany['isOwningSide']) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1372 | 13 | $targetEntity = $this->em->getClassMetadata($manyToMany['targetEntity']); |
|
1373 | 13 | $association = $targetEntity->associationMappings[$manyToMany['mappedBy']]; |
|
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1374 | } |
||
1375 | |||
1376 | 92 | $joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform); |
|
1377 | 92 | $joinColumns = ($manyToMany['isOwningSide']) |
|
1378 | 84 | ? $association['joinTable']['inverseJoinColumns'] |
|
1379 | 92 | : $association['joinTable']['joinColumns']; |
|
1380 | |||
1381 | 92 | foreach ($joinColumns as $joinColumn) { |
|
1382 | 92 | $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); |
|
1383 | 92 | $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform); |
|
1384 | 92 | $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableName . '.' . $quotedSourceColumn; |
|
1385 | } |
||
1386 | |||
1387 | 92 | return ' INNER JOIN ' . $joinTableName . ' ON ' . implode(' AND ', $conditions); |
|
1388 | } |
||
1389 | |||
1390 | /** |
||
1391 | * {@inheritdoc} |
||
1392 | */ |
||
1393 | 1023 | public function getInsertSQL() |
|
1394 | { |
||
1395 | 1023 | if ($this->insertSql !== null) { |
|
1396 | 83 | return $this->insertSql; |
|
1397 | } |
||
1398 | |||
1399 | 1023 | $columns = $this->getInsertColumnList(); |
|
1400 | 1023 | $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); |
|
1401 | |||
1402 | 1023 | if (empty($columns)) { |
|
1403 | 112 | $identityColumn = $this->quoteStrategy->getColumnName($this->class->identifier[0], $this->class, $this->platform); |
|
1404 | 112 | $this->insertSql = $this->platform->getEmptyIdentityInsertSQL($tableName, $identityColumn); |
|
1405 | |||
1406 | 112 | return $this->insertSql; |
|
1407 | } |
||
1408 | |||
1409 | 998 | $values = []; |
|
1410 | 998 | $columns = array_unique($columns); |
|
1411 | |||
1412 | 998 | foreach ($columns as $column) { |
|
1413 | 998 | $placeholder = '?'; |
|
1414 | |||
1415 | 998 | if (isset($this->class->fieldNames[$column]) |
|
1416 | 998 | && isset($this->columnTypes[$this->class->fieldNames[$column]]) |
|
1417 | 998 | && isset($this->class->fieldMappings[$this->class->fieldNames[$column]]['requireSQLConversion'])) { |
|
1418 | 6 | $type = Type::getType($this->columnTypes[$this->class->fieldNames[$column]]); |
|
1419 | 6 | $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform); |
|
1420 | } |
||
1421 | |||
1422 | 998 | $values[] = $placeholder; |
|
1423 | } |
||
1424 | |||
1425 | 998 | $columns = implode(', ', $columns); |
|
1426 | 998 | $values = implode(', ', $values); |
|
1427 | |||
1428 | 998 | $this->insertSql = sprintf('INSERT INTO %s (%s) VALUES (%s)', $tableName, $columns, $values); |
|
1429 | |||
1430 | 998 | return $this->insertSql; |
|
1431 | } |
||
1432 | |||
1433 | /** |
||
1434 | * Gets the list of columns to put in the INSERT SQL statement. |
||
1435 | * |
||
1436 | * Subclasses should override this method to alter or change the list of |
||
1437 | * columns placed in the INSERT statements used by the persister. |
||
1438 | * |
||
1439 | * @return array The list of columns. |
||
1440 | */ |
||
1441 | 955 | protected function getInsertColumnList() |
|
1442 | { |
||
1443 | 955 | $columns = []; |
|
1444 | |||
1445 | 955 | foreach ($this->class->reflFields as $name => $field) { |
|
1446 | 955 | if ($this->class->isVersioned && $this->class->versionField == $name) { |
|
1447 | 200 | continue; |
|
1448 | } |
||
1449 | |||
1450 | 955 | if (isset($this->class->embeddedClasses[$name])) { |
|
1451 | 8 | continue; |
|
1452 | } |
||
1453 | |||
1454 | 955 | if (isset($this->class->associationMappings[$name])) { |
|
1455 | 836 | $assoc = $this->class->associationMappings[$name]; |
|
1456 | |||
1457 | 836 | if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { |
|
1458 | 788 | View Code Duplication | foreach ($assoc['joinColumns'] as $joinColumn) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1459 | 788 | $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); |
|
1460 | } |
||
1461 | } |
||
1462 | |||
1463 | 836 | continue; |
|
1464 | } |
||
1465 | |||
1466 | 955 | if (! $this->class->isIdGeneratorIdentity() || $this->class->identifier[0] != $name) { |
|
1467 | 885 | $columns[] = $this->quoteStrategy->getColumnName($name, $this->class, $this->platform); |
|
1468 | 955 | $this->columnTypes[$name] = $this->class->fieldMappings[$name]['type']; |
|
1469 | } |
||
1470 | } |
||
1471 | |||
1472 | 955 | return $columns; |
|
1473 | } |
||
1474 | |||
1475 | /** |
||
1476 | * Gets the SQL snippet of a qualified column name for the given field name. |
||
1477 | * |
||
1478 | * @param string $field The field name. |
||
1479 | * @param ClassMetadata $class The class that declares this field. The table this class is |
||
1480 | * mapped to must own the column for the given field. |
||
1481 | * @param string $alias |
||
1482 | * |
||
1483 | * @return string |
||
1484 | */ |
||
1485 | 518 | View Code Duplication | protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') |
1486 | { |
||
1487 | 518 | $root = $alias == 'r' ? '' : $alias ; |
|
1488 | 518 | $tableAlias = $this->getSQLTableAlias($class->name, $root); |
|
1489 | 518 | $fieldMapping = $class->fieldMappings[$field]; |
|
1490 | 518 | $sql = sprintf('%s.%s', $tableAlias, $this->quoteStrategy->getColumnName($field, $class, $this->platform)); |
|
1491 | 518 | $columnAlias = $this->getSQLColumnAlias($fieldMapping['columnName']); |
|
1492 | |||
1493 | 518 | $this->currentPersisterContext->rsm->addFieldResult($alias, $columnAlias, $field); |
|
1494 | |||
1495 | 518 | if (isset($fieldMapping['requireSQLConversion'])) { |
|
1496 | 3 | $type = Type::getType($fieldMapping['type']); |
|
1497 | 3 | $sql = $type->convertToPHPValueSQL($sql, $this->platform); |
|
1498 | } |
||
1499 | |||
1500 | 518 | return $sql . ' AS ' . $columnAlias; |
|
1501 | } |
||
1502 | |||
1503 | /** |
||
1504 | * Gets the SQL table alias for the given class name. |
||
1505 | * |
||
1506 | * @param string $className |
||
1507 | * @param string $assocName |
||
1508 | * |
||
1509 | * @return string The SQL table alias. |
||
1510 | * |
||
1511 | * @todo Reconsider. Binding table aliases to class names is not such a good idea. |
||
1512 | */ |
||
1513 | 624 | protected function getSQLTableAlias($className, $assocName = '') |
|
1514 | { |
||
1515 | 624 | if ($assocName) { |
|
1516 | 182 | $className .= '#' . $assocName; |
|
1517 | } |
||
1518 | |||
1519 | 624 | if (isset($this->currentPersisterContext->sqlTableAliases[$className])) { |
|
1520 | 620 | return $this->currentPersisterContext->sqlTableAliases[$className]; |
|
1521 | } |
||
1522 | |||
1523 | 624 | $tableAlias = 't' . $this->currentPersisterContext->sqlAliasCounter++; |
|
1524 | |||
1525 | 624 | $this->currentPersisterContext->sqlTableAliases[$className] = $tableAlias; |
|
1526 | |||
1527 | 624 | return $tableAlias; |
|
1528 | } |
||
1529 | |||
1530 | /** |
||
1531 | * {@inheritdoc} |
||
1532 | */ |
||
1533 | public function lock(array $criteria, $lockMode) |
||
1534 | { |
||
1535 | $lockSql = ''; |
||
1536 | $conditionSql = $this->getSelectConditionSQL($criteria); |
||
1537 | |||
1538 | View Code Duplication | switch ($lockMode) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1539 | case LockMode::PESSIMISTIC_READ: |
||
1540 | $lockSql = $this->platform->getReadLockSQL(); |
||
1541 | |||
1542 | break; |
||
1543 | case LockMode::PESSIMISTIC_WRITE: |
||
0 ignored issues
–
show
The case body in a switch statement must start on the line following the statement.
According to the PSR-2, the body of a case statement must start on the line immediately following the case statement. switch ($expr) {
case "A":
doSomething(); //right
break;
case "B":
doSomethingElse(); //wrong
break;
} To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
1544 | |||
1545 | $lockSql = $this->platform->getWriteLockSQL(); |
||
1546 | break; |
||
1547 | } |
||
1548 | |||
1549 | $lock = $this->getLockTablesSql($lockMode); |
||
1550 | $where = ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' '; |
||
1551 | $sql = 'SELECT 1 ' |
||
1552 | . $lock |
||
1553 | . $where |
||
1554 | . $lockSql; |
||
1555 | |||
1556 | list($params, $types) = $this->expandParameters($criteria); |
||
1557 | |||
1558 | $this->conn->executeQuery($sql, $params, $types); |
||
1559 | } |
||
1560 | |||
1561 | /** |
||
1562 | * Gets the FROM and optionally JOIN conditions to lock the entity managed by this persister. |
||
1563 | * |
||
1564 | * @param integer $lockMode One of the Doctrine\DBAL\LockMode::* constants. |
||
1565 | * |
||
1566 | * @return string |
||
1567 | */ |
||
1568 | 13 | protected function getLockTablesSql($lockMode) |
|
1569 | { |
||
1570 | 13 | return $this->platform->appendLockHint( |
|
1571 | 'FROM ' |
||
1572 | 13 | . $this->quoteStrategy->getTableName($this->class, $this->platform) . ' ' |
|
1573 | 13 | . $this->getSQLTableAlias($this->class->name), |
|
1574 | 13 | $lockMode |
|
1575 | ); |
||
1576 | } |
||
1577 | |||
1578 | /** |
||
1579 | * Gets the Select Where Condition from a Criteria object. |
||
1580 | * |
||
1581 | * @param \Doctrine\Common\Collections\Criteria $criteria |
||
1582 | * |
||
1583 | * @return string |
||
1584 | */ |
||
1585 | 58 | protected function getSelectConditionCriteriaSQL(Criteria $criteria) |
|
1586 | { |
||
1587 | 58 | $expression = $criteria->getWhereExpression(); |
|
1588 | |||
1589 | 58 | if ($expression === null) { |
|
1590 | 2 | return ''; |
|
1591 | } |
||
1592 | |||
1593 | 57 | $visitor = new SqlExpressionVisitor($this, $this->class); |
|
1594 | |||
1595 | 57 | return $visitor->dispatch($expression); |
|
1596 | } |
||
1597 | |||
1598 | /** |
||
1599 | * {@inheritdoc} |
||
1600 | */ |
||
1601 | 603 | public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null) |
|
1602 | { |
||
1603 | 603 | $selectedColumns = []; |
|
1604 | 603 | $columns = $this->getSelectConditionStatementColumnSQL($field, $assoc); |
|
1605 | |||
1606 | 599 | if (count($columns) > 1 && $comparison === Comparison::IN) { |
|
1607 | /* |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
37% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
1608 | * @todo try to support multi-column IN expressions. |
||
1609 | * Example: (col1, col2) IN (('val1A', 'val2A'), ('val1B', 'val2B')) |
||
1610 | */ |
||
1611 | 1 | throw ORMException::cantUseInOperatorOnCompositeKeys(); |
|
1612 | } |
||
1613 | |||
1614 | 598 | foreach ($columns as $column) { |
|
1615 | 598 | $placeholder = '?'; |
|
1616 | |||
1617 | 598 | View Code Duplication | if (isset($this->class->fieldMappings[$field]['requireSQLConversion'])) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1618 | 1 | $type = Type::getType($this->class->fieldMappings[$field]['type']); |
|
1619 | 1 | $placeholder = $type->convertToDatabaseValueSQL($placeholder, $this->platform); |
|
1620 | } |
||
1621 | |||
1622 | 598 | if (null !== $comparison) { |
|
1623 | // special case null value handling |
||
1624 | 61 | View Code Duplication | if (($comparison === Comparison::EQ || $comparison === Comparison::IS) && null ===$value) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1625 | 6 | $selectedColumns[] = $column . ' IS NULL'; |
|
1626 | |||
1627 | 6 | continue; |
|
1628 | } |
||
1629 | |||
1630 | 55 | View Code Duplication | if ($comparison === Comparison::NEQ && null === $value) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1631 | 3 | $selectedColumns[] = $column . ' IS NOT NULL'; |
|
1632 | |||
1633 | 3 | continue; |
|
1634 | } |
||
1635 | |||
1636 | 52 | $selectedColumns[] = $column . ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder); |
|
1637 | |||
1638 | 52 | continue; |
|
1639 | } |
||
1640 | |||
1641 | 570 | if (is_array($value)) { |
|
1642 | 14 | $in = sprintf('%s IN (%s)', $column, $placeholder); |
|
1643 | |||
1644 | 14 | if (false !== array_search(null, $value, true)) { |
|
1645 | 4 | $selectedColumns[] = sprintf('(%s OR %s IS NULL)', $in, $column); |
|
1646 | |||
1647 | 4 | continue; |
|
1648 | } |
||
1649 | |||
1650 | 10 | $selectedColumns[] = $in; |
|
1651 | |||
1652 | 10 | continue; |
|
1653 | } |
||
1654 | |||
1655 | 559 | if (null === $value) { |
|
1656 | 9 | $selectedColumns[] = sprintf('%s IS NULL', $column); |
|
1657 | |||
1658 | 9 | continue; |
|
1659 | } |
||
1660 | |||
1661 | 551 | $selectedColumns[] = sprintf('%s = %s', $column, $placeholder); |
|
1662 | } |
||
1663 | |||
1664 | 598 | return implode(' AND ', $selectedColumns); |
|
1665 | } |
||
1666 | |||
1667 | /** |
||
1668 | * Builds the left-hand-side of a where condition statement. |
||
1669 | * |
||
1670 | * @param string $field |
||
1671 | * @param array|null $assoc |
||
1672 | * |
||
1673 | * @return string[] |
||
1674 | * |
||
1675 | * @throws \Doctrine\ORM\ORMException |
||
1676 | */ |
||
1677 | 603 | private function getSelectConditionStatementColumnSQL($field, $assoc = null) |
|
1678 | { |
||
1679 | 603 | if (isset($this->class->fieldMappings[$field])) { |
|
1680 | 508 | $className = (isset($this->class->fieldMappings[$field]['inherited'])) |
|
1681 | 54 | ? $this->class->fieldMappings[$field]['inherited'] |
|
1682 | 508 | : $this->class->name; |
|
1683 | |||
1684 | 508 | return [$this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getColumnName($field, $this->class, $this->platform)]; |
|
1685 | } |
||
1686 | |||
1687 | 294 | if (isset($this->class->associationMappings[$field])) { |
|
1688 | 147 | $association = $this->class->associationMappings[$field]; |
|
1689 | // Many-To-Many requires join table check for joinColumn |
||
1690 | 147 | $columns = []; |
|
1691 | 147 | $class = $this->class; |
|
1692 | |||
1693 | 147 | if ($association['type'] === ClassMetadata::MANY_TO_MANY) { |
|
1694 | 3 | if ( ! $association['isOwningSide']) { |
|
1695 | 2 | $association = $assoc; |
|
1696 | } |
||
1697 | |||
1698 | 3 | $joinTableName = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform); |
|
1699 | 3 | $joinColumns = $assoc['isOwningSide'] |
|
1700 | 2 | ? $association['joinTable']['joinColumns'] |
|
1701 | 3 | : $association['joinTable']['inverseJoinColumns']; |
|
1702 | |||
1703 | |||
1704 | 3 | foreach ($joinColumns as $joinColumn) { |
|
1705 | 3 | $columns[] = $joinTableName . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); |
|
1706 | } |
||
1707 | |||
1708 | } else { |
||
1709 | 145 | if ( ! $association['isOwningSide']) { |
|
1710 | 1 | throw ORMException::invalidFindByInverseAssociation($this->class->name, $field); |
|
1711 | } |
||
1712 | |||
1713 | 144 | $className = (isset($association['inherited'])) |
|
1714 | 11 | ? $association['inherited'] |
|
1715 | 144 | : $this->class->name; |
|
1716 | |||
1717 | 144 | View Code Duplication | foreach ($association['joinColumns'] as $joinColumn) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1718 | 144 | $columns[] = $this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); |
|
1719 | } |
||
1720 | } |
||
1721 | 146 | return $columns; |
|
1722 | } |
||
1723 | |||
1724 | 163 | if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) { |
|
1725 | // very careless developers could potentially open up this normally hidden api for userland attacks, |
||
1726 | // therefore checking for spaces and function calls which are not allowed. |
||
1727 | |||
1728 | // found a join column condition, not really a "field" |
||
1729 | 160 | return [$field]; |
|
1730 | } |
||
1731 | |||
1732 | 3 | throw ORMException::unrecognizedField($field); |
|
1733 | } |
||
1734 | |||
1735 | /** |
||
1736 | * Gets the conditional SQL fragment used in the WHERE clause when selecting |
||
1737 | * entities in this persister. |
||
1738 | * |
||
1739 | * Subclasses are supposed to override this method if they intend to change |
||
1740 | * or alter the criteria by which entities are selected. |
||
1741 | * |
||
1742 | * @param array $criteria |
||
1743 | * @param array|null $assoc |
||
1744 | * |
||
1745 | * @return string |
||
1746 | */ |
||
1747 | 597 | protected function getSelectConditionSQL(array $criteria, $assoc = null) |
|
1748 | { |
||
1749 | 597 | $conditions = []; |
|
1750 | |||
1751 | 597 | foreach ($criteria as $field => $value) { |
|
1752 | 572 | $conditions[] = $this->getSelectConditionStatementSQL($field, $value, $assoc); |
|
1753 | } |
||
1754 | |||
1755 | 594 | return implode(' AND ', $conditions); |
|
1756 | } |
||
1757 | |||
1758 | /** |
||
1759 | * {@inheritdoc} |
||
1760 | */ |
||
1761 | 5 | View Code Duplication | public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) |
1762 | { |
||
1763 | 5 | $this->switchPersisterContext($offset, $limit); |
|
1764 | |||
1765 | 5 | $stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit); |
|
1766 | |||
1767 | 5 | return $this->loadArrayFromStatement($assoc, $stmt); |
|
1768 | } |
||
1769 | |||
1770 | /** |
||
1771 | * {@inheritdoc} |
||
1772 | */ |
||
1773 | 76 | public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) |
|
1774 | { |
||
1775 | 76 | $stmt = $this->getOneToManyStatement($assoc, $sourceEntity); |
|
1776 | |||
1777 | 76 | return $this->loadCollectionFromStatement($assoc, $stmt, $coll); |
|
1778 | } |
||
1779 | |||
1780 | /** |
||
1781 | * Builds criteria and execute SQL statement to fetch the one to many entities from. |
||
1782 | * |
||
1783 | * @param array $assoc |
||
1784 | * @param object $sourceEntity |
||
1785 | * @param int|null $offset |
||
1786 | * @param int|null $limit |
||
1787 | * |
||
1788 | * @return \Doctrine\DBAL\Statement |
||
1789 | */ |
||
1790 | 81 | private function getOneToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null) |
|
1791 | { |
||
1792 | 81 | $this->switchPersisterContext($offset, $limit); |
|
1793 | |||
1794 | 81 | $criteria = []; |
|
1795 | 81 | $parameters = []; |
|
1796 | 81 | $owningAssoc = $this->class->associationMappings[$assoc['mappedBy']]; |
|
1797 | 81 | $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']); |
|
1798 | 81 | $tableAlias = $this->getSQLTableAlias(isset($owningAssoc['inherited']) ? $owningAssoc['inherited'] : $this->class->name); |
|
1799 | |||
1800 | 81 | foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { |
|
1801 | 81 | if ($sourceClass->containsForeignIdentifier) { |
|
0 ignored issues
–
show
Accessing
containsForeignIdentifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1802 | 3 | $field = $sourceClass->getFieldForColumn($sourceKeyColumn); |
|
1803 | 3 | $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); |
|
0 ignored issues
–
show
Accessing
reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1804 | |||
1805 | 3 | View Code Duplication | if (isset($sourceClass->associationMappings[$field])) { |
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1806 | 3 | $value = $this->em->getUnitOfWork()->getEntityIdentifier($value); |
|
1807 | 3 | $value = $value[$this->em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; |
|
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() Accessing
identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1808 | } |
||
1809 | |||
1810 | 3 | $criteria[$tableAlias . "." . $targetKeyColumn] = $value; |
|
1811 | 3 | $parameters[] = [ |
|
1812 | 3 | 'value' => $value, |
|
1813 | 3 | 'field' => $field, |
|
1814 | 3 | 'class' => $sourceClass, |
|
1815 | ]; |
||
1816 | |||
1817 | 3 | continue; |
|
1818 | } |
||
1819 | |||
1820 | 79 | $field = $sourceClass->fieldNames[$sourceKeyColumn]; |
|
0 ignored issues
–
show
Accessing
fieldNames on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1821 | 79 | $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); |
|
0 ignored issues
–
show
Accessing
reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1822 | |||
1823 | 79 | $criteria[$tableAlias . "." . $targetKeyColumn] = $value; |
|
1824 | 79 | $parameters[] = [ |
|
1825 | 79 | 'value' => $value, |
|
1826 | 79 | 'field' => $field, |
|
1827 | 79 | 'class' => $sourceClass, |
|
1828 | ]; |
||
1829 | |||
1830 | } |
||
1831 | |||
1832 | 81 | $sql = $this->getSelectSQL($criteria, $assoc, null, $limit, $offset); |
|
1833 | 81 | list($params, $types) = $this->expandToManyParameters($parameters); |
|
1834 | |||
1835 | 81 | return $this->conn->executeQuery($sql, $params, $types); |
|
1836 | } |
||
1837 | |||
1838 | /** |
||
1839 | * {@inheritdoc} |
||
1840 | */ |
||
1841 | 573 | public function expandParameters($criteria) |
|
1842 | { |
||
1843 | 573 | $params = []; |
|
1844 | 573 | $types = []; |
|
1845 | |||
1846 | 573 | foreach ($criteria as $field => $value) { |
|
1847 | 548 | if ($value === null) { |
|
1848 | 3 | continue; // skip null values. |
|
1849 | } |
||
1850 | |||
1851 | 546 | $types = array_merge($types, $this->getTypes($field, $value, $this->class)); |
|
1852 | 546 | $params = array_merge($params, $this->getValues($value)); |
|
1853 | } |
||
1854 | |||
1855 | 573 | return [$params, $types]; |
|
1856 | } |
||
1857 | |||
1858 | /** |
||
1859 | * Expands the parameters from the given criteria and use the correct binding types if found, |
||
1860 | * specialized for OneToMany or ManyToMany associations. |
||
1861 | * |
||
1862 | * @param mixed[][] $criteria an array of arrays containing following: |
||
1863 | * - field to which each criterion will be bound |
||
1864 | * - value to be bound |
||
1865 | * - class to which the field belongs to |
||
1866 | * |
||
1867 | * |
||
1868 | * @return array |
||
1869 | */ |
||
1870 | 156 | private function expandToManyParameters($criteria) |
|
1871 | { |
||
1872 | 156 | $params = []; |
|
1873 | 156 | $types = []; |
|
1874 | |||
1875 | 156 | foreach ($criteria as $criterion) { |
|
1876 | 156 | if ($criterion['value'] === null) { |
|
1877 | 6 | continue; // skip null values. |
|
1878 | } |
||
1879 | |||
1880 | 150 | $types = array_merge($types, $this->getTypes($criterion['field'], $criterion['value'], $criterion['class'])); |
|
1881 | 150 | $params = array_merge($params, $this->getValues($criterion['value'])); |
|
1882 | } |
||
1883 | |||
1884 | 156 | return [$params, $types]; |
|
1885 | } |
||
1886 | |||
1887 | /** |
||
1888 | * Infers field types to be used by parameter type casting. |
||
1889 | * |
||
1890 | * @param string $field |
||
1891 | * @param mixed $value |
||
1892 | * @param ClassMetadata $class |
||
1893 | * |
||
1894 | * @return array |
||
1895 | * |
||
1896 | * @throws \Doctrine\ORM\Query\QueryException |
||
1897 | */ |
||
1898 | 705 | private function getTypes($field, $value, ClassMetadata $class) |
|
1899 | { |
||
1900 | 705 | $types = []; |
|
1901 | |||
1902 | switch (true) { |
||
1903 | 705 | case (isset($class->fieldMappings[$field])): |
|
1904 | 646 | $types = array_merge($types, [$class->fieldMappings[$field]['type']]); |
|
1905 | 646 | break; |
|
1906 | |||
1907 | 146 | case (isset($class->associationMappings[$field])): |
|
1908 | 145 | $assoc = $class->associationMappings[$field]; |
|
1909 | 145 | $class = $this->em->getClassMetadata($assoc['targetEntity']); |
|
1910 | |||
1911 | 145 | View Code Duplication | if (! $assoc['isOwningSide']) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1912 | 2 | $assoc = $class->associationMappings[$assoc['mappedBy']]; |
|
0 ignored issues
–
show
Accessing
associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1913 | 2 | $class = $this->em->getClassMetadata($assoc['targetEntity']); |
|
1914 | } |
||
1915 | |||
1916 | 145 | $columns = $assoc['type'] === ClassMetadata::MANY_TO_MANY |
|
1917 | 3 | ? $assoc['relationToTargetKeyColumns'] |
|
1918 | 145 | : $assoc['sourceToTargetKeyColumns']; |
|
1919 | |||
1920 | 145 | foreach ($columns as $column){ |
|
1921 | 145 | $types[] = PersisterHelper::getTypeOfColumn($column, $class, $this->em); |
|
0 ignored issues
–
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata> . It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
1922 | } |
||
1923 | 145 | break; |
|
1924 | |||
1925 | default: |
||
1926 | 1 | $types[] = null; |
|
1927 | 1 | break; |
|
1928 | } |
||
1929 | |||
1930 | 705 | if (is_array($value)) { |
|
1931 | 16 | return array_map(function ($type) { |
|
1932 | 16 | $type = Type::getType($type); |
|
1933 | |||
1934 | 16 | return $type->getBindingType() + Connection::ARRAY_PARAM_OFFSET; |
|
1935 | 16 | }, $types); |
|
1936 | } |
||
1937 | |||
1938 | 695 | return $types; |
|
1939 | } |
||
1940 | |||
1941 | /** |
||
1942 | * Retrieves the parameters that identifies a value. |
||
1943 | * |
||
1944 | * @param mixed $value |
||
1945 | * |
||
1946 | * @return array |
||
1947 | */ |
||
1948 | 580 | private function getValues($value) |
|
1949 | { |
||
1950 | 580 | if (is_array($value)) { |
|
1951 | 16 | $newValue = []; |
|
1952 | |||
1953 | 16 | foreach ($value as $itemValue) { |
|
1954 | 16 | $newValue = array_merge($newValue, $this->getValues($itemValue)); |
|
1955 | } |
||
1956 | |||
1957 | 16 | return [$newValue]; |
|
1958 | } |
||
1959 | |||
1960 | 580 | if (is_object($value) && $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) { |
|
1961 | 44 | $class = $this->em->getClassMetadata(get_class($value)); |
|
1962 | 44 | if ($class->isIdentifierComposite) { |
|
0 ignored issues
–
show
Accessing
isIdentifierComposite on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
1963 | 3 | $newValue = []; |
|
1964 | |||
1965 | 3 | foreach ($class->getIdentifierValues($value) as $innerValue) { |
|
1966 | 3 | $newValue = array_merge($newValue, $this->getValues($innerValue)); |
|
1967 | } |
||
1968 | |||
1969 | 3 | return $newValue; |
|
1970 | } |
||
1971 | } |
||
1972 | |||
1973 | 580 | return [$this->getIndividualValue($value)]; |
|
1974 | } |
||
1975 | |||
1976 | /** |
||
1977 | * Retrieves an individual parameter value. |
||
1978 | * |
||
1979 | * @param mixed $value |
||
1980 | * |
||
1981 | * @return mixed |
||
1982 | */ |
||
1983 | 580 | private function getIndividualValue($value) |
|
1984 | { |
||
1985 | 580 | if ( ! is_object($value) || ! $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) { |
|
1986 | 577 | return $value; |
|
1987 | } |
||
1988 | |||
1989 | 44 | return $this->em->getUnitOfWork()->getSingleIdentifierValue($value); |
|
1990 | } |
||
1991 | |||
1992 | /** |
||
1993 | * {@inheritdoc} |
||
1994 | */ |
||
1995 | 14 | public function exists($entity, Criteria $extraConditions = null) |
|
1996 | { |
||
1997 | 14 | $criteria = $this->class->getIdentifierValues($entity); |
|
1998 | |||
1999 | 14 | if ( ! $criteria) { |
|
0 ignored issues
–
show
The expression
$criteria of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
2000 | 2 | return false; |
|
2001 | } |
||
2002 | |||
2003 | 13 | $alias = $this->getSQLTableAlias($this->class->name); |
|
2004 | |||
2005 | $sql = 'SELECT 1 ' |
||
2006 | 13 | . $this->getLockTablesSql(null) |
|
2007 | 13 | . ' WHERE ' . $this->getSelectConditionSQL($criteria); |
|
2008 | |||
2009 | 13 | list($params, $types) = $this->expandParameters($criteria); |
|
2010 | |||
2011 | 13 | if (null !== $extraConditions) { |
|
2012 | 9 | $sql .= ' AND ' . $this->getSelectConditionCriteriaSQL($extraConditions); |
|
2013 | 9 | list($criteriaParams, $criteriaTypes) = $this->expandCriteriaParameters($extraConditions); |
|
2014 | |||
2015 | 9 | $params = array_merge($params, $criteriaParams); |
|
2016 | 9 | $types = array_merge($types, $criteriaTypes); |
|
2017 | } |
||
2018 | |||
2019 | 13 | if ($filterSql = $this->generateFilterConditionSQL($this->class, $alias)) { |
|
2020 | 3 | $sql .= ' AND ' . $filterSql; |
|
2021 | } |
||
2022 | |||
2023 | 13 | return (bool) $this->conn->fetchColumn($sql, $params, 0, $types); |
|
2024 | } |
||
2025 | |||
2026 | /** |
||
2027 | * Generates the appropriate join SQL for the given join column. |
||
2028 | * |
||
2029 | * @param array $joinColumns The join columns definition of an association. |
||
2030 | * |
||
2031 | * @return string LEFT JOIN if one of the columns is nullable, INNER JOIN otherwise. |
||
2032 | */ |
||
2033 | 12 | protected function getJoinSQLForJoinColumns($joinColumns) |
|
2034 | { |
||
2035 | // if one of the join columns is nullable, return left join |
||
2036 | 12 | foreach ($joinColumns as $joinColumn) { |
|
2037 | 12 | if ( ! isset($joinColumn['nullable']) || $joinColumn['nullable']) { |
|
2038 | 12 | return 'LEFT JOIN'; |
|
2039 | } |
||
2040 | } |
||
2041 | |||
2042 | 5 | return 'INNER JOIN'; |
|
2043 | } |
||
2044 | |||
2045 | /** |
||
2046 | * {@inheritdoc} |
||
2047 | */ |
||
2048 | 591 | public function getSQLColumnAlias($columnName) |
|
2049 | { |
||
2050 | 591 | return $this->quoteStrategy->getColumnAlias($columnName, $this->currentPersisterContext->sqlAliasCounter++, $this->platform); |
|
2051 | } |
||
2052 | |||
2053 | /** |
||
2054 | * Generates the filter SQL for a given entity and table alias. |
||
2055 | * |
||
2056 | * @param ClassMetadata $targetEntity Metadata of the target entity. |
||
2057 | * @param string $targetTableAlias The table alias of the joined/selected table. |
||
2058 | * |
||
2059 | * @return string The SQL query part to add to a query. |
||
2060 | */ |
||
2061 | 615 | View Code Duplication | protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) |
2062 | { |
||
2063 | 615 | $filterClauses = []; |
|
2064 | |||
2065 | 615 | foreach ($this->em->getFilters()->getEnabledFilters() as $filter) { |
|
2066 | 22 | if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) { |
|
2067 | 22 | $filterClauses[] = '(' . $filterExpr . ')'; |
|
2068 | } |
||
2069 | } |
||
2070 | |||
2071 | 615 | $sql = implode(' AND ', $filterClauses); |
|
2072 | |||
2073 | 615 | return $sql ? "(" . $sql . ")" : ""; // Wrap again to avoid "X or Y and FilterConditionSQL" |
|
2074 | } |
||
2075 | |||
2076 | /** |
||
2077 | * Switches persister context according to current query offset/limits |
||
2078 | * |
||
2079 | * This is due to the fact that to-many associations cannot be fetch-joined when a limit is involved |
||
2080 | * |
||
2081 | * @param null|int $offset |
||
2082 | * @param null|int $limit |
||
2083 | */ |
||
2084 | 597 | protected function switchPersisterContext($offset, $limit) |
|
2085 | { |
||
2086 | 597 | if (null === $offset && null === $limit) { |
|
2087 | 584 | $this->currentPersisterContext = $this->noLimitsContext; |
|
2088 | |||
2089 | 584 | return; |
|
2090 | } |
||
2091 | |||
2092 | 42 | $this->currentPersisterContext = $this->limitsHandlingContext; |
|
2093 | 42 | } |
|
2094 | } |
||
2095 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.