composer package updates
[openemr.git] / vendor / doctrine / orm / lib / Doctrine / ORM / Persisters / Collection / ManyToManyPersister.php
blob38edb20f1c5e4edac393831bec641ad9f5f7ebc2
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.
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>.
20 namespace Doctrine\ORM\Persisters\Collection;
22 use Doctrine\Common\Collections\Criteria;
23 use Doctrine\ORM\Mapping\ClassMetadata;
24 use Doctrine\ORM\Persisters\SqlExpressionVisitor;
25 use Doctrine\ORM\Persisters\SqlValueVisitor;
26 use Doctrine\ORM\PersistentCollection;
27 use Doctrine\ORM\Query;
28 use Doctrine\ORM\Utility\PersisterHelper;
30 /**
31 * Persister for many-to-many collections.
33 * @author Roman Borschel <roman@code-factory.org>
34 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
35 * @author Alexander <iam.asm89@gmail.com>
36 * @since 2.0
38 class ManyToManyPersister extends AbstractCollectionPersister
40 /**
41 * {@inheritdoc}
43 public function delete(PersistentCollection $collection)
45 $mapping = $collection->getMapping();
47 if ( ! $mapping['isOwningSide']) {
48 return; // ignore inverse side
51 $this->conn->executeUpdate($this->getDeleteSQL($collection), $this->getDeleteSQLParameters($collection));
54 /**
55 * {@inheritdoc}
57 public function update(PersistentCollection $collection)
59 $mapping = $collection->getMapping();
61 if ( ! $mapping['isOwningSide']) {
62 return; // ignore inverse side
65 list($deleteSql, $deleteTypes) = $this->getDeleteRowSQL($collection);
66 list($insertSql, $insertTypes) = $this->getInsertRowSQL($collection);
68 foreach ($collection->getDeleteDiff() as $element) {
69 $this->conn->executeUpdate(
70 $deleteSql,
71 $this->getDeleteRowSQLParameters($collection, $element),
72 $deleteTypes
76 foreach ($collection->getInsertDiff() as $element) {
77 $this->conn->executeUpdate(
78 $insertSql,
79 $this->getInsertRowSQLParameters($collection, $element),
80 $insertTypes
85 /**
86 * {@inheritdoc}
88 public function get(PersistentCollection $collection, $index)
90 $mapping = $collection->getMapping();
92 if ( ! isset($mapping['indexBy'])) {
93 throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections.");
96 $persister = $this->uow->getEntityPersister($mapping['targetEntity']);
97 $mappedKey = $mapping['isOwningSide']
98 ? $mapping['inversedBy']
99 : $mapping['mappedBy'];
101 return $persister->load(array($mappedKey => $collection->getOwner(), $mapping['indexBy'] => $index), null, $mapping, array(), 0, 1);
105 * {@inheritdoc}
107 public function count(PersistentCollection $collection)
109 $conditions = array();
110 $params = array();
111 $types = array();
112 $mapping = $collection->getMapping();
113 $id = $this->uow->getEntityIdentifier($collection->getOwner());
114 $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
115 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
116 $association = ( ! $mapping['isOwningSide'])
117 ? $targetClass->associationMappings[$mapping['mappedBy']]
118 : $mapping;
120 $joinTableName = $this->quoteStrategy->getJoinTableName($association, $sourceClass, $this->platform);
121 $joinColumns = ( ! $mapping['isOwningSide'])
122 ? $association['joinTable']['inverseJoinColumns']
123 : $association['joinTable']['joinColumns'];
125 foreach ($joinColumns as $joinColumn) {
126 $columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $sourceClass, $this->platform);
127 $referencedName = $joinColumn['referencedColumnName'];
128 $conditions[] = 't.' . $columnName . ' = ?';
129 $params[] = $id[$sourceClass->getFieldForColumn($referencedName)];
130 $types[] = PersisterHelper::getTypeOfColumn($referencedName, $sourceClass, $this->em);
133 list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping);
135 if ($filterSql) {
136 $conditions[] = $filterSql;
139 // If there is a provided criteria, make part of conditions
140 // @todo Fix this. Current SQL returns something like:
142 /*if ($criteria && ($expression = $criteria->getWhereExpression()) !== null) {
143 // A join is needed on the target entity
144 $targetTableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);
145 $targetJoinSql = ' JOIN ' . $targetTableName . ' te'
146 . ' ON' . implode(' AND ', $this->getOnConditionSQL($association));
148 // And criteria conditions needs to be added
149 $persister = $this->uow->getEntityPersister($targetClass->name);
150 $visitor = new SqlExpressionVisitor($persister, $targetClass);
151 $conditions[] = $visitor->dispatch($expression);
153 $joinTargetEntitySQL = $targetJoinSql . $joinTargetEntitySQL;
156 $sql = 'SELECT COUNT(*)'
157 . ' FROM ' . $joinTableName . ' t'
158 . $joinTargetEntitySQL
159 . ' WHERE ' . implode(' AND ', $conditions);
161 return $this->conn->fetchColumn($sql, $params, 0, $types);
165 * {@inheritDoc}
167 public function slice(PersistentCollection $collection, $offset, $length = null)
169 $mapping = $collection->getMapping();
170 $persister = $this->uow->getEntityPersister($mapping['targetEntity']);
172 return $persister->getManyToManyCollection($mapping, $collection->getOwner(), $offset, $length);
175 * {@inheritdoc}
177 public function containsKey(PersistentCollection $collection, $key)
179 $mapping = $collection->getMapping();
181 if ( ! isset($mapping['indexBy'])) {
182 throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections.");
185 list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictionsWithKey($collection, $key, true);
187 $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
189 return (bool) $this->conn->fetchColumn($sql, $params, 0, $types);
193 * {@inheritDoc}
195 public function contains(PersistentCollection $collection, $element)
197 if ( ! $this->isValidEntityState($element)) {
198 return false;
201 list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, true);
203 $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
205 return (bool) $this->conn->fetchColumn($sql, $params, 0, $types);
209 * {@inheritDoc}
211 public function removeElement(PersistentCollection $collection, $element)
213 if ( ! $this->isValidEntityState($element)) {
214 return false;
217 list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, false);
219 $sql = 'DELETE FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
221 return (bool) $this->conn->executeUpdate($sql, $params, $types);
225 * {@inheritDoc}
227 public function loadCriteria(PersistentCollection $collection, Criteria $criteria)
229 $mapping = $collection->getMapping();
230 $owner = $collection->getOwner();
231 $ownerMetadata = $this->em->getClassMetadata(get_class($owner));
232 $whereClauses = $params = array();
234 foreach ($mapping['relationToSourceKeyColumns'] as $key => $value) {
235 $whereClauses[] = sprintf('t.%s = ?', $key);
236 $params[] = $ownerMetadata->getFieldValue($owner, $value);
239 $parameters = $this->expandCriteriaParameters($criteria);
241 foreach ($parameters as $parameter) {
242 list($name, $value, $operator) = $parameter;
244 $whereClauses[] = sprintf('te.%s %s ?', $name, $operator);
245 $params[] = $value;
248 $mapping = $collection->getMapping();
249 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
250 $tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);
251 $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $ownerMetadata, $this->platform);
252 $onConditions = $this->getOnConditionSQL($mapping);
254 $rsm = new Query\ResultSetMappingBuilder($this->em);
255 $rsm->addRootEntityFromClassMetadata($mapping['targetEntity'], 'te');
257 $sql = 'SELECT ' . $rsm->generateSelectClause()
258 . ' FROM ' . $tableName . ' te'
259 . ' JOIN ' . $joinTable . ' t ON'
260 . implode(' AND ', $onConditions)
261 . ' WHERE ' . implode(' AND ', $whereClauses);
263 $stmt = $this->conn->executeQuery($sql, $params);
265 return $this
266 ->em
267 ->newHydrator(Query::HYDRATE_OBJECT)
268 ->hydrateAll($stmt, $rsm);
272 * Generates the filter SQL for a given mapping.
274 * This method is not used for actually grabbing the related entities
275 * but when the extra-lazy collection methods are called on a filtered
276 * association. This is why besides the many to many table we also
277 * have to join in the actual entities table leading to additional
278 * JOIN.
280 * @param array $mapping Array containing mapping information.
282 * @return string[] ordered tuple:
283 * - JOIN condition to add to the SQL
284 * - WHERE condition to add to the SQL
286 public function getFilterSql($mapping)
288 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
289 $rootClass = $this->em->getClassMetadata($targetClass->rootEntityName);
290 $filterSql = $this->generateFilterConditionSQL($rootClass, 'te');
292 if ('' === $filterSql) {
293 return array('', '');
296 // A join is needed if there is filtering on the target entity
297 $tableName = $this->quoteStrategy->getTableName($rootClass, $this->platform);
298 $joinSql = ' JOIN ' . $tableName . ' te'
299 . ' ON' . implode(' AND ', $this->getOnConditionSQL($mapping));
301 return array($joinSql, $filterSql);
305 * Generates the filter SQL for a given entity and table alias.
307 * @param ClassMetadata $targetEntity Metadata of the target entity.
308 * @param string $targetTableAlias The table alias of the joined/selected table.
310 * @return string The SQL query part to add to a query.
312 protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
314 $filterClauses = array();
316 foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
317 if ($filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
318 $filterClauses[] = '(' . $filterExpr . ')';
322 return $filterClauses
323 ? '(' . implode(' AND ', $filterClauses) . ')'
324 : '';
328 * Generate ON condition
330 * @param array $mapping
332 * @return array
334 protected function getOnConditionSQL($mapping)
336 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
337 $association = ( ! $mapping['isOwningSide'])
338 ? $targetClass->associationMappings[$mapping['mappedBy']]
339 : $mapping;
341 $joinColumns = $mapping['isOwningSide']
342 ? $association['joinTable']['inverseJoinColumns']
343 : $association['joinTable']['joinColumns'];
345 $conditions = array();
347 foreach ($joinColumns as $joinColumn) {
348 $joinColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
349 $refColumnName = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
351 $conditions[] = ' t.' . $joinColumnName . ' = ' . 'te.' . $refColumnName;
354 return $conditions;
358 * {@inheritdoc}
360 * @override
362 protected function getDeleteSQL(PersistentCollection $collection)
364 $columns = array();
365 $mapping = $collection->getMapping();
366 $class = $this->em->getClassMetadata(get_class($collection->getOwner()));
367 $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform);
369 foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
370 $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
373 return 'DELETE FROM ' . $joinTable
374 . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
378 * {@inheritdoc}
380 * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteSql.
381 * @override
383 protected function getDeleteSQLParameters(PersistentCollection $collection)
385 $mapping = $collection->getMapping();
386 $identifier = $this->uow->getEntityIdentifier($collection->getOwner());
388 // Optimization for single column identifier
389 if (count($mapping['relationToSourceKeyColumns']) === 1) {
390 return array(reset($identifier));
393 // Composite identifier
394 $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
395 $params = array();
397 foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) {
398 $params[] = isset($sourceClass->fieldNames[$refColumnName])
399 ? $identifier[$sourceClass->fieldNames[$refColumnName]]
400 : $identifier[$sourceClass->getFieldForColumn($columnName)];
403 return $params;
407 * Gets the SQL statement used for deleting a row from the collection.
409 * @param \Doctrine\ORM\PersistentCollection $collection
411 * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array
412 * of types for bound parameters
414 protected function getDeleteRowSQL(PersistentCollection $collection)
416 $mapping = $collection->getMapping();
417 $class = $this->em->getClassMetadata($mapping['sourceEntity']);
418 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
419 $columns = array();
420 $types = array();
422 foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
423 $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
424 $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em);
427 foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
428 $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
429 $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
432 return array(
433 'DELETE FROM ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform)
434 . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?',
435 $types,
440 * Gets the SQL parameters for the corresponding SQL statement to delete the given
441 * element from the given collection.
443 * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteRowSql.
445 * @param \Doctrine\ORM\PersistentCollection $collection
446 * @param mixed $element
448 * @return array
450 protected function getDeleteRowSQLParameters(PersistentCollection $collection, $element)
452 return $this->collectJoinTableColumnParameters($collection, $element);
456 * Gets the SQL statement used for inserting a row in the collection.
458 * @param \Doctrine\ORM\PersistentCollection $collection
460 * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array
461 * of types for bound parameters
463 protected function getInsertRowSQL(PersistentCollection $collection)
465 $columns = array();
466 $types = array();
467 $mapping = $collection->getMapping();
468 $class = $this->em->getClassMetadata($mapping['sourceEntity']);
469 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
471 foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
472 $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
473 $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em);
476 foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
477 $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
478 $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
481 return array(
482 'INSERT INTO ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform)
483 . ' (' . implode(', ', $columns) . ')'
484 . ' VALUES'
485 . ' (' . implode(', ', array_fill(0, count($columns), '?')) . ')',
486 $types,
491 * Gets the SQL parameters for the corresponding SQL statement to insert the given
492 * element of the given collection into the database.
494 * Internal note: Order of the parameters must be the same as the order of the columns in getInsertRowSql.
496 * @param \Doctrine\ORM\PersistentCollection $collection
497 * @param mixed $element
499 * @return array
501 protected function getInsertRowSQLParameters(PersistentCollection $collection, $element)
503 return $this->collectJoinTableColumnParameters($collection, $element);
507 * Collects the parameters for inserting/deleting on the join table in the order
508 * of the join table columns as specified in ManyToManyMapping#joinTableColumns.
510 * @param \Doctrine\ORM\PersistentCollection $collection
511 * @param object $element
513 * @return array
515 private function collectJoinTableColumnParameters(PersistentCollection $collection, $element)
517 $params = array();
518 $mapping = $collection->getMapping();
519 $isComposite = count($mapping['joinTableColumns']) > 2;
521 $identifier1 = $this->uow->getEntityIdentifier($collection->getOwner());
522 $identifier2 = $this->uow->getEntityIdentifier($element);
524 if ($isComposite) {
525 $class1 = $this->em->getClassMetadata(get_class($collection->getOwner()));
526 $class2 = $collection->getTypeClass();
529 foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
530 $isRelationToSource = isset($mapping['relationToSourceKeyColumns'][$joinTableColumn]);
532 if ( ! $isComposite) {
533 $params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2);
535 continue;
538 if ($isRelationToSource) {
539 $params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
541 continue;
544 $params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
547 return $params;
551 * @param \Doctrine\ORM\PersistentCollection $collection
552 * @param string $key
553 * @param boolean $addFilters Whether the filter SQL should be included or not.
555 * @return array ordered vector:
556 * - quoted join table name
557 * - where clauses to be added for filtering
558 * - parameters to be bound for filtering
559 * - types of the parameters to be bound for filtering
561 private function getJoinTableRestrictionsWithKey(PersistentCollection $collection, $key, $addFilters)
563 $filterMapping = $collection->getMapping();
564 $mapping = $filterMapping;
565 $indexBy = $mapping['indexBy'];
566 $id = $this->uow->getEntityIdentifier($collection->getOwner());
567 $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
568 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
570 if (! $mapping['isOwningSide']) {
571 $associationSourceClass = $this->em->getClassMetadata($mapping['targetEntity']);
572 $mapping = $associationSourceClass->associationMappings[$mapping['mappedBy']];
573 $joinColumns = $mapping['joinTable']['joinColumns'];
574 $sourceRelationMode = 'relationToTargetKeyColumns';
575 $targetRelationMode = 'relationToSourceKeyColumns';
576 } else {
577 $associationSourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
578 $joinColumns = $mapping['joinTable']['inverseJoinColumns'];
579 $sourceRelationMode = 'relationToSourceKeyColumns';
580 $targetRelationMode = 'relationToTargetKeyColumns';
583 $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform). ' t';
584 $whereClauses = array();
585 $params = array();
586 $types = array();
588 $joinNeeded = ! in_array($indexBy, $targetClass->identifier);
590 if ($joinNeeded) { // extra join needed if indexBy is not a @id
591 $joinConditions = array();
593 foreach ($joinColumns as $joinTableColumn) {
594 $joinConditions[] = 't.' . $joinTableColumn['name'] . ' = tr.' . $joinTableColumn['referencedColumnName'];
597 $tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);
598 $quotedJoinTable .= ' JOIN ' . $tableName . ' tr ON ' . implode(' AND ', $joinConditions);
599 $columnName = $targetClass->getColumnName($indexBy);
601 $whereClauses[] = 'tr.' . $columnName . ' = ?';
602 $params[] = $key;
603 $types[] = PersisterHelper::getTypeOfColumn($columnName, $targetClass, $this->em);
606 foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
607 if (isset($mapping[$sourceRelationMode][$joinTableColumn])) {
608 $column = $mapping[$sourceRelationMode][$joinTableColumn];
609 $whereClauses[] = 't.' . $joinTableColumn . ' = ?';
610 $params[] = $sourceClass->containsForeignIdentifier
611 ? $id[$sourceClass->getFieldForColumn($column)]
612 : $id[$sourceClass->fieldNames[$column]];
613 $types[] = PersisterHelper::getTypeOfColumn($column, $sourceClass, $this->em);
614 } elseif ( ! $joinNeeded) {
615 $column = $mapping[$targetRelationMode][$joinTableColumn];
617 $whereClauses[] = 't.' . $joinTableColumn . ' = ?';
618 $params[] = $key;
619 $types[] = PersisterHelper::getTypeOfColumn($column, $targetClass, $this->em);
623 if ($addFilters) {
624 list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
626 if ($filterSql) {
627 $quotedJoinTable .= ' ' . $joinTargetEntitySQL;
628 $whereClauses[] = $filterSql;
632 return array($quotedJoinTable, $whereClauses, $params, $types);
636 * @param \Doctrine\ORM\PersistentCollection $collection
637 * @param object $element
638 * @param boolean $addFilters Whether the filter SQL should be included or not.
640 * @return array ordered vector:
641 * - quoted join table name
642 * - where clauses to be added for filtering
643 * - parameters to be bound for filtering
644 * - types of the parameters to be bound for filtering
646 private function getJoinTableRestrictions(PersistentCollection $collection, $element, $addFilters)
648 $filterMapping = $collection->getMapping();
649 $mapping = $filterMapping;
651 if ( ! $mapping['isOwningSide']) {
652 $sourceClass = $this->em->getClassMetadata($mapping['targetEntity']);
653 $targetClass = $this->em->getClassMetadata($mapping['sourceEntity']);
654 $sourceId = $this->uow->getEntityIdentifier($element);
655 $targetId = $this->uow->getEntityIdentifier($collection->getOwner());
657 $mapping = $sourceClass->associationMappings[$mapping['mappedBy']];
658 } else {
659 $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
660 $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
661 $sourceId = $this->uow->getEntityIdentifier($collection->getOwner());
662 $targetId = $this->uow->getEntityIdentifier($element);
665 $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform);
666 $whereClauses = array();
667 $params = array();
668 $types = array();
670 foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
671 $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?';
673 if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
674 $targetColumn = $mapping['relationToTargetKeyColumns'][$joinTableColumn];
675 $params[] = $targetId[$targetClass->getFieldForColumn($targetColumn)];
676 $types[] = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em);
678 continue;
681 // relationToSourceKeyColumns
682 $targetColumn = $mapping['relationToSourceKeyColumns'][$joinTableColumn];
683 $params[] = $sourceId[$sourceClass->getFieldForColumn($targetColumn)];
684 $types[] = PersisterHelper::getTypeOfColumn($targetColumn, $sourceClass, $this->em);
687 if ($addFilters) {
688 $quotedJoinTable .= ' t';
690 list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
692 if ($filterSql) {
693 $quotedJoinTable .= ' ' . $joinTargetEntitySQL;
694 $whereClauses[] = $filterSql;
698 return array($quotedJoinTable, $whereClauses, $params, $types);
702 * Expands Criteria Parameters by walking the expressions and grabbing all
703 * parameters and types from it.
705 * @param \Doctrine\Common\Collections\Criteria $criteria
707 * @return array
709 private function expandCriteriaParameters(Criteria $criteria)
711 $expression = $criteria->getWhereExpression();
713 if ($expression === null) {
714 return array();
717 $valueVisitor = new SqlValueVisitor();
719 $valueVisitor->dispatch($expression);
721 list($values, $types) = $valueVisitor->getParamsAndTypes();
723 return $types;