/** * {@inheritdoc} * * @param <type> $coll * @return <type> * @override */ protected function _getDeleteRowSql(PersistentCollection $coll) { $mapping = $coll->getMapping(); $targetClass = $this->_em->getClassMetadata($mapping->getTargetEntityName()); $table = $targetClass->getTableName(); $ownerMapping = $targetClass->getAssociationMapping($mapping->getMappedByFieldName()); $setClause = ''; foreach ($ownerMapping->getSourceToTargetKeyColumns() as $sourceCol => $targetCol) { if ($setClause != '') { $setClause .= ', '; } $setClause .= "{$sourceCol} = NULL"; } $whereClause = ''; foreach ($targetClass->getIdentifierColumnNames() as $idColumn) { if ($whereClause != '') { $whereClause .= ' AND '; } $whereClause .= "{$idColumn} = ?"; } return array("UPDATE {$table} SET {$setClause} WHERE {$whereClause}", $this->_uow->getEntityIdentifier($element)); }
/** * @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) { $uow = $this->_em->getUnitOfWork(); $mapping = $filterMapping = $coll->getMapping(); if (!$mapping['isOwningSide']) { $sourceClass = $this->_em->getClassMetadata($mapping['targetEntity']); $targetClass = $this->_em->getClassMetadata($mapping['sourceEntity']); $sourceId = $uow->getEntityIdentifier($element); $targetId = $uow->getEntityIdentifier($coll->getOwner()); $mapping = $sourceClass->associationMappings[$mapping['mappedBy']]; } else { $sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']); $targetClass = $this->_em->getClassMetadata($mapping['targetEntity']); $sourceId = $uow->getEntityIdentifier($coll->getOwner()); $targetId = $uow->getEntityIdentifier($element); } $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform); $whereClauses = array(); $params = array(); foreach ($mapping['joinTableColumns'] as $joinTableColumn) { $whereClauses[] = $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) { list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); if ($filterSql) { $quotedJoinTable .= ' t ' . $joinTargetEntitySQL; $whereClauses[] = $filterSql; } } return array($quotedJoinTable, $whereClauses, $params); }
/** * {@inheritdoc} */ public function update(PersistentCollection $coll) { $mapping = $coll->getMapping(); if (!$mapping['isOwningSide']) { return; // ignore inverse side } $this->deleteRows($coll); $this->insertRows($coll); }
/** * Loads a collection of entities in a one-to-many association. * * @param OneToManyMapping $assoc * @param array $criteria The criteria by which to select the entities. * @param PersistentCollection The collection to fill. */ public function loadOneToManyCollection($assoc, array $criteria, PersistentCollection $coll) { $owningAssoc = $this->_class->associationMappings[$coll->getMapping()->mappedBy]; $sql = $this->_getSelectEntitiesSQL($criteria, $owningAssoc, $assoc->orderBy); $params = array_values($criteria); $stmt = $this->_conn->executeQuery($sql, $params); while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) { $coll->hydrateAdd($this->_createEntity($result)); } $stmt->closeCursor(); }
/** * Delete Class Table Inheritance entities. * A temporary table is needed to keep IDs to be deleted in both parent and child class' tables. * * Thanks Steve Ebersole (Hibernate) for idea on how to tackle reliably this scenario, we owe him a beer! =) * * @param PersistentCollection $collection * * @return int * * @throws \Doctrine\DBAL\DBALException */ private function deleteJoinedEntityCollection(PersistentCollection $collection) { $mapping = $collection->getMapping(); $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); $rootClass = $this->em->getClassMetadata($targetClass->rootEntityName); // 1) Build temporary table DDL $tempTable = $this->platform->getTemporaryTableName($rootClass->getTemporaryIdTableName()); $idColumnNames = $rootClass->getIdentifierColumnNames(); $idColumnList = implode(', ', $idColumnNames); $columnDefinitions = []; foreach ($idColumnNames as $idColumnName) { $columnDefinitions[$idColumnName] = ['notnull' => true, 'type' => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $this->em))]; } $statement = $this->platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' . $this->platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; $this->conn->executeUpdate($statement); // 2) Build insert table records into temporary table $query = $this->em->createQuery(' SELECT t0.' . implode(', t0.', $rootClass->getIdentifierFieldNames()) . ' FROM ' . $targetClass->name . ' t0 WHERE t0.' . $mapping['mappedBy'] . ' = :owner')->setParameter('owner', $collection->getOwner()); $statement = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ') ' . $query->getSQL(); $parameters = array_values($sourceClass->getIdentifierValues($collection->getOwner())); $numDeleted = $this->conn->executeUpdate($statement, $parameters); // 3) Delete records on each table in the hierarchy $classNames = array_merge($targetClass->parentClasses, [$targetClass->name], $targetClass->subClasses); foreach (array_reverse($classNames) as $className) { $tableName = $this->quoteStrategy->getTableName($this->em->getClassMetadata($className), $this->platform); $statement = 'DELETE FROM ' . $tableName . ' WHERE (' . $idColumnList . ')' . ' IN (SELECT ' . $idColumnList . ' FROM ' . $tempTable . ')'; $this->conn->executeUpdate($statement); } // 4) Drop temporary table $statement = $this->platform->getDropTemporaryTableSQL($tempTable); $this->conn->executeUpdate($statement); return $numDeleted; }
/** * @param PersistentCollection $coll * @param object $element * @return boolean */ public function removeElement(PersistentCollection $coll, $element) { $uow = $this->_em->getUnitOfWork(); // shortcut for new entities $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW); if ($entityState === UnitOfWork::STATE_NEW) { return false; } // If Entity is scheduled for inclusion, it is not in this collection. // We can assure that because it would have return true before on array check if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) { return false; } $mapping = $coll->getMapping(); $class = $this->_em->getClassMetadata($mapping['targetEntity']); $sql = 'DELETE FROM ' . $class->getQuotedTableName($this->_conn->getDatabasePlatform()) . ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?'; return (bool) $this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element)); }
/** * {@inheritdoc} * * @override */ protected function _getDeleteSql(PersistentCollection $coll) { $mapping = $coll->getMapping(); $joinTable = $mapping->getJoinTable(); $whereClause = ''; foreach ($mapping->getSourceToRelationKeyColumns() as $relationColumn) { if ($whereClause !== '') { $whereClause .= ' AND '; } $whereClause .= "{$relationColumn} = ?"; } return 'DELETE FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause; }
/** * Initializes (loads) an uninitialized persistent collection of an entity. * * @param \Doctrine\ORM\PersistentCollection $collection The collection to initialize. * * @return void * * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733. */ public function loadCollection(PersistentCollection $collection) { $assoc = $collection->getMapping(); $persister = $this->getEntityPersister($assoc['targetEntity']); switch ($assoc['type']) { case ClassMetadata::ONE_TO_MANY: $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection); break; case ClassMetadata::MANY_TO_MANY: $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection); break; } $collection->setInitialized(true); }
/** * @param PersistentCollection $coll * @param object $element * @return boolean */ public function removeElement(PersistentCollection $coll, $element) { $uow = $this->_em->getUnitOfWork(); // shortcut for new entities if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) { return false; } $mapping = $coll->getMapping(); $class = $this->_em->getClassMetadata($mapping['targetEntity']); $sql = 'DELETE FROM ' . $class->getQuotedTableName($this->_conn->getDatabasePlatform()) . ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?'; return (bool) $this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element)); }
/** * {@inheritdoc} * * @override * @internal Order of the parameters must be the same as the order of the columns in * _getDeleteSql. */ protected function _getDeleteSqlParameters(PersistentCollection $coll) { $params = array(); $mapping = $coll->getMapping(); $identifier = $this->_uow->getEntityIdentifier($coll->getOwner()); if (count($mapping->relationToSourceKeyColumns) > 1) { $sourceClass = $this->_em->getClassMetadata(get_class($mapping->getOwner())); foreach ($mapping->relationToSourceKeyColumns as $relColumn => $srcColumn) { $params[] = $identifier[$sourceClass->fieldNames[$srcColumn]]; } } else { $params[] = array_pop($identifier); } return $params; }
/** * @param PersistentCollection $collection */ protected function calculateCollectionData(PersistentCollection $collection) { $ownerEntity = $collection->getOwner(); if ($this->hasConfig(get_class($ownerEntity))) { $meta = $this->getConfig(get_class($ownerEntity)); $collectionMapping = $collection->getMapping(); if (isset($meta->propertyMetadata[$collectionMapping['fieldName']])) { $method = $meta->propertyMetadata[$collectionMapping['fieldName']]->method; $newCollection = $collection->toArray(); $oldCollection = $collection->getSnapshot(); $oldData = array_reduce($oldCollection, function ($result, $item) use($method) { return $result . ($result ? ', ' : '') . $item->{$method}(); }); $newData = array_reduce($newCollection, function ($result, $item) use($method) { return $result . ($result ? ', ' : '') . $item->{$method}(); }); $this->collectionLogData[$collectionMapping['fieldName']] = array('old' => $oldData, 'new' => $newData); } } }
/** * @param \Doctrine\ORM\PersistentCollection $collection * @param object $element * @param boolean $addFilters Whether the filter SQL should be included or not. * * @return array ordered vector: * - quoted join table name * - where clauses to be added for filtering * - parameters to be bound for filtering * - types of the parameters to be bound for filtering */ private function getJoinTableRestrictions(PersistentCollection $collection, $element, $addFilters) { $filterMapping = $collection->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($collection->getOwner()); $mapping = $sourceClass->associationMappings[$mapping['mappedBy']]; } else { $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); $sourceId = $this->uow->getEntityIdentifier($collection->getOwner()); $targetId = $this->uow->getEntityIdentifier($element); } $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform); $whereClauses = array(); $params = array(); $types = array(); foreach ($mapping['joinTableColumns'] as $joinTableColumn) { $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?'; if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { $targetColumn = $mapping['relationToTargetKeyColumns'][$joinTableColumn]; $params[] = $targetId[$targetClass->getFieldForColumn($targetColumn)]; $types[] = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em); continue; } // relationToSourceKeyColumns $targetColumn = $mapping['relationToSourceKeyColumns'][$joinTableColumn]; $params[] = $sourceId[$sourceClass->getFieldForColumn($targetColumn)]; $types[] = PersisterHelper::getTypeOfColumn($targetColumn, $sourceClass, $this->em); } if ($addFilters) { $quotedJoinTable .= ' t'; list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); if ($filterSql) { $quotedJoinTable .= ' ' . $joinTargetEntitySQL; $whereClauses[] = $filterSql; } } return array($quotedJoinTable, $whereClauses, $params, $types); }
/** * {@inheritdoc} */ public function removeElement(PersistentCollection $collection, $element) { $mapping = $collection->getMapping(); if (!$mapping['orphanRemoval']) { // no-op: this is not the owning side, therefore no operations should be applied return false; } if (!$this->isValidEntityState($element)) { return false; } return $this->uow->getEntityPersister($mapping['targetEntity'])->delete($element); }
/** * @param PersistentCollection $coll * @param object $element */ public function contains(PersistentCollection $coll, $element) { $mapping = $coll->getMapping(); $uow = $this->_em->getUnitOfWork(); // shortcut for new entities if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) { return false; } // only works with single id identifier entities. Will throw an exception in Entity Persisters // if that is not the case for the 'mappedBy' field. $id = current($uow->getEntityIdentifier($coll->getOwner())); return $uow->getEntityPersister($mapping['targetEntity'])->exists($element, array($mapping['mappedBy'] => $id)); }
/** * @param PersistentCollection $coll * @param object $element */ public function contains(PersistentCollection $coll, $element) { $uow = $this->_em->getUnitOfWork(); // shortcut for new entities if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) { return false; } $params = array(); $mapping = $coll->getMapping(); if (!$mapping['isOwningSide']) { $sourceClass = $this->_em->getClassMetadata($mapping['targetEntity']); $targetClass = $this->_em->getClassMetadata($mapping['sourceEntity']); $sourceId = $uow->getEntityIdentifier($element); $targetId = $uow->getEntityIdentifier($coll->getOwner()); $mapping = $sourceClass->associationMappings[$mapping['mappedBy']]; } else { $sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']); $targetClass = $this->_em->getClassMetadata($mapping['targetEntity']); $sourceId = $uow->getEntityIdentifier($coll->getOwner()); $targetId = $uow->getEntityIdentifier($element); } $joinTable = $mapping['joinTable']; $whereClause = ''; foreach ($mapping['joinTableColumns'] as $joinTableColumn) { if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { if ($whereClause !== '') { $whereClause .= ' AND '; } $whereClause .= "{$joinTableColumn} = ?"; if ($targetClass->containsForeignIdentifier) { $params[] = $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]; } else { $params[] = $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]]; } } else { if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) { if ($whereClause !== '') { $whereClause .= ' AND '; } $whereClause .= "{$joinTableColumn} = ?"; if ($sourceClass->containsForeignIdentifier) { $params[] = $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]; } else { $params[] = $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]]; } } } } $sql = 'SELECT 1 FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause; return (bool) $this->_conn->fetchColumn($sql, $params); }
/** * Updates the given collection, synchronizing it's state with the database * by inserting, updating and deleting individual elements. * * @param PersistentCollection $coll */ public function update(PersistentCollection $coll) { if (!$coll->getMapping()->isOwningSide) { return; // ignore inverse side } $this->deleteRows($coll); //$this->updateRows($coll); $this->insertRows($coll); }
/** * {@inheritDoc} */ public function loadCriteria(PersistentCollection $coll, Criteria $criteria) { $mapping = $coll->getMapping(); $owner = $coll->getOwner(); $ownerMetadata = $this->em->getClassMetadata(get_class($owner)); $whereClauses = $params = array(); foreach ($mapping['relationToSourceKeyColumns'] as $key => $value) { $whereClauses[] = sprintf('t.%s = ?', $key); $params[] = $ownerMetadata->getFieldValue($owner, $value); } $parameters = $this->expandCriteriaParameters($criteria); foreach ($parameters as $parameter) { list($name, $value) = $parameter; $whereClauses[] = sprintf('te.%s = ?', $name); $params[] = $value; } $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); $rsm = new Query\ResultSetMappingBuilder($this->em); $rsm->addRootEntityFromClassMetadata($mapping['targetEntity'], 'te'); $sql = 'SELECT ' . $rsm->generateSelectClause() . ' FROM ' . $tableName . ' te' . ' JOIN ' . $joinTable . ' t ON' . implode(' AND ', $onConditions) . ' WHERE ' . implode(' AND ', $whereClauses); $stmt = $this->conn->executeQuery($sql, $params); $hydrator = $this->em->newHydrator(Query::HYDRATE_OBJECT); return $hydrator->hydrateAll($stmt, $rsm); }
/** * @param \Doctrine\ORM\PersistentCollection $coll * @param object $element * * @return boolean */ public function removeElement(PersistentCollection $coll, $element) { $mapping = $coll->getMapping(); if (!$mapping['orphanRemoval']) { // no-op: this is not the owning side, therefore no operations should be applied return false; } $uow = $this->em->getUnitOfWork(); // shortcut for new entities $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW); if ($entityState === UnitOfWork::STATE_NEW) { return false; } // If Entity is scheduled for inclusion, it is not in this collection. // We can assure that because it would have return true before on array check if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) { return false; } $this->uow->getEntityPersister($mapping['targetEntity'])->delete($element); return true; }
/** * @param PersistentCollection $collection */ protected function calculateCollectionData(PersistentCollection $collection) { $ownerEntity = $collection->getOwner(); $ownerEntityClassName = $this->getEntityClassName($ownerEntity); if ($this->checkAuditable($ownerEntityClassName)) { $meta = $this->getConfig($ownerEntityClassName); $collectionMapping = $collection->getMapping(); if (isset($meta->propertyMetadata[$collectionMapping['fieldName']])) { $method = $meta->propertyMetadata[$collectionMapping['fieldName']]->method; // calculate collection changes $newCollection = $collection->toArray(); $oldCollection = $collection->getSnapshot(); $oldData = array_reduce($oldCollection, function ($result, $item) use($method) { return $result . ($result ? ', ' : '') . $item->{$method}(); }); $newData = array_reduce($newCollection, function ($result, $item) use($method) { return $result . ($result ? ', ' : '') . $item->{$method}(); }); $entityIdentifier = $this->getEntityIdentifierString($ownerEntity); $fieldName = $collectionMapping['fieldName']; $this->collectionLogData[$ownerEntityClassName][$entityIdentifier][$fieldName] = array('old' => $oldData, 'new' => $newData); } } }
/** * Loads a collection of entities in a one-to-many association. * * @param array $criteria The criteria by which to select the entities. * @param PersistentCollection The collection to fill. */ public function loadOneToManyCollection(array $criteria, PersistentCollection $coll) { $owningAssoc = $this->_class->associationMappings[$coll->getMapping()->mappedByFieldName]; $sql = $this->_getSelectEntitiesSql($criteria, $owningAssoc); $params = array_values($criteria); if ($this->_sqlLogger !== null) { $this->_sqlLogger->logSql($sql, $params); } $stmt = $this->_conn->prepare($sql); $stmt->execute($params); while ($result = $stmt->fetch(Connection::FETCH_ASSOC)) { $coll->hydrateAdd($this->_createEntity($result)); } $stmt->closeCursor(); }