Commit ae1b9371 authored by romanb's avatar romanb

[2.0] Fixed #2366.

parent be0088f0
......@@ -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
......@@ -301,10 +301,13 @@ 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();
$this->_changed();
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)
{}
}
\ No newline at end of file
......@@ -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);
}
}
/**
......@@ -425,7 +409,7 @@ class StandardEntityPersister
if (isset($this->_class->fieldNames[$column])) {
$fieldName = $this->_class->fieldNames[$column];
$data[$fieldName] = Type::getType($this->_class->getTypeOfField($fieldName))
->convertToPHPValue($value, $this->_platform);
->convertToPHPValue($value, $this->_platform);
} else {
$joinColumnValues[$column] = $value;
}
......@@ -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
......
......@@ -1309,6 +1309,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();
......@@ -183,6 +183,35 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
array())->fetchColumn();
$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()
{
......@@ -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);
}
}
\ No newline at end of file
......@@ -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();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment