Complex classes like ManyToManyPersister often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ManyToManyPersister, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 40 | class ManyToManyPersister extends AbstractCollectionPersister |
||
| 41 | { |
||
| 42 | /** |
||
| 43 | * {@inheritdoc} |
||
| 44 | */ |
||
| 45 | 17 | public function delete(PersistentCollection $collection) |
|
| 46 | { |
||
| 47 | 17 | $mapping = $collection->getMapping(); |
|
| 48 | |||
| 49 | 17 | if ( ! $mapping['isOwningSide']) { |
|
| 50 | return; // ignore inverse side |
||
| 51 | } |
||
| 52 | |||
| 53 | 17 | $types = array(); |
|
| 54 | 17 | $class = $this->em->getClassMetadata($mapping['sourceEntity']); |
|
| 55 | |||
| 56 | 17 | foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { |
|
| 57 | 17 | $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em); |
|
|
|
|||
| 58 | } |
||
| 59 | |||
| 60 | 17 | $this->conn->executeUpdate($this->getDeleteSQL($collection), $this->getDeleteSQLParameters($collection), $types); |
|
| 61 | 17 | } |
|
| 62 | |||
| 63 | /** |
||
| 64 | * {@inheritdoc} |
||
| 65 | */ |
||
| 66 | 339 | public function update(PersistentCollection $collection) |
|
| 67 | { |
||
| 68 | 339 | $mapping = $collection->getMapping(); |
|
| 69 | |||
| 70 | 339 | if ( ! $mapping['isOwningSide']) { |
|
| 71 | 241 | return; // ignore inverse side |
|
| 72 | } |
||
| 73 | |||
| 74 | 338 | list($deleteSql, $deleteTypes) = $this->getDeleteRowSQL($collection); |
|
| 75 | 338 | list($insertSql, $insertTypes) = $this->getInsertRowSQL($collection); |
|
| 76 | |||
| 77 | 338 | foreach ($collection->getDeleteDiff() as $element) { |
|
| 78 | 12 | $this->conn->executeUpdate( |
|
| 79 | $deleteSql, |
||
| 80 | 12 | $this->getDeleteRowSQLParameters($collection, $element), |
|
| 81 | $deleteTypes |
||
| 82 | ); |
||
| 83 | } |
||
| 84 | |||
| 85 | 338 | foreach ($collection->getInsertDiff() as $element) { |
|
| 86 | 338 | $this->conn->executeUpdate( |
|
| 87 | $insertSql, |
||
| 88 | 338 | $this->getInsertRowSQLParameters($collection, $element), |
|
| 89 | $insertTypes |
||
| 90 | ); |
||
| 91 | } |
||
| 92 | 338 | } |
|
| 93 | |||
| 94 | /** |
||
| 95 | * {@inheritdoc} |
||
| 96 | */ |
||
| 97 | 3 | public function get(PersistentCollection $collection, $index) |
|
| 98 | { |
||
| 99 | 3 | $mapping = $collection->getMapping(); |
|
| 100 | |||
| 101 | 3 | if ( ! isset($mapping['indexBy'])) { |
|
| 102 | throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); |
||
| 103 | } |
||
| 104 | |||
| 105 | 3 | $persister = $this->uow->getEntityPersister($mapping['targetEntity']); |
|
| 106 | 3 | $mappedKey = $mapping['isOwningSide'] |
|
| 107 | 2 | ? $mapping['inversedBy'] |
|
| 108 | 3 | : $mapping['mappedBy']; |
|
| 109 | |||
| 110 | 3 | return $persister->load(array($mappedKey => $collection->getOwner(), $mapping['indexBy'] => $index), null, $mapping, array(), 0, 1); |
|
| 111 | } |
||
| 112 | |||
| 113 | /** |
||
| 114 | * {@inheritdoc} |
||
| 115 | */ |
||
| 116 | 18 | public function count(PersistentCollection $collection) |
|
| 117 | { |
||
| 118 | 18 | $conditions = array(); |
|
| 119 | 18 | $params = array(); |
|
| 120 | 18 | $types = array(); |
|
| 121 | 18 | $mapping = $collection->getMapping(); |
|
| 122 | 18 | $id = $this->uow->getEntityIdentifier($collection->getOwner()); |
|
| 123 | 18 | $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); |
|
| 124 | 18 | $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); |
|
| 125 | 18 | $association = ( ! $mapping['isOwningSide']) |
|
| 126 | 4 | ? $targetClass->associationMappings[$mapping['mappedBy']] |
|
| 127 | 18 | : $mapping; |
|
| 128 | |||
| 129 | 18 | $joinTableName = $this->quoteStrategy->getJoinTableName($association, $sourceClass, $this->platform); |
|
| 130 | 18 | $joinColumns = ( ! $mapping['isOwningSide']) |
|
| 131 | 4 | ? $association['joinTable']['inverseJoinColumns'] |
|
| 132 | 18 | : $association['joinTable']['joinColumns']; |
|
| 133 | |||
| 134 | 18 | foreach ($joinColumns as $joinColumn) { |
|
| 135 | 18 | $columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $sourceClass, $this->platform); |
|
| 136 | 18 | $referencedName = $joinColumn['referencedColumnName']; |
|
| 137 | 18 | $conditions[] = 't.' . $columnName . ' = ?'; |
|
| 138 | 18 | $params[] = $id[$sourceClass->getFieldForColumn($referencedName)]; |
|
| 139 | 18 | $types[] = PersisterHelper::getTypeOfColumn($referencedName, $sourceClass, $this->em); |
|
| 140 | } |
||
| 141 | |||
| 142 | 18 | list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping); |
|
| 143 | |||
| 144 | 18 | if ($filterSql) { |
|
| 145 | 3 | $conditions[] = $filterSql; |
|
| 146 | } |
||
| 147 | |||
| 148 | // If there is a provided criteria, make part of conditions |
||
| 149 | // @todo Fix this. Current SQL returns something like: |
||
| 150 | // |
||
| 151 | /*if ($criteria && ($expression = $criteria->getWhereExpression()) !== null) { |
||
| 152 | // A join is needed on the target entity |
||
| 153 | $targetTableName = $this->quoteStrategy->getTableName($targetClass, $this->platform); |
||
| 154 | $targetJoinSql = ' JOIN ' . $targetTableName . ' te' |
||
| 155 | . ' ON' . implode(' AND ', $this->getOnConditionSQL($association)); |
||
| 156 | |||
| 157 | // And criteria conditions needs to be added |
||
| 158 | $persister = $this->uow->getEntityPersister($targetClass->name); |
||
| 159 | $visitor = new SqlExpressionVisitor($persister, $targetClass); |
||
| 160 | $conditions[] = $visitor->dispatch($expression); |
||
| 161 | |||
| 162 | $joinTargetEntitySQL = $targetJoinSql . $joinTargetEntitySQL; |
||
| 163 | }*/ |
||
| 164 | |||
| 165 | $sql = 'SELECT COUNT(*)' |
||
| 166 | 18 | . ' FROM ' . $joinTableName . ' t' |
|
| 167 | 18 | . $joinTargetEntitySQL |
|
| 168 | 18 | . ' WHERE ' . implode(' AND ', $conditions); |
|
| 169 | |||
| 170 | 18 | return $this->conn->fetchColumn($sql, $params, 0, $types); |
|
| 171 | } |
||
| 172 | |||
| 173 | /** |
||
| 174 | * {@inheritDoc} |
||
| 175 | */ |
||
| 176 | 8 | public function slice(PersistentCollection $collection, $offset, $length = null) |
|
| 177 | { |
||
| 178 | 8 | $mapping = $collection->getMapping(); |
|
| 179 | 8 | $persister = $this->uow->getEntityPersister($mapping['targetEntity']); |
|
| 180 | |||
| 181 | 8 | return $persister->getManyToManyCollection($mapping, $collection->getOwner(), $offset, $length); |
|
| 182 | } |
||
| 183 | /** |
||
| 184 | * {@inheritdoc} |
||
| 185 | */ |
||
| 186 | 7 | public function containsKey(PersistentCollection $collection, $key) |
|
| 187 | { |
||
| 188 | 7 | $mapping = $collection->getMapping(); |
|
| 189 | |||
| 190 | 7 | if ( ! isset($mapping['indexBy'])) { |
|
| 191 | throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); |
||
| 192 | } |
||
| 193 | |||
| 194 | 7 | list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictionsWithKey($collection, $key, true); |
|
| 195 | |||
| 196 | 7 | $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); |
|
| 197 | |||
| 198 | 7 | return (bool) $this->conn->fetchColumn($sql, $params, 0, $types); |
|
| 199 | } |
||
| 200 | |||
| 201 | /** |
||
| 202 | * {@inheritDoc} |
||
| 203 | */ |
||
| 204 | 7 | public function contains(PersistentCollection $collection, $element) |
|
| 205 | { |
||
| 206 | 7 | if ( ! $this->isValidEntityState($element)) { |
|
| 207 | 2 | return false; |
|
| 208 | } |
||
| 209 | |||
| 210 | 7 | list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, true); |
|
| 211 | |||
| 212 | 7 | $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); |
|
| 213 | |||
| 214 | 7 | return (bool) $this->conn->fetchColumn($sql, $params, 0, $types); |
|
| 215 | } |
||
| 216 | |||
| 217 | /** |
||
| 218 | * {@inheritDoc} |
||
| 219 | */ |
||
| 220 | 2 | public function removeElement(PersistentCollection $collection, $element) |
|
| 221 | { |
||
| 222 | 2 | if ( ! $this->isValidEntityState($element)) { |
|
| 223 | 2 | return false; |
|
| 224 | } |
||
| 225 | |||
| 226 | 2 | list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, false); |
|
| 227 | |||
| 228 | 2 | $sql = 'DELETE FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); |
|
|
1 ignored issue
–
show
|
|||
| 229 | |||
| 230 | 2 | return (bool) $this->conn->executeUpdate($sql, $params, $types); |
|
| 231 | } |
||
| 232 | |||
| 233 | /** |
||
| 234 | * {@inheritDoc} |
||
| 235 | */ |
||
| 236 | 17 | public function loadCriteria(PersistentCollection $collection, Criteria $criteria) |
|
| 237 | { |
||
| 238 | 17 | $mapping = $collection->getMapping(); |
|
| 239 | 17 | $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); |
|
| 240 | |||
| 241 | 17 | $queryBuilder = $this->createQueryBuilderForAssociation($collection); |
|
| 242 | |||
| 243 | 17 | if ($whereExpr = $criteria->getWhereExpression()) { |
|
| 244 | 12 | $mappingVisitor = new MappingVisitor( |
|
| 245 | 12 | $this->quoteStrategy, |
|
| 246 | $targetClass, |
||
| 247 | 12 | $this->platform |
|
| 248 | ); |
||
| 249 | |||
| 250 | 12 | $mappedExpr = $mappingVisitor->dispatch($criteria->getWhereExpression()); |
|
| 251 | |||
| 252 | 12 | $whereClauseExpressionVisitor = new QueryExpressionVisitor(['te']); |
|
| 253 | 12 | $whereExpr = $whereClauseExpressionVisitor->dispatch($mappedExpr); |
|
| 254 | 12 | $queryBuilder->where($whereExpr . ''); |
|
| 255 | |||
| 256 | /** @var Parameter $parameter */ |
||
| 257 | 12 | foreach($whereClauseExpressionVisitor->getParameters() as $parameter) { |
|
| 258 | 11 | $queryBuilder->setParameter($parameter->getName(), $parameter->getValue(), $parameter->getType()); |
|
| 259 | } |
||
| 260 | } |
||
| 261 | |||
| 262 | 17 | $this->applyCriteriaOrdering($queryBuilder, $criteria, $targetClass); |
|
| 263 | |||
| 264 | 17 | $this->applyCriteriaLimit($queryBuilder, $criteria); |
|
| 265 | |||
| 266 | 17 | $rsm = new Query\ResultSetMappingBuilder($this->em); |
|
| 267 | 17 | $rsm->addRootEntityFromClassMetadata($targetClass->name, 'te'); |
|
| 268 | |||
| 269 | 17 | $stmt = $queryBuilder->execute(); |
|
| 270 | |||
| 271 | return $this |
||
| 272 | 17 | ->em |
|
| 273 | 17 | ->newHydrator(Query::HYDRATE_OBJECT) |
|
| 274 | 17 | ->hydrateAll($stmt, $rsm); |
|
| 275 | } |
||
| 276 | |||
| 277 | /** |
||
| 278 | * Generates the filter SQL for a given mapping. |
||
| 279 | * |
||
| 280 | * This method is not used for actually grabbing the related entities |
||
| 281 | * but when the extra-lazy collection methods are called on a filtered |
||
| 282 | * association. This is why besides the many to many table we also |
||
| 283 | * have to join in the actual entities table leading to additional |
||
| 284 | * JOIN. |
||
| 285 | * |
||
| 286 | * @param array $mapping Array containing mapping information. |
||
| 287 | * |
||
| 288 | * @return string[] ordered tuple: |
||
| 289 | * - JOIN condition to add to the SQL |
||
| 290 | * - WHERE condition to add to the SQL |
||
| 291 | */ |
||
| 292 | 32 | public function getFilterSql($mapping) |
|
| 293 | { |
||
| 294 | 32 | $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); |
|
| 295 | 32 | $rootClass = $this->em->getClassMetadata($targetClass->rootEntityName); |
|
| 296 | 32 | $filterSql = $this->generateFilterConditionSQL($rootClass, 'te'); |
|
| 297 | |||
| 298 | 32 | if ('' === $filterSql) { |
|
| 299 | 32 | return array('', ''); |
|
| 300 | } |
||
| 301 | |||
| 302 | // A join is needed if there is filtering on the target entity |
||
| 303 | 6 | $tableName = $this->quoteStrategy->getTableName($rootClass, $this->platform); |
|
| 304 | 6 | $joinSql = ' JOIN ' . $tableName . ' te' |
|
| 305 | 6 | . ' ON' . implode(' AND ', $this->getOnConditionSQL($mapping)); |
|
| 306 | |||
| 307 | 6 | return array($joinSql, $filterSql); |
|
| 308 | } |
||
| 309 | |||
| 310 | /** |
||
| 311 | * Generates the filter SQL for a given entity and table alias. |
||
| 312 | * |
||
| 313 | * @param ClassMetadata $targetEntity Metadata of the target entity. |
||
| 314 | * @param string $targetTableAlias The table alias of the joined/selected table. |
||
| 315 | * |
||
| 316 | * @return string The SQL query part to add to a query. |
||
| 317 | */ |
||
| 318 | 32 | protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) |
|
| 332 | |||
| 333 | /** |
||
| 334 | * Generate ON condition |
||
| 335 | * |
||
| 336 | * @param array $mapping |
||
| 337 | * |
||
| 338 | * @return array |
||
| 339 | */ |
||
| 340 | 23 | protected function getOnConditionSQL($mapping) |
|
| 341 | { |
||
| 342 | 23 | $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); |
|
| 343 | 23 | $association = ( ! $mapping['isOwningSide']) |
|
| 344 | 3 | ? $targetClass->associationMappings[$mapping['mappedBy']] |
|
| 345 | 23 | : $mapping; |
|
| 346 | |||
| 347 | 23 | $joinColumns = $mapping['isOwningSide'] |
|
| 348 | 20 | ? $association['joinTable']['inverseJoinColumns'] |
|
| 349 | 23 | : $association['joinTable']['joinColumns']; |
|
| 350 | |||
| 351 | 23 | $conditions = array(); |
|
| 352 | |||
| 353 | 23 | foreach ($joinColumns as $joinColumn) { |
|
| 354 | 23 | $joinColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); |
|
| 355 | 23 | $refColumnName = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); |
|
| 356 | |||
| 357 | 23 | $conditions[] = ' t.' . $joinColumnName . ' = ' . 'te.' . $refColumnName; |
|
| 358 | } |
||
| 359 | |||
| 360 | 23 | return $conditions; |
|
| 361 | } |
||
| 362 | |||
| 363 | /** |
||
| 364 | * {@inheritdoc} |
||
| 365 | * |
||
| 366 | * @override |
||
| 367 | */ |
||
| 368 | 17 | protected function getDeleteSQL(PersistentCollection $collection) |
|
| 369 | { |
||
| 370 | 17 | $columns = array(); |
|
| 371 | 17 | $mapping = $collection->getMapping(); |
|
| 372 | 17 | $class = $this->em->getClassMetadata(get_class($collection->getOwner())); |
|
| 373 | 17 | $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform); |
|
| 374 | |||
| 375 | 17 | foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { |
|
| 376 | 17 | $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); |
|
| 377 | } |
||
| 378 | |||
| 379 | 17 | return 'DELETE FROM ' . $joinTable |
|
| 380 | 17 | . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; |
|
| 381 | } |
||
| 382 | |||
| 383 | /** |
||
| 384 | * {@inheritdoc} |
||
| 385 | * |
||
| 386 | * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteSql. |
||
| 387 | * @override |
||
| 388 | */ |
||
| 389 | 17 | protected function getDeleteSQLParameters(PersistentCollection $collection) |
|
| 390 | { |
||
| 391 | 17 | $mapping = $collection->getMapping(); |
|
| 392 | 17 | $identifier = $this->uow->getEntityIdentifier($collection->getOwner()); |
|
| 393 | |||
| 394 | // Optimization for single column identifier |
||
| 395 | 17 | if (count($mapping['relationToSourceKeyColumns']) === 1) { |
|
| 396 | 15 | return array(reset($identifier)); |
|
| 397 | } |
||
| 398 | |||
| 399 | // Composite identifier |
||
| 400 | 2 | $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); |
|
| 401 | 2 | $params = array(); |
|
| 402 | |||
| 403 | 2 | foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) { |
|
| 404 | 2 | $params[] = isset($sourceClass->fieldNames[$refColumnName]) |
|
| 405 | 1 | ? $identifier[$sourceClass->fieldNames[$refColumnName]] |
|
| 406 | 2 | : $identifier[$sourceClass->getFieldForColumn($columnName)]; |
|
| 407 | } |
||
| 408 | |||
| 409 | 2 | return $params; |
|
| 410 | } |
||
| 411 | |||
| 412 | /** |
||
| 413 | * Gets the SQL statement used for deleting a row from the collection. |
||
| 414 | * |
||
| 415 | * @param \Doctrine\ORM\PersistentCollection $collection |
||
| 416 | * |
||
| 417 | * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array |
||
| 418 | * of types for bound parameters |
||
| 419 | */ |
||
| 420 | 338 | protected function getDeleteRowSQL(PersistentCollection $collection) |
|
| 421 | { |
||
| 422 | 338 | $mapping = $collection->getMapping(); |
|
| 423 | 338 | $class = $this->em->getClassMetadata($mapping['sourceEntity']); |
|
| 424 | 338 | $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); |
|
| 425 | 338 | $columns = array(); |
|
| 426 | 338 | $types = array(); |
|
| 427 | |||
| 428 | 338 | foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { |
|
| 429 | 338 | $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); |
|
| 430 | 338 | $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em); |
|
| 431 | } |
||
| 432 | |||
| 433 | 338 | foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { |
|
| 434 | 338 | $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); |
|
| 435 | 338 | $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); |
|
| 436 | } |
||
| 437 | |||
| 438 | return array( |
||
| 439 | 338 | 'DELETE FROM ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform) |
|
| 440 | 338 | . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?', |
|
| 441 | 338 | $types, |
|
| 442 | ); |
||
| 443 | } |
||
| 444 | |||
| 445 | /** |
||
| 446 | * Gets the SQL parameters for the corresponding SQL statement to delete the given |
||
| 447 | * element from the given collection. |
||
| 448 | * |
||
| 449 | * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteRowSql. |
||
| 450 | * |
||
| 451 | * @param \Doctrine\ORM\PersistentCollection $collection |
||
| 452 | * @param mixed $element |
||
| 453 | * |
||
| 454 | * @return array |
||
| 455 | */ |
||
| 456 | 12 | protected function getDeleteRowSQLParameters(PersistentCollection $collection, $element) |
|
| 460 | |||
| 461 | /** |
||
| 462 | * Gets the SQL statement used for inserting a row in the collection. |
||
| 463 | * |
||
| 464 | * @param \Doctrine\ORM\PersistentCollection $collection |
||
| 465 | * |
||
| 466 | * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array |
||
| 467 | * of types for bound parameters |
||
| 468 | */ |
||
| 469 | 338 | protected function getInsertRowSQL(PersistentCollection $collection) |
|
| 470 | { |
||
| 471 | 338 | $columns = array(); |
|
| 472 | 338 | $types = array(); |
|
| 473 | 338 | $mapping = $collection->getMapping(); |
|
| 474 | 338 | $class = $this->em->getClassMetadata($mapping['sourceEntity']); |
|
| 475 | 338 | $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); |
|
| 476 | |||
| 477 | 338 | foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { |
|
| 478 | 338 | $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); |
|
| 479 | 338 | $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em); |
|
| 480 | } |
||
| 481 | |||
| 482 | 338 | foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { |
|
| 483 | 338 | $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); |
|
| 484 | 338 | $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); |
|
| 485 | } |
||
| 486 | |||
| 487 | return array( |
||
| 488 | 338 | 'INSERT INTO ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform) |
|
| 489 | 338 | . ' (' . implode(', ', $columns) . ')' |
|
| 490 | 338 | . ' VALUES' |
|
| 491 | 338 | . ' (' . implode(', ', array_fill(0, count($columns), '?')) . ')', |
|
| 492 | 338 | $types, |
|
| 493 | ); |
||
| 494 | } |
||
| 495 | |||
| 496 | /** |
||
| 497 | * Gets the SQL parameters for the corresponding SQL statement to insert the given |
||
| 498 | * element of the given collection into the database. |
||
| 499 | * |
||
| 500 | * Internal note: Order of the parameters must be the same as the order of the columns in getInsertRowSql. |
||
| 501 | * |
||
| 502 | * @param \Doctrine\ORM\PersistentCollection $collection |
||
| 503 | * @param mixed $element |
||
| 504 | * |
||
| 505 | * @return array |
||
| 506 | */ |
||
| 507 | 338 | protected function getInsertRowSQLParameters(PersistentCollection $collection, $element) |
|
| 511 | |||
| 512 | /** |
||
| 513 | * Collects the parameters for inserting/deleting on the join table in the order |
||
| 514 | * of the join table columns as specified in ManyToManyMapping#joinTableColumns. |
||
| 515 | * |
||
| 516 | * @param \Doctrine\ORM\PersistentCollection $collection |
||
| 517 | * @param object $element |
||
| 518 | * |
||
| 519 | * @return array |
||
| 520 | */ |
||
| 521 | 338 | private function collectJoinTableColumnParameters(PersistentCollection $collection, $element) |
|
| 555 | |||
| 556 | /** |
||
| 557 | * @param \Doctrine\ORM\PersistentCollection $collection |
||
| 558 | * @param string $key |
||
| 559 | * @param boolean $addFilters Whether the filter SQL should be included or not. |
||
| 560 | * |
||
| 561 | * @return array ordered vector: |
||
| 562 | * - quoted join table name |
||
| 563 | * - where clauses to be added for filtering |
||
| 564 | * - parameters to be bound for filtering |
||
| 565 | * - types of the parameters to be bound for filtering |
||
| 566 | */ |
||
| 567 | 7 | private function getJoinTableRestrictionsWithKey(PersistentCollection $collection, $key, $addFilters) |
|
| 640 | |||
| 641 | /** |
||
| 642 | * @param \Doctrine\ORM\PersistentCollection $collection |
||
| 643 | * @param object $element |
||
| 644 | * @param boolean $addFilters Whether the filter SQL should be included or not. |
||
| 645 | * |
||
| 646 | * @return array ordered vector: |
||
| 647 | * - quoted join table name |
||
| 648 | * - where clauses to be added for filtering |
||
| 649 | * - parameters to be bound for filtering |
||
| 650 | * - types of the parameters to be bound for filtering |
||
| 651 | */ |
||
| 652 | 9 | private function getJoinTableRestrictions(PersistentCollection $collection, $element, $addFilters) |
|
| 706 | |||
| 707 | /** |
||
| 708 | * @param QueryBuilder $queryBuilder |
||
| 709 | * @param Criteria $criteria |
||
| 710 | * @param ClassMetadata $targetClass |
||
| 711 | */ |
||
| 712 | 17 | private function applyCriteriaOrdering(QueryBuilder $queryBuilder, Criteria $criteria, ClassMetadata $targetClass) |
|
| 727 | |||
| 728 | /** |
||
| 729 | * @param QueryBuilder $queryBuilder |
||
| 730 | * @param Criteria $criteria |
||
| 731 | */ |
||
| 732 | 17 | private function applyCriteriaLimit(QueryBuilder $queryBuilder, Criteria $criteria) |
|
| 737 | |||
| 738 | /** |
||
| 739 | * @param PersistentCollection $collection |
||
| 740 | * @return \Doctrine\DBAL\Query\QueryBuilder |
||
| 741 | */ |
||
| 742 | 17 | private function createQueryBuilderForAssociation(PersistentCollection $collection) |
|
| 783 | } |
||
| 784 |
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.