[2.0] Fixed #2366.
This commit is contained in:
parent
be0088f00c
commit
ae1b9371ec
@ -413,13 +413,12 @@ class EntityManager
|
||||
* Refreshes the persistent state of an entity from the database,
|
||||
* overriding any local changes that have not yet been persisted.
|
||||
*
|
||||
* @param object $entity
|
||||
* @todo Implementation
|
||||
* @param object $entity The entity to refresh.
|
||||
*/
|
||||
public function refresh($entity)
|
||||
{
|
||||
$this->_errorIfClosed();
|
||||
throw DoctrineException::notImplemented();
|
||||
$this->_unitOfWork->refresh($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -260,7 +260,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
} else {
|
||||
// Eager load
|
||||
//TODO: Allow more efficient and configurable batching of these loads
|
||||
$assoc->load($entity, new $className, $this->_em, $joinColumns);
|
||||
$assoc->load($entity, new $assoc->targetEntityName, $this->_em, $joinColumns);
|
||||
}
|
||||
} else {
|
||||
//TODO: Eager load
|
||||
|
@ -37,8 +37,18 @@ namespace Doctrine\ORM\Mapping;
|
||||
*/
|
||||
abstract class AssociationMapping
|
||||
{
|
||||
const FETCH_MANUAL = 1;
|
||||
/**
|
||||
* Specifies that an association is to be fetched when it is first accessed.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const FETCH_LAZY = 2;
|
||||
/**
|
||||
* Specifies that an association is to be fetched when the owner of the
|
||||
* association is fetched.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const FETCH_EAGER = 3;
|
||||
|
||||
/**
|
||||
@ -66,7 +76,7 @@ abstract class AssociationMapping
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $fetchMode = self::FETCH_MANUAL;
|
||||
public $fetchMode = self::FETCH_LAZY;
|
||||
|
||||
/**
|
||||
* Flag that indicates whether the class that defines this mapping is
|
||||
@ -244,16 +254,6 @@ abstract class AssociationMapping
|
||||
return $this->fetchMode == self::FETCH_LAZY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the target entity/entities of the association are manually fetched.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isManuallyFetched()
|
||||
{
|
||||
return $this->fetchMode == self::FETCH_MANUAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the source entity of this association represents the owning side.
|
||||
*
|
||||
|
@ -198,7 +198,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
|
||||
public function remove($key)
|
||||
{
|
||||
//TODO: delete entity if shouldDeleteOrphans
|
||||
/*if ($this->_association->isOneToMany() && $this->_association->shouldDeleteOrphans()) {
|
||||
/*if ($this->_association->isOneToMany() && $this->_association->shouldDeleteOrphans) {
|
||||
$this->_em->remove($removed);
|
||||
}*/
|
||||
$removed = parent::remove($key);
|
||||
@ -209,8 +209,8 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
|
||||
}
|
||||
|
||||
/**
|
||||
* When the collection is a Map this is like put(key,value)/add(key,value).
|
||||
* When the collection is a List this is like add(position,value).
|
||||
* When the collection is used as a Map this is like put(key,value)/add(key,value).
|
||||
* When the collection is used as a List this is like add(position,value).
|
||||
*
|
||||
* @param integer $key
|
||||
* @param mixed $value
|
||||
@ -302,6 +302,9 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the collection by loading its contents from the database.
|
||||
*/
|
||||
private function _initialize()
|
||||
{
|
||||
|
||||
@ -375,15 +378,21 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
|
||||
{
|
||||
//TODO: Register collection as dirty with the UoW if necessary
|
||||
//TODO: If oneToMany() && shouldDeleteOrphan() delete entities
|
||||
/*if ($this->_association->isOneToMany() && $this->_association->shouldDeleteOrphans()) {
|
||||
/*if ($this->_association->isOneToMany() && $this->_association->shouldDeleteOrphans) {
|
||||
foreach ($this->_data as $entity) {
|
||||
$this->_em->remove($entity);
|
||||
}
|
||||
}*/
|
||||
parent::clear();
|
||||
if ($this->_association->isOwningSide) {
|
||||
$this->_changed();
|
||||
$this->_em->getUnitOfWork()->scheduleCollectionDeletion($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this collection as changed/dirty.
|
||||
*/
|
||||
private function _changed()
|
||||
{
|
||||
$this->_isDirty = true;
|
||||
|
@ -25,6 +25,7 @@ namespace Doctrine\ORM\Persisters;
|
||||
* Persister for collections of basic elements / value types.
|
||||
*
|
||||
* @author robo
|
||||
* @todo Implementation once support for collections of basic elements (i.e. strings) is added.
|
||||
*/
|
||||
class ElementCollectionPersister extends AbstractCollectionPersister
|
||||
{
|
||||
|
@ -108,7 +108,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
$mapping = $coll->getMapping();
|
||||
$joinTable = $mapping->getJoinTable();
|
||||
$whereClause = '';
|
||||
foreach ($mapping->getSourceToRelationKeyColumns() as $relationColumn) {
|
||||
foreach ($mapping->sourceToRelationKeyColumns as $relationColumn) {
|
||||
if ($whereClause !== '') $whereClause .= ' AND ';
|
||||
$whereClause .= "$relationColumn = ?";
|
||||
}
|
||||
|
@ -26,18 +26,23 @@ use Doctrine\ORM\PersistentCollection;
|
||||
/**
|
||||
* Persister for one-to-many collections.
|
||||
*
|
||||
* This persister is only used for uni-directional one-to-many mappings.
|
||||
* IMPORTANT:
|
||||
* This persister is only used for uni-directional one-to-many mappings on a foreign key
|
||||
* (which are not yet supported). So currently this persister is not used.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @todo Complete implementation when the support for uni-directional one-to-many mappings
|
||||
* on a foreign key gets added.
|
||||
*/
|
||||
class OneToManyPersister extends AbstractCollectionPersister
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* Generates the SQL UPDATE that updates a particular row's foreign
|
||||
* key to null.
|
||||
*
|
||||
* @param <type> $coll
|
||||
* @return <type>
|
||||
* @param PersistentCollection $coll
|
||||
* @return string
|
||||
* @override
|
||||
*/
|
||||
protected function _getDeleteRowSql(PersistentCollection $coll)
|
||||
@ -63,14 +68,53 @@ class OneToManyPersister extends AbstractCollectionPersister
|
||||
return array("UPDATE $table SET $setClause WHERE $whereClause", $this->_uow->getEntityIdentifier($element));
|
||||
}
|
||||
|
||||
protected function _getInsertRowSql()
|
||||
protected function _getInsertRowSql(PersistentCollection $coll)
|
||||
{
|
||||
return "UPDATE xxx SET foreign_key = yyy WHERE foreign_key = zzz";
|
||||
}
|
||||
|
||||
/* Not used for OneToManyPersister */
|
||||
protected function _getUpdateRowSql()
|
||||
protected function _getUpdateRowSql(PersistentCollection $coll)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the SQL UPDATE that updates all the foreign keys to null.
|
||||
*
|
||||
* @param PersistentCollection $coll
|
||||
*/
|
||||
protected function _getDeleteSql(PersistentCollection $coll)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL parameters for the corresponding SQL statement to delete
|
||||
* the given collection.
|
||||
*
|
||||
* @param PersistentCollection $coll
|
||||
*/
|
||||
protected function _getDeleteSqlParameters(PersistentCollection $coll)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Gets the SQL parameters for the corresponding SQL statement to insert the given
|
||||
* element of the given collection into the database.
|
||||
*
|
||||
* @param PersistentCollection $coll
|
||||
* @param mixed $element
|
||||
*/
|
||||
protected function _getInsertRowSqlParameters(PersistentCollection $coll, $element)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Gets the SQL parameters for the corresponding SQL statement to delete the given
|
||||
* element from the given collection.
|
||||
*
|
||||
* @param PersistentCollection $coll
|
||||
* @param mixed $element
|
||||
*/
|
||||
protected function _getDeleteRowSqlParameters(PersistentCollection $coll, $element)
|
||||
{}
|
||||
}
|
@ -70,13 +70,6 @@ class StandardEntityPersister
|
||||
*/
|
||||
protected $_em;
|
||||
|
||||
/**
|
||||
* The EventManager instance.
|
||||
*
|
||||
* @var Doctrine\Common\EventManager
|
||||
*/
|
||||
protected $_evm;
|
||||
|
||||
/**
|
||||
* Queued inserts.
|
||||
*
|
||||
@ -96,7 +89,6 @@ class StandardEntityPersister
|
||||
{
|
||||
$this->_em = $em;
|
||||
$this->_platform = $em->getConnection()->getDatabasePlatform();
|
||||
$this->_evm = $em->getEventManager();
|
||||
$this->_entityName = $class->name;
|
||||
$this->_conn = $em->getConnection();
|
||||
$this->_class = $class;
|
||||
@ -206,17 +198,9 @@ class StandardEntityPersister
|
||||
);
|
||||
$tableName = $this->_class->primaryTable['name'];
|
||||
|
||||
if ($this->_evm->hasListeners(Events::preUpdate)) {
|
||||
$this->_preUpdate($entity);
|
||||
}
|
||||
|
||||
if (isset($updateData[$tableName]) && $updateData[$tableName]) {
|
||||
$this->_doUpdate($entity, $tableName, $updateData[$tableName], $id);
|
||||
}
|
||||
|
||||
if ($this->_evm->hasListeners(Events::postUpdate)) {
|
||||
$this->_postUpdate($entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -458,7 +442,9 @@ class StandardEntityPersister
|
||||
$proxy = $this->_em->getProxyFactory()->getAssociationProxy($entity, $assoc, $joinColumnValues);
|
||||
$this->_class->reflFields[$field]->setValue($entity, $proxy);
|
||||
} else {
|
||||
//TODO: Eager fetch
|
||||
// Eager load
|
||||
//TODO: Allow more efficient and configurable batching of these loads
|
||||
$assoc->load($entity, new $assoc->targetEntityName, $this->_em, $joinColumnValues);
|
||||
}
|
||||
} else {
|
||||
// Inject collection
|
||||
|
@ -1310,6 +1310,71 @@ class UnitOfWork implements PropertyChangedListener
|
||||
return $managedCopy;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param $entity
|
||||
* @return unknown_type
|
||||
*/
|
||||
public function refresh($entity)
|
||||
{
|
||||
$visited = array();
|
||||
return $this->_doRefresh($entity, $visited);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a refresh operation on an entity.
|
||||
*
|
||||
* @param object $entity The entity to refresh.
|
||||
* @param array $visited The already visited entities during cascades.
|
||||
*/
|
||||
private function _doRefresh($entity, array &$visited)
|
||||
{
|
||||
$oid = spl_object_hash($entity);
|
||||
if (isset($visited[$oid])) {
|
||||
return; // Prevent infinite recursion
|
||||
}
|
||||
|
||||
$visited[$oid] = $entity; // mark visited
|
||||
|
||||
$class = $this->_em->getClassMetadata(get_class($entity));
|
||||
switch ($this->getEntityState($entity)) {
|
||||
case self::STATE_MANAGED:
|
||||
$this->getEntityPersister($class->name)->load(
|
||||
array_combine($class->identifier, $this->_entityIdentifiers[$oid]),
|
||||
$entity
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw DoctrineException::updateMe("NEW, REMOVED or DETACHED entity can not be refreshed.");
|
||||
}
|
||||
$this->_cascadeRefresh($entity, $visited);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cascades a refresh operation to associated entities.
|
||||
*
|
||||
* @param object $entity
|
||||
* @param array $visited
|
||||
*/
|
||||
private function _cascadeRefresh($entity, array &$visited)
|
||||
{
|
||||
$class = $this->_em->getClassMetadata(get_class($entity));
|
||||
foreach ($class->associationMappings as $assocMapping) {
|
||||
if ( ! $assocMapping->isCascadeRefresh) {
|
||||
continue;
|
||||
}
|
||||
$relatedEntities = $class->reflFields[$assocMapping->sourceFieldName]->getValue($entity);
|
||||
if ($relatedEntities instanceof Collection) {
|
||||
foreach ($relatedEntities as $relatedEntity) {
|
||||
$this->_doRefresh($relatedEntity, $visited);
|
||||
}
|
||||
} else if ($relatedEntities !== null) {
|
||||
$this->_doRefresh($relatedEntities, $visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cascades a merge operation to associated entities.
|
||||
*
|
||||
@ -1324,7 +1389,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ( ! $assocMapping->isCascadeMerge) {
|
||||
continue;
|
||||
}
|
||||
$relatedEntities = $class->reflFields[$assocMapping->getSourceFieldName()]
|
||||
$relatedEntities = $class->reflFields[$assocMapping->sourceFieldName]
|
||||
->getValue($entity);
|
||||
if ($relatedEntities instanceof Collection) {
|
||||
foreach ($relatedEntities as $relatedEntity) {
|
||||
|
@ -173,8 +173,8 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
array())->fetchColumn();
|
||||
$this->assertEquals(10, $count);
|
||||
|
||||
//$user->groups->clear();
|
||||
unset($user->groups);
|
||||
$user->groups->clear();
|
||||
//unset($user->groups);
|
||||
|
||||
$this->_em->flush();
|
||||
|
||||
@ -184,6 +184,35 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertEquals(0, $count);
|
||||
}
|
||||
|
||||
/* NOT YET IMPLEMENTED
|
||||
public function testOneToManyOrphanDelete()
|
||||
{
|
||||
$user = new CmsUser;
|
||||
$user->name = 'Guilherme';
|
||||
$user->username = 'gblanco';
|
||||
$user->status = 'developer';
|
||||
|
||||
for ($i=0; $i<3; ++$i) {
|
||||
$phone = new CmsPhonenumber;
|
||||
$phone->phonenumber = 100 + $i;
|
||||
$user->addPhonenumber($phone);
|
||||
}
|
||||
|
||||
$this->_em->persist($user);
|
||||
|
||||
$this->_em->flush();
|
||||
|
||||
$user->getPhonenumbers()->remove(0);
|
||||
|
||||
$this->_em->flush();
|
||||
|
||||
// Check that the links in the association table have been deleted
|
||||
$count = $this->_em->getConnection()->execute("SELECT COUNT(*) FROM cms_phonenumbers",
|
||||
array())->fetchColumn();
|
||||
$this->assertEquals(2, $count); // only 2 remaining
|
||||
|
||||
}*/
|
||||
|
||||
public function testBasicQuery()
|
||||
{
|
||||
$user = new CmsUser;
|
||||
@ -304,4 +333,21 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$query = $this->_em->createQuery("select u, g from Doctrine\Tests\Models\CMS\CmsUser u inner join u.groups g");
|
||||
$this->assertEquals(0, count($query->getResultList()));
|
||||
}
|
||||
|
||||
public function testBasicRefresh()
|
||||
{
|
||||
$user = new CmsUser;
|
||||
$user->name = 'Guilherme';
|
||||
$user->username = 'gblanco';
|
||||
$user->status = 'developer';
|
||||
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
|
||||
$user->status = 'mascot';
|
||||
|
||||
$this->assertEquals('mascot', $user->status);
|
||||
$this->_em->refresh($user);
|
||||
$this->assertEquals('developer', $user->status);
|
||||
}
|
||||
}
|
@ -83,7 +83,7 @@ class OneToOneBidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctional
|
||||
$this->_createFixture();
|
||||
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCustomer');
|
||||
$metadata->getAssociationMapping('cart')->fetchMode = AssociationMapping::FETCH_LAZY;
|
||||
$metadata->getAssociationMapping('mentor')->fetchMode = AssociationMapping::FETCH_EAGER;
|
||||
|
||||
$query = $this->_em->createQuery('select c from Doctrine\Tests\Models\ECommerce\ECommerceCustomer c');
|
||||
$result = $query->getResultList();
|
||||
|
Loading…
x
Reference in New Issue
Block a user