diff --git a/lib/Doctrine/ORM/Cache/Persister/AbstractCollectionPersister.php b/lib/Doctrine/ORM/Cache/Persister/AbstractCollectionPersister.php index 12c234398..c6b377c07 100644 --- a/lib/Doctrine/ORM/Cache/Persister/AbstractCollectionPersister.php +++ b/lib/Doctrine/ORM/Cache/Persister/AbstractCollectionPersister.php @@ -226,22 +226,6 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister return $this->persister->count($collection); } - /** - * {@inheritdoc} - */ - public function deleteRows(PersistentCollection $collection) - { - $this->persister->deleteRows($collection); - } - - /** - * {@inheritdoc} - */ - public function insertRows(PersistentCollection $collection) - { - $this->persister->insertRows($collection); - } - /** * {@inheritdoc} */ diff --git a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php index 0b1dcfbc3..a541e3b6b 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php @@ -79,73 +79,7 @@ abstract class AbstractCollectionPersister implements CollectionPersister */ public function delete(PersistentCollection $coll) { - $mapping = $coll->getMapping(); - - if ( ! $mapping['isOwningSide']) { - return; // ignore inverse side - } - - $this->conn->executeUpdate($this->getDeleteSQL($coll), $this->getDeleteSQLParameters($coll)); - } - - /** - * Gets the SQL statement for deleting the given collection. - * - * @param \Doctrine\ORM\PersistentCollection $coll - * - * @return string - */ - abstract protected function getDeleteSQL(PersistentCollection $coll); - - /** - * Gets the SQL parameters for the corresponding SQL statement to delete - * the given collection. - * - * @param \Doctrine\ORM\PersistentCollection $coll - * - * @return array - */ - abstract protected function getDeleteSQLParameters(PersistentCollection $coll); - - /** - * {@inheritdoc} - */ - public function update(PersistentCollection $coll) - { - $mapping = $coll->getMapping(); - - if ( ! $mapping['isOwningSide']) { - return; // ignore inverse side - } - - $this->deleteRows($coll); - $this->insertRows($coll); - } - - /** - * {@inheritdoc} - */ - public function deleteRows(PersistentCollection $coll) - { - $diff = $coll->getDeleteDiff(); - $sql = $this->getDeleteRowSQL($coll); - - foreach ($diff as $element) { - $this->conn->executeUpdate($sql, $this->getDeleteRowSQLParameters($coll, $element)); - } - } - - /** - * {@inheritdoc} - */ - public function insertRows(PersistentCollection $coll) - { - $diff = $coll->getInsertDiff(); - $sql = $this->getInsertRowSQL($coll); - - foreach ($diff as $element) { - $this->conn->executeUpdate($sql, $this->getInsertRowSQLParameters($coll, $element)); - } + throw new \BadMethodCallException("Deleting elements is not supported by this CollectionPersister."); } /** @@ -211,53 +145,4 @@ abstract class AbstractCollectionPersister implements CollectionPersister { throw new \BadMethodCallException("Filtering a collection by Criteria is not supported by this CollectionPersister."); } - - /** - * Gets the SQL statement used for deleting a row from the collection. - * - * @param \Doctrine\ORM\PersistentCollection $coll - * - * @return string - */ - abstract protected function getDeleteRowSQL(PersistentCollection $coll); - - /** - * Gets the SQL parameters for the corresponding SQL statement to delete the given - * element from the given collection. - * - * @param \Doctrine\ORM\PersistentCollection $coll - * @param mixed $element - * - * @return array - */ - abstract protected function getDeleteRowSQLParameters(PersistentCollection $coll, $element); - - /** - * Gets the SQL statement used for updating a row in the collection. - * - * @param \Doctrine\ORM\PersistentCollection $coll - * - * @return string - */ - abstract protected function getUpdateRowSQL(PersistentCollection $coll); - - /** - * Gets the SQL statement used for inserting a row in the collection. - * - * @param \Doctrine\ORM\PersistentCollection $coll - * - * @return string - */ - abstract protected function getInsertRowSQL(PersistentCollection $coll); - - /** - * Gets the SQL parameters for the corresponding SQL statement to insert the given - * element of the given collection into the database. - * - * @param \Doctrine\ORM\PersistentCollection $coll - * @param mixed $element - * - * @return array - */ - abstract protected function getInsertRowSQLParameters(PersistentCollection $coll, $element); } diff --git a/lib/Doctrine/ORM/Persisters/CollectionPersister.php b/lib/Doctrine/ORM/Persisters/CollectionPersister.php index eea429652..d2ad2e4da 100644 --- a/lib/Doctrine/ORM/Persisters/CollectionPersister.php +++ b/lib/Doctrine/ORM/Persisters/CollectionPersister.php @@ -50,24 +50,6 @@ interface CollectionPersister */ public function update(PersistentCollection $collection); - /** - * Deletes rows. - * - * @param \Doctrine\ORM\PersistentCollection $collection - * - * @return void - */ - public function deleteRows(PersistentCollection $collection); - - /** - * Inserts rows. - * - * @param \Doctrine\ORM\PersistentCollection $collection - * - * @return void - */ - public function insertRows(PersistentCollection $collection); - /** * Counts the size of this persistent collection. * diff --git a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php index c8ac9d4d5..6ab3fc3d9 100644 --- a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -37,180 +37,42 @@ class ManyToManyPersister extends AbstractCollectionPersister { /** * {@inheritdoc} - * - * @override */ - protected function getDeleteRowSQL(PersistentCollection $coll) + public function delete(PersistentCollection $coll) { - $columns = array(); - $mapping = $coll->getMapping(); - $class = $this->em->getClassMetadata(get_class($coll->getOwner())); - $tableName = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform); + $mapping = $coll->getMapping(); - foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { - $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + if ( ! $mapping['isOwningSide']) { + return; // ignore inverse side } - foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { - $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); - } - - return 'DELETE FROM ' . $tableName - . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; + $this->conn->executeUpdate($this->getDeleteSQL($coll), $this->getDeleteSQLParameters($coll)); } /** * {@inheritdoc} - * - * @override - * - * @internal Order of the parameters must be the same as the order of the columns in getDeleteRowSql. */ - protected function getDeleteRowSQLParameters(PersistentCollection $coll, $element) + public function update(PersistentCollection $coll) { - return $this->collectJoinTableColumnParameters($coll, $element); - } + $mapping = $coll->getMapping(); - /** - * {@inheritdoc} - * - * @throws \BadMethodCallException Not used for OneToManyPersister - */ - protected function getUpdateRowSQL(PersistentCollection $coll) - { - throw new \BadMethodCallException("Insert Row SQL is not used for ManyToManyPersister"); - } - - /** - * {@inheritdoc} - * - * @override - * - * @internal Order of the parameters must be the same as the order of the columns in getInsertRowSql. - */ - protected function getInsertRowSQL(PersistentCollection $coll) - { - $columns = array(); - $mapping = $coll->getMapping(); - $class = $this->em->getClassMetadata(get_class($coll->getOwner())); - $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform); - - foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { - $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + if ( ! $mapping['isOwningSide']) { + return; // ignore inverse side } - foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { - $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + $diff = $coll->getDeleteDiff(); + $sql = $this->getDeleteRowSQL($coll); + + foreach ($diff as $element) { + $this->conn->executeUpdate($sql, $this->getDeleteRowSQLParameters($coll, $element)); } - return 'INSERT INTO ' . $joinTable . ' (' . implode(', ', $columns) . ')' - . ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')'; - } + $diff = $coll->getInsertDiff(); + $sql = $this->getInsertRowSQL($coll); - /** - * {@inheritdoc} - * - * @override - * - * @internal Order of the parameters must be the same as the order of the columns in getInsertRowSql. - */ - protected function getInsertRowSQLParameters(PersistentCollection $coll, $element) - { - return $this->collectJoinTableColumnParameters($coll, $element); - } - - /** - * Collects the parameters for inserting/deleting on the join table in the order - * of the join table columns as specified in ManyToManyMapping#joinTableColumns. - * - * @param \Doctrine\ORM\PersistentCollection $coll - * @param object $element - * - * @return array - */ - private function collectJoinTableColumnParameters(PersistentCollection $coll, $element) - { - $params = array(); - $mapping = $coll->getMapping(); - $isComposite = count($mapping['joinTableColumns']) > 2; - - $identifier1 = $this->uow->getEntityIdentifier($coll->getOwner()); - $identifier2 = $this->uow->getEntityIdentifier($element); - - if ($isComposite) { - $class1 = $this->em->getClassMetadata(get_class($coll->getOwner())); - $class2 = $coll->getTypeClass(); + foreach ($diff as $element) { + $this->conn->executeUpdate($sql, $this->getInsertRowSQLParameters($coll, $element)); } - - foreach ($mapping['joinTableColumns'] as $joinTableColumn) { - $isRelationToSource = isset($mapping['relationToSourceKeyColumns'][$joinTableColumn]); - - if ( ! $isComposite) { - $params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2); - - continue; - } - - if ($isRelationToSource) { - $params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]; - - continue; - } - - $params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]; - } - - return $params; - } - - /** - * {@inheritdoc} - * - * @override - */ - protected function getDeleteSQL(PersistentCollection $coll) - { - $columns = array(); - $mapping = $coll->getMapping(); - $class = $this->em->getClassMetadata(get_class($coll->getOwner())); - $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform); - - foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { - $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); - } - - return 'DELETE FROM ' . $joinTable - . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; - } - - /** - * {@inheritdoc} - * - * @override - * - * @internal Order of the parameters must be the same as the order of the columns in getDeleteSql. - */ - protected function getDeleteSQLParameters(PersistentCollection $coll) - { - $mapping = $coll->getMapping(); - $identifier = $this->uow->getEntityIdentifier($coll->getOwner()); - - // Optimization for single column identifier - if (count($mapping['relationToSourceKeyColumns']) === 1) { - return array(reset($identifier)); - } - - // Composite identifier - $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); - $params = array(); - - foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) { - $params[] = isset($sourceClass->fieldNames[$refColumnName]) - ? $identifier[$sourceClass->fieldNames[$refColumnName]] - : $identifier[$sourceClass->getFieldForColumn($columnName)]; - } - - return $params; } /** @@ -328,135 +190,46 @@ class ManyToManyPersister extends AbstractCollectionPersister } /** - * @param \Doctrine\ORM\PersistentCollection $coll - * @param string $key - * @param boolean $addFilters Whether the filter SQL should be included or not. - * - * @return array + * {@inheritDoc} */ - private function getJoinTableRestrictionsWithKey(PersistentCollection $coll, $key, $addFilters) + public function loadCriteria(PersistentCollection $coll, Criteria $criteria) { - $filterMapping = $coll->getMapping(); - $mapping = $filterMapping; - $indexBy = $mapping['indexBy']; - $id = $this->uow->getEntityIdentifier($coll->getOwner()); + $mapping = $coll->getMapping(); + $owner = $coll->getOwner(); + $ownerMetadata = $this->em->getClassMetadata(get_class($owner)); + $whereClauses = $params = array(); - $targetEntity = $this->em->getClassMetadata($mapping['targetEntity']); - - if (! $mapping['isOwningSide']) { - $associationSourceClass = $this->em->getClassMetadata($mapping['targetEntity']); - $mapping = $associationSourceClass->associationMappings[$mapping['mappedBy']]; - $joinColumns = $mapping['joinTable']['joinColumns']; - $relationMode = 'relationToTargetKeyColumns'; - } else { - $joinColumns = $mapping['joinTable']['inverseJoinColumns']; - $associationSourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); - $relationMode = 'relationToSourceKeyColumns'; + foreach ($mapping['relationToSourceKeyColumns'] as $key => $value) { + $whereClauses[] = sprintf('t.%s = ?', $key); + $params[] = $ownerMetadata->getFieldValue($owner, $value); } - $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform). ' t'; - $whereClauses = array(); - $params = array(); - - $joinNeeded = !in_array($indexBy, $targetEntity->identifier); - - if ($joinNeeded) { // extra join needed if indexBy is not a @id - $joinConditions = array(); - - foreach ($joinColumns as $joinTableColumn) { - $joinConditions[] = 't.' . $joinTableColumn['name'] . ' = tr.' . $joinTableColumn['referencedColumnName']; - } - $tableName = $this->quoteStrategy->getTableName($targetEntity, $this->platform); - $quotedJoinTable .= ' JOIN ' . $tableName . ' tr ON ' . implode(' AND ', $joinConditions); - - $whereClauses[] = 'tr.' . $targetEntity->getColumnName($indexBy) . ' = ?'; - $params[] = $key; + $parameters = $this->expandCriteriaParameters($criteria); + foreach ($parameters as $parameter) { + list($name, $value) = $parameter; + $whereClauses[] = sprintf('te.%s = ?', $name); + $params[] = $value; } - foreach ($mapping['joinTableColumns'] as $joinTableColumn) { - if (isset($mapping[$relationMode][$joinTableColumn])) { - $whereClauses[] = 't.' . $joinTableColumn . ' = ?'; - $params[] = $targetEntity->containsForeignIdentifier - ? $id[$targetEntity->getFieldForColumn($mapping[$relationMode][$joinTableColumn])] - : $id[$targetEntity->fieldNames[$mapping[$relationMode][$joinTableColumn]]]; - } elseif (!$joinNeeded) { - $whereClauses[] = 't.' . $joinTableColumn . ' = ?'; - $params[] = $key; - } - } + $mapping = $coll->getMapping(); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform); + $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $ownerMetadata, $this->platform); + $onConditions = $this->getOnConditionSQL($mapping); - if ($addFilters) { - list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); + $rsm = new Query\ResultSetMappingBuilder($this->em); + $rsm->addRootEntityFromClassMetadata($mapping['targetEntity'], 'te'); - if ($filterSql) { - $quotedJoinTable .= ' ' . $joinTargetEntitySQL; - $whereClauses[] = $filterSql; - } - } + $sql = 'SELECT ' . $rsm->generateSelectClause() . ' FROM ' . $tableName . ' te' + . ' JOIN ' . $joinTable . ' t ON' + . implode(' AND ', $onConditions) + . ' WHERE ' . implode(' AND ', $whereClauses); - return array($quotedJoinTable, $whereClauses, $params); - } + $stmt = $this->conn->executeQuery($sql, $params); + $hydrator = $this->em->newHydrator(Query::HYDRATE_OBJECT); - /** - * @param \Doctrine\ORM\PersistentCollection $coll - * @param object $element - * @param boolean $addFilters Whether the filter SQL should be included or not. - * - * @return array - */ - private function getJoinTableRestrictions(PersistentCollection $coll, $element, $addFilters) - { - $filterMapping = $coll->getMapping(); - $mapping = $filterMapping; - - if ( ! $mapping['isOwningSide']) { - $sourceClass = $this->em->getClassMetadata($mapping['targetEntity']); - $targetClass = $this->em->getClassMetadata($mapping['sourceEntity']); - $sourceId = $this->uow->getEntityIdentifier($element); - $targetId = $this->uow->getEntityIdentifier($coll->getOwner()); - - $mapping = $sourceClass->associationMappings[$mapping['mappedBy']]; - } else { - $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); - $sourceId = $this->uow->getEntityIdentifier($coll->getOwner()); - $targetId = $this->uow->getEntityIdentifier($element); - } - - $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform); - $whereClauses = array(); - $params = array(); - - foreach ($mapping['joinTableColumns'] as $joinTableColumn) { - $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?'; - - if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { - $params[] = ($targetClass->containsForeignIdentifier) - ? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])] - : $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]]; - - continue; - } - - // relationToSourceKeyColumns - $params[] = ($sourceClass->containsForeignIdentifier) - ? $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])] - : $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]]; - } - - if ($addFilters) { - $quotedJoinTable .= ' t'; - - list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); - - if ($filterSql) { - $quotedJoinTable .= ' ' . $joinTargetEntitySQL; - $whereClauses[] = $filterSql; - } - } - - return array($quotedJoinTable, $whereClauses, $params); + return $hydrator->hydrateAll($stmt, $rsm); } /** @@ -549,46 +322,313 @@ class ManyToManyPersister extends AbstractCollectionPersister } /** - * {@inheritDoc} + * {@inheritdoc} + * + * @override */ - public function loadCriteria(PersistentCollection $coll, Criteria $criteria) + protected function getDeleteSQL(PersistentCollection $coll) { - $mapping = $coll->getMapping(); - $owner = $coll->getOwner(); - $ownerMetadata = $this->em->getClassMetadata(get_class($owner)); - $whereClauses = $params = array(); + $columns = array(); + $mapping = $coll->getMapping(); + $class = $this->em->getClassMetadata(get_class($coll->getOwner())); + $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform); - foreach ($mapping['relationToSourceKeyColumns'] as $key => $value) { - $whereClauses[] = sprintf('t.%s = ?', $key); - $params[] = $ownerMetadata->getFieldValue($owner, $value); + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); } - $parameters = $this->expandCriteriaParameters($criteria); + return 'DELETE FROM ' . $joinTable + . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; + } - foreach ($parameters as $parameter) { - list($name, $value) = $parameter; - $whereClauses[] = sprintf('te.%s = ?', $name); - $params[] = $value; + /** + * {@inheritdoc} + * + * @override + * + * @internal Order of the parameters must be the same as the order of the columns in getDeleteSql. + */ + protected function getDeleteSQLParameters(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + $identifier = $this->uow->getEntityIdentifier($coll->getOwner()); + + // Optimization for single column identifier + if (count($mapping['relationToSourceKeyColumns']) === 1) { + return array(reset($identifier)); } - $mapping = $coll->getMapping(); - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); - $tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform); - $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $ownerMetadata, $this->platform); - $onConditions = $this->getOnConditionSQL($mapping); + // Composite identifier + $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $params = array(); - $rsm = new Query\ResultSetMappingBuilder($this->em); - $rsm->addRootEntityFromClassMetadata($mapping['targetEntity'], 'te'); + foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) { + $params[] = isset($sourceClass->fieldNames[$refColumnName]) + ? $identifier[$sourceClass->fieldNames[$refColumnName]] + : $identifier[$sourceClass->getFieldForColumn($columnName)]; + } - $sql = 'SELECT ' . $rsm->generateSelectClause() . ' FROM ' . $tableName . ' te' - . ' JOIN ' . $joinTable . ' t ON' - . implode(' AND ', $onConditions) - . ' WHERE ' . implode(' AND ', $whereClauses); + return $params; + } - $stmt = $this->conn->executeQuery($sql, $params); - $hydrator = $this->em->newHydrator(Query::HYDRATE_OBJECT); + /** + * Gets the SQL statement used for deleting a row from the collection. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * + * @return string + */ + protected function getDeleteRowSQL(PersistentCollection $coll) + { + $columns = array(); + $mapping = $coll->getMapping(); + $class = $this->em->getClassMetadata(get_class($coll->getOwner())); + $tableName = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform); - return $hydrator->hydrateAll($stmt, $rsm); + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + return 'DELETE FROM ' . $tableName + . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; + } + + /** + * Gets the SQL parameters for the corresponding SQL statement to delete the given + * element from the given collection. + * + * @internal Order of the parameters must be the same as the order of the columns in getDeleteRowSql. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * @param mixed $element + * + * @return array + */ + protected function getDeleteRowSQLParameters(PersistentCollection $coll, $element) + { + return $this->collectJoinTableColumnParameters($coll, $element); + } + + /** + * Gets the SQL statement used for inserting a row in the collection. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * + * @return string + */ + protected function getInsertRowSQL(PersistentCollection $coll) + { + $columns = array(); + $mapping = $coll->getMapping(); + $class = $this->em->getClassMetadata(get_class($coll->getOwner())); + $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform); + + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + return 'INSERT INTO ' . $joinTable . ' (' . implode(', ', $columns) . ')' + . ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')'; + } + + /** + * Gets the SQL parameters for the corresponding SQL statement to insert the given + * element of the given collection into the database. + * + * @internal Order of the parameters must be the same as the order of the columns in getInsertRowSql. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * @param mixed $element + * + * @return array + */ + protected function getInsertRowSQLParameters(PersistentCollection $coll, $element) + { + return $this->collectJoinTableColumnParameters($coll, $element); + } + + /** + * Collects the parameters for inserting/deleting on the join table in the order + * of the join table columns as specified in ManyToManyMapping#joinTableColumns. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * @param object $element + * + * @return array + */ + private function collectJoinTableColumnParameters(PersistentCollection $coll, $element) + { + $params = array(); + $mapping = $coll->getMapping(); + $isComposite = count($mapping['joinTableColumns']) > 2; + + $identifier1 = $this->uow->getEntityIdentifier($coll->getOwner()); + $identifier2 = $this->uow->getEntityIdentifier($element); + + if ($isComposite) { + $class1 = $this->em->getClassMetadata(get_class($coll->getOwner())); + $class2 = $coll->getTypeClass(); + } + + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + $isRelationToSource = isset($mapping['relationToSourceKeyColumns'][$joinTableColumn]); + + if ( ! $isComposite) { + $params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2); + + continue; + } + + if ($isRelationToSource) { + $params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]; + + continue; + } + + $params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]; + } + + return $params; + } + + /** + * @param \Doctrine\ORM\PersistentCollection $coll + * @param string $key + * @param boolean $addFilters Whether the filter SQL should be included or not. + * + * @return array + */ + private function getJoinTableRestrictionsWithKey(PersistentCollection $coll, $key, $addFilters) + { + $filterMapping = $coll->getMapping(); + $mapping = $filterMapping; + $indexBy = $mapping['indexBy']; + $id = $this->uow->getEntityIdentifier($coll->getOwner()); + + $targetEntity = $this->em->getClassMetadata($mapping['targetEntity']); + + if (! $mapping['isOwningSide']) { + $associationSourceClass = $this->em->getClassMetadata($mapping['targetEntity']); + $mapping = $associationSourceClass->associationMappings[$mapping['mappedBy']]; + $joinColumns = $mapping['joinTable']['joinColumns']; + $relationMode = 'relationToTargetKeyColumns'; + } else { + $joinColumns = $mapping['joinTable']['inverseJoinColumns']; + $associationSourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $relationMode = 'relationToSourceKeyColumns'; + } + + $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform). ' t'; + $whereClauses = array(); + $params = array(); + + $joinNeeded = !in_array($indexBy, $targetEntity->identifier); + + if ($joinNeeded) { // extra join needed if indexBy is not a @id + $joinConditions = array(); + + foreach ($joinColumns as $joinTableColumn) { + $joinConditions[] = 't.' . $joinTableColumn['name'] . ' = tr.' . $joinTableColumn['referencedColumnName']; + } + $tableName = $this->quoteStrategy->getTableName($targetEntity, $this->platform); + $quotedJoinTable .= ' JOIN ' . $tableName . ' tr ON ' . implode(' AND ', $joinConditions); + + $whereClauses[] = 'tr.' . $targetEntity->getColumnName($indexBy) . ' = ?'; + $params[] = $key; + + } + + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + if (isset($mapping[$relationMode][$joinTableColumn])) { + $whereClauses[] = 't.' . $joinTableColumn . ' = ?'; + $params[] = $targetEntity->containsForeignIdentifier + ? $id[$targetEntity->getFieldForColumn($mapping[$relationMode][$joinTableColumn])] + : $id[$targetEntity->fieldNames[$mapping[$relationMode][$joinTableColumn]]]; + } elseif (!$joinNeeded) { + $whereClauses[] = 't.' . $joinTableColumn . ' = ?'; + $params[] = $key; + } + } + + if ($addFilters) { + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); + + if ($filterSql) { + $quotedJoinTable .= ' ' . $joinTargetEntitySQL; + $whereClauses[] = $filterSql; + } + } + + return array($quotedJoinTable, $whereClauses, $params); + } + + /** + * @param \Doctrine\ORM\PersistentCollection $coll + * @param object $element + * @param boolean $addFilters Whether the filter SQL should be included or not. + * + * @return array + */ + private function getJoinTableRestrictions(PersistentCollection $coll, $element, $addFilters) + { + $filterMapping = $coll->getMapping(); + $mapping = $filterMapping; + + if ( ! $mapping['isOwningSide']) { + $sourceClass = $this->em->getClassMetadata($mapping['targetEntity']); + $targetClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $sourceId = $this->uow->getEntityIdentifier($element); + $targetId = $this->uow->getEntityIdentifier($coll->getOwner()); + + $mapping = $sourceClass->associationMappings[$mapping['mappedBy']]; + } else { + $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $sourceId = $this->uow->getEntityIdentifier($coll->getOwner()); + $targetId = $this->uow->getEntityIdentifier($element); + } + + $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform); + $whereClauses = array(); + $params = array(); + + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?'; + + if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { + $params[] = ($targetClass->containsForeignIdentifier) + ? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])] + : $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]]; + + continue; + } + + // relationToSourceKeyColumns + $params[] = ($sourceClass->containsForeignIdentifier) + ? $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])] + : $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]]; + } + + if ($addFilters) { + $quotedJoinTable .= ' t'; + + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); + + if ($filterSql) { + $quotedJoinTable .= ' ' . $joinTargetEntitySQL; + $whereClauses[] = $filterSql; + } + } + + return array($quotedJoinTable, $whereClauses, $params); } /** diff --git a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php index c1e8a6c38..5586bd99a 100644 --- a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php @@ -33,6 +33,28 @@ use Doctrine\ORM\UnitOfWork; */ class OneToManyPersister extends AbstractCollectionPersister { + /** + * {@inheritdoc} + */ + public function delete(PersistentCollection $coll) + { + // This can never happen. One to many can only be inverse side. + // For owning side one to many, it is required to have a join table, + // then classifying it as a ManyToManyPersister. + return; + } + + /** + * {@inheritdoc} + */ + public function update(PersistentCollection $coll) + { + // This can never happen. One to many can only be inverse side. + // For owning side one to many, it is required to have a join table, + // then classifying it as a ManyToManyPersister. + return; + } + /** * {@inheritdoc} */ @@ -97,36 +119,6 @@ class OneToManyPersister extends AbstractCollectionPersister throw new \BadMethodCallException("Insert Row SQL is not used for OneToManyPersister"); } - /** - * {@inheritdoc} - * - * @throws \BadMethodCallException Not used for OneToManyPersister. - */ - protected function getUpdateRowSQL(PersistentCollection $coll) - { - throw new \BadMethodCallException("Update Row SQL is not used for OneToManyPersister"); - } - - /** - * {@inheritdoc} - * - * @throws \BadMethodCallException Not used for OneToManyPersister. - */ - protected function getDeleteSQL(PersistentCollection $coll) - { - throw new \BadMethodCallException("Delete Row SQL is not used for OneToManyPersister"); - } - - /** - * {@inheritdoc} - * - * @throws \BadMethodCallException Not used for OneToManyPersister. - */ - protected function getDeleteSQLParameters(PersistentCollection $coll) - { - throw new \BadMethodCallException("Delete Row SQL is not used for OneToManyPersister"); - } - /** * {@inheritdoc} */ diff --git a/tests/Doctrine/Tests/ORM/Cache/Persister/AbstractCollectionPersisterTest.php b/tests/Doctrine/Tests/ORM/Cache/Persister/AbstractCollectionPersisterTest.php index d8c94a39c..e216cd79e 100644 --- a/tests/Doctrine/Tests/ORM/Cache/Persister/AbstractCollectionPersisterTest.php +++ b/tests/Doctrine/Tests/ORM/Cache/Persister/AbstractCollectionPersisterTest.php @@ -49,8 +49,6 @@ abstract class AbstractCollectionPersisterTest extends OrmTestCase protected $collectionPersisterMockMethods = array( 'delete', 'update', - 'deleteRows', - 'insertRows', 'count', 'slice', 'contains', @@ -154,36 +152,6 @@ abstract class AbstractCollectionPersisterTest extends OrmTestCase $this->assertNull($persister->update($collection)); } - public function testInvokeDeleteRows() - { - $entity = new State("Foo"); - $persister = $this->createPersisterDefault(); - $collection = $this->createCollection($entity); - - $this->em->getUnitOfWork()->registerManaged($entity, array('id'=>1), array('id'=>1, 'name'=>'Foo')); - - $this->collectionPersister->expects($this->once()) - ->method('deleteRows') - ->with($this->equalTo($collection)); - - $this->assertNull($persister->deleteRows($collection)); - } - - public function testInvokeInsertRows() - { - $entity = new State("Foo"); - $persister = $this->createPersisterDefault(); - $collection = $this->createCollection($entity); - - $this->em->getUnitOfWork()->registerManaged($entity, array('id'=>1), array('id'=>1, 'name'=>'Foo')); - - $this->collectionPersister->expects($this->once()) - ->method('insertRows') - ->with($this->equalTo($collection)); - - $this->assertNull($persister->insertRows($collection)); - } - public function testInvokeCount() { $entity = new State("Foo");