/** * {@inheritdoc} */ public function update(PersistentCollection $collection) { if ($collection->isDirty() && $collection->getSnapshot()) { throw CacheException::updateReadOnlyCollection(ClassUtils::getClass($collection->getOwner()), $this->association['fieldName']); } parent::update($collection); }
/** * {@inheritdoc} */ public function delete(PersistentCollection $collection) { $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId); $lock = $this->region->lock($key); $this->persister->delete($collection); if ($lock === null) { return; } $this->queuedCache['delete'][spl_object_hash($collection)] = array('key' => $key, 'lock' => $lock); }
/** * {@inheritdoc} */ public function update(PersistentCollection $collection) { $isInitialized = $collection->isInitialized(); $isDirty = $collection->isDirty(); if (!$isInitialized && !$isDirty) { return; } $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId); // Invalidate non initialized collections OR ordered collection if ($isDirty && !$isInitialized || isset($this->association['orderBy'])) { $this->persister->update($collection); $this->queuedCache['delete'][spl_object_hash($collection)] = $key; return; } $this->persister->update($collection); $this->queuedCache['update'][spl_object_hash($collection)] = ['key' => $key, 'list' => $collection]; }
/** * {@inheritdoc} */ public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) { $persister = $this->uow->getCollectionPersister($assoc); $hasCache = $persister instanceof CachedPersister; if ($hasCache) { $ownerId = $this->uow->getEntityIdentifier($coll->getOwner()); $key = new CollectionCacheKey($assoc['sourceEntity'], $assoc['fieldName'], $ownerId); $list = $persister->loadCollectionCache($coll, $key); if ($list !== null) { if ($this->cacheLogger) { $this->cacheLogger->collectionCacheHit($persister->getCacheRegion()->getName(), $key); } return $list; } } $list = $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $coll); if ($hasCache) { $persister->storeCollectionCache($key, $list); if ($this->cacheLogger) { $this->cacheLogger->collectionCacheMiss($persister->getCacheRegion()->getName(), $key); } } return $list; }
/** * @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); } } }
/** * {@inheritdoc} * * @override */ protected function _getDeleteSqlParameters(PersistentCollection $coll) { //FIXME: This is still problematic for composite keys because we silently // rely on a specific ordering of the columns. return $this->_uow->getEntityIdentifier($coll->getOwner()); }
/** * {@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 * @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); }
/** * @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); }
/** * 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; }
/** * Clears cache entries related to the current collection * * @param PersistentCollection $collection */ protected function evictCollectionCache(PersistentCollection $collection) { $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $this->uow->getEntityIdentifier($collection->getOwner())); $this->region->evict($key); if ($this->cacheLogger) { $this->cacheLogger->collectionCachePut($this->regionName, $key); } }
/** * @param PersistentCollection $coll * @param object $element * @return boolean */ public function contains(PersistentCollection $coll, $element) { $mapping = $coll->getMapping(); $uow = $this->_em->getUnitOfWork(); // shortcut for new entities $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW); if ($entityState === UnitOfWork::STATE_NEW) { return false; } // Entity is scheduled for inclusion if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) { return false; } $persister = $uow->getEntityPersister($mapping['targetEntity']); // 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 $persister->exists($element, array($mapping['mappedBy'] => $id)); }
/** * {@inheritdoc} */ public function count(PersistentCollection $collection) { $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId); $entry = $this->region->get($key); if ($entry !== null) { return count($entry->identifiers); } return $this->persister->count($collection); }
/** * {@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 contains(PersistentCollection $collection, $element) { if (!$this->isValidEntityState($element)) { return false; } $mapping = $collection->getMapping(); $persister = $this->uow->getEntityPersister($mapping['targetEntity']); // only works with single id identifier entities. Will throw an // exception in Entity Persisters if that is not the case for the // 'mappedBy' field. $criteria = new Criteria(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner())); return $persister->exists($element, $criteria); }
/** * @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)); }