Commit 28ca2acb authored by romanb's avatar romanb

[2.0] Refined implementation and semantics of the merge and detach operations....

[2.0] Refined implementation and semantics of the merge and detach operations. General cleanups and API improvements. Added a testcase for detaching/serializing->unserializing->modifying->merging to demonstrate the transparent serialization.
parent da07bf4a
......@@ -25,9 +25,9 @@
<xs:complexType name="cascade-type">
<xs:sequence>
<xs:element name="cascade-all" type="orm:emptyType" minOccurs="0"/>
<xs:element name="cascade-save" type="orm:emptyType" minOccurs="0"/>
<xs:element name="cascade-persist" type="orm:emptyType" minOccurs="0"/>
<xs:element name="cascade-merge" type="orm:emptyType" minOccurs="0"/>
<xs:element name="cascade-delete" type="orm:emptyType" minOccurs="0"/>
<xs:element name="cascade-remove" type="orm:emptyType" minOccurs="0"/>
<xs:element name="cascade-refresh" type="orm:emptyType" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
......
......@@ -416,21 +416,25 @@ class EntityManager
}
/**
* Detaches an entity from the EntityManager.
* Detaches an entity from the EntityManager, causing a managed entity to
* become detached. Unflushed changes made to the entity if any
* (including removal of the entity), will not be synchronized to the database.
* Entities which previously referenced the detached entity will continue to
* reference it.
*
* @param object $entity The entity to detach.
* @return boolean
*/
public function detach($entity)
{
return $this->_unitOfWork->removeFromIdentityMap($entity);
$this->_unitOfWork->detach($entity);
}
/**
* Merges the state of a detached entity into the persistence context
* of this EntityManager.
* of this EntityManager and returns the managed copy of the entity.
* The entity passed to merge will not become associated/managed with this EntityManager.
*
* @param object $entity The entity to merge into the persistence context.
* @param object $entity The detached entity to merge into the persistence context.
* @return object The managed copy of the entity.
*/
public function merge($entity)
......
......@@ -66,8 +66,8 @@ abstract class AssociationMapping
);
public $cascades = array();
public $isCascadeDelete;
public $isCascadeSave;
public $isCascadeRemove;
public $isCascadePersist;
public $isCascadeRefresh;
public $isCascadeMerge;
......@@ -184,8 +184,8 @@ abstract class AssociationMapping
(bool)$mapping['optional'] : true;
$this->cascades = isset($mapping['cascade']) ?
(array)$mapping['cascade'] : array();
$this->isCascadeDelete = in_array('delete', $this->cascades);
$this->isCascadeSave = in_array('save', $this->cascades);
$this->isCascadeRemove = in_array('remove', $this->cascades);
$this->isCascadePersist = in_array('persist', $this->cascades);
$this->isCascadeRefresh = in_array('refresh', $this->cascades);
$this->isCascadeMerge = in_array('merge', $this->cascades);
}
......@@ -196,9 +196,9 @@ abstract class AssociationMapping
*
* @return boolean
*/
public function isCascadeDelete()
public function isCascadeRemove()
{
return $this->isCascadeDelete;
return $this->isCascadeRemove;
}
/**
......@@ -207,9 +207,9 @@ abstract class AssociationMapping
*
* @return boolean
*/
public function isCascadeSave()
public function isCascadePersist()
{
return $this->isCascadeSave;
return $this->isCascadePersist;
}
/**
......@@ -374,7 +374,7 @@ abstract class AssociationMapping
*/
public function usesJoinTable()
{
return (bool)$this->joinTable;
return (bool) $this->joinTable;
}
/**
......
......@@ -269,11 +269,11 @@ class XmlDriver extends AbstractFileDriver
private function _getCascadeMappings($cascadeElement)
{
$cascades = array();
if (isset($cascadeElement->{'cascade-save'})) {
$cascades[] = 'save';
if (isset($cascadeElement->{'cascade-persist'})) {
$cascades[] = 'persist';
}
if (isset($cascadeElement->{'cascade-delete'})) {
$cascades[] = 'delete';
if (isset($cascadeElement->{'cascade-remove'})) {
$cascades[] = 'remove';
}
if (isset($cascadeElement->{'cascade-merge'})) {
$cascades[] = 'merge';
......
......@@ -255,11 +255,11 @@ class YamlDriver extends AbstractFileDriver
private function _getCascadeMappings($cascadeElement)
{
$cascades = array();
if (isset($cascadeElement['cascadeSave'])) {
$cascades[] = 'save';
if (isset($cascadeElement['cascadePersist'])) {
$cascades[] = 'persist';
}
if (isset($cascadeElement['cascadeDelete'])) {
$cascades[] = 'delete';
if (isset($cascadeElement['cascadeRemove'])) {
$cascades[] = 'remove';
}
if (isset($cascadeElement['cascadeMerge'])) {
$cascades[] = 'merge';
......
......@@ -250,11 +250,11 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
// Set back reference to owner
if ($this->_association->isOneToMany()) {
// OneToMany
$this->_typeClass->getReflectionProperty($this->_backRefFieldName)
$this->_typeClass->reflFields[$this->_backRefFieldName]
->setValue($value, $this->_owner);
} else {
// ManyToMany
$this->_typeClass->getReflectionProperty($this->_backRefFieldName)
$this->_typeClass->reflFields[$this->_backRefFieldName]
->getValue($value)->add($this->_owner);
}
}
......@@ -418,6 +418,16 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
{
$this->_isDirty = $dirty;
}
/**
*
* @param $bool
* @return unknown_type
*/
public function setInitialized($bool)
{
$this->_initialized = $bool;
}
/* Serializable implementation */
......
This diff is collapsed.
......@@ -26,7 +26,7 @@ class CmsUser
*/
public $name;
/**
* @OneToMany(targetEntity="CmsPhonenumber", mappedBy="user", cascade={"save", "delete"}, orphanRemoval=true)
* @OneToMany(targetEntity="CmsPhonenumber", mappedBy="user", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
*/
public $phonenumbers;
/**
......@@ -34,11 +34,11 @@ class CmsUser
*/
public $articles;
/**
* @OneToOne(targetEntity="CmsAddress", mappedBy="user", cascade={"save"})
* @OneToOne(targetEntity="CmsAddress", mappedBy="user", cascade={"persist"})
*/
public $address;
/**
* @ManyToMany(targetEntity="CmsGroup", cascade={"save"})
* @ManyToMany(targetEntity="CmsGroup", cascade={"persist"})
* @JoinTable(name="cms_users_groups",
joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")})
......
......@@ -33,7 +33,7 @@ class ECommerceCart
private $customer;
/**
* @ManyToMany(targetEntity="ECommerceProduct", cascade={"save"})
* @ManyToMany(targetEntity="ECommerceProduct", cascade={"persist"})
* @JoinTable(name="ecommerce_carts_products",
joinColumns={@JoinColumn(name="cart_id", referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="product_id", referencedColumnName="id")})
......
......@@ -32,7 +32,7 @@ class ECommerceCategory
private $products;
/**
* @OneToMany(targetEntity="ECommerceCategory", mappedBy="parent", cascade={"save"})
* @OneToMany(targetEntity="ECommerceCategory", mappedBy="parent", cascade={"persist"})
*/
private $children;
......
......@@ -25,7 +25,7 @@ class ECommerceCustomer
private $name;
/**
* @OneToOne(targetEntity="ECommerceCart", mappedBy="customer", cascade={"save"})
* @OneToOne(targetEntity="ECommerceCart", mappedBy="customer", cascade={"persist"})
*/
private $cart;
......@@ -34,7 +34,7 @@ class ECommerceCustomer
* only one customer at the time, while a customer can choose only one
* mentor. Not properly appropriate but it works.
*
* @OneToOne(targetEntity="ECommerceCustomer", cascade={"save"})
* @OneToOne(targetEntity="ECommerceCustomer", cascade={"persist"})
* @JoinColumn(name="mentor_id", referencedColumnName="id")
*/
private $mentor;
......
......@@ -27,18 +27,18 @@ class ECommerceProduct
private $name;
/**
* @OneToOne(targetEntity="ECommerceShipping", cascade={"save"})
* @OneToOne(targetEntity="ECommerceShipping", cascade={"persist"})
* @JoinColumn(name="shipping_id", referencedColumnName="id")
*/
private $shipping;
/**
* @OneToMany(targetEntity="ECommerceFeature", mappedBy="product", cascade={"save"})
* @OneToMany(targetEntity="ECommerceFeature", mappedBy="product", cascade={"persist"})
*/
private $features;
/**
* @ManyToMany(targetEntity="ECommerceCategory", cascade={"save"})
* @ManyToMany(targetEntity="ECommerceCategory", cascade={"persist"})
* @JoinTable(name="ecommerce_products_categories",
joinColumns={@JoinColumn(name="product_id", referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="category_id", referencedColumnName="id")})
......@@ -48,7 +48,7 @@ class ECommerceProduct
/**
* This relation is saved with two records in the association table for
* simplicity.
* @ManyToMany(targetEntity="ECommerceProduct", cascade={"save"})
* @ManyToMany(targetEntity="ECommerceProduct", cascade={"persist"})
* @JoinTable(name="ecommerce_products_related",
joinColumns={@JoinColumn(name="product_id", referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="related_id", referencedColumnName="id")})
......
......@@ -19,7 +19,7 @@ class ForumUser
*/
public $username;
/**
* @OneToOne(targetEntity="ForumAvatar", cascade={"save"})
* @OneToOne(targetEntity="ForumAvatar", cascade={"persist"})
* @JoinColumn(name="avatar_id", referencedColumnName="id")
*/
public $avatar;
......
......@@ -3,6 +3,7 @@
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\ORM\UnitOfWork;
require_once __DIR__ . '/../../TestInit.php';
......@@ -42,5 +43,48 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertTrue($this->_em->contains($user2));
$this->assertEquals('Roman B.', $user2->name);
}
public function testSerializeUnserializeModifyMerge()
{
$user = new CmsUser;
$user->name = 'Guilherme';
$user->username = 'gblanco';
$user->status = 'developer';
$ph1 = new CmsPhonenumber;
$ph1->phonenumber = 1234;
$user->addPhonenumber($ph1);
$this->_em->persist($user);
$this->_em->flush();
$this->assertTrue($this->_em->contains($user));
$serialized = serialize($user);
$this->_em->clear();
$this->assertFalse($this->_em->contains($user));
unset($user);
$user = unserialize($serialized);
$ph2 = new CmsPhonenumber;
$ph2->phonenumber = 56789;
$user->addPhonenumber($ph2);
$this->assertEquals(2, count($user->getPhonenumbers()));
$this->assertFalse($this->_em->contains($user));
$this->_em->persist($ph2);
//$removed = $user->removePhonenumber(1); // [romanb] this is currently broken, I'm on it.
// Merge back in
$user = $this->_em->merge($user); // merge cascaded to phonenumbers
$this->_em->flush();
$this->assertTrue($this->_em->contains($user));
$this->assertEquals(2, count($user->getPhonenumbers()));
$phonenumbers = $user->getPhonenumbers();
$this->assertTrue($this->_em->contains($phonenumbers[0]));
$this->assertTrue($this->_em->contains($phonenumbers[1]));
}
}
......@@ -41,7 +41,7 @@ class XmlDriverTest extends \Doctrine\Tests\OrmTestCase
$this->assertTrue(isset($class->associationMappings['phonenumbers']));
$this->assertFalse($class->associationMappings['phonenumbers']->isOwningSide);
$this->assertTrue($class->associationMappings['phonenumbers']->isInverseSide());
$this->assertTrue($class->associationMappings['phonenumbers']->isCascadeSave);
$this->assertTrue($class->associationMappings['phonenumbers']->isCascadePersist);
$this->assertTrue($class->associationMappings['groups'] instanceof \Doctrine\ORM\Mapping\ManyToManyMapping);
$this->assertTrue(isset($class->associationMappings['groups']));
......
......@@ -41,7 +41,7 @@ class YamlDriverTest extends \Doctrine\Tests\OrmTestCase
$this->assertTrue(isset($class->associationMappings['phonenumbers']));
$this->assertFalse($class->associationMappings['phonenumbers']->isOwningSide);
$this->assertTrue($class->associationMappings['phonenumbers']->isInverseSide());
$this->assertTrue($class->associationMappings['phonenumbers']->isCascadeSave);
$this->assertTrue($class->associationMappings['phonenumbers']->isCascadePersist);
$this->assertTrue($class->associationMappings['groups'] instanceof \Doctrine\ORM\Mapping\ManyToManyMapping);
$this->assertTrue(isset($class->associationMappings['groups']));
......
......@@ -19,7 +19,7 @@
<one-to-many field="phonenumbers" targetEntity="Phonenumber" mappedBy="user">
<cascade>
<cascade-save/>
<cascade-persist/>
</cascade>
</one-to-many>
......
......@@ -20,7 +20,7 @@ YamlMappingTest\User:
phonenumbers:
targetEntity: Phonenumber
mappedBy: user
cascade: cascadeSave
cascade: cascadePersist
manyToMany:
groups:
targetEntity: Group
......
......@@ -32,7 +32,7 @@ class InsertPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase
//$mem = memory_get_usage();
//echo "Memory usage before: " . ($mem / 1024) . " KB" . PHP_EOL;
$batchSize = 20;
for ($i=0; $i<10000; ++$i) {
for ($i=1; $i<=10000; ++$i) {
$user = new CmsUser;
$user->status = 'user';
$user->username = 'user' . $i;
......@@ -43,7 +43,6 @@ class InsertPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase
$this->_em->clear();
}
}
//$memAfter = memory_get_usage();
//echo "Memory usage after: " . ($memAfter / 1024) . " KB" . PHP_EOL;
......
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