Merge branch 'master' of git://github.com/doctrine/doctrine2 into SupportCustomIdGenerators
This commit is contained in:
commit
f13f44a2fc
@ -23,6 +23,8 @@ use PDO,
|
|||||||
Doctrine\ORM\Mapping\ClassMetadata,
|
Doctrine\ORM\Mapping\ClassMetadata,
|
||||||
Doctrine\ORM\PersistentCollection,
|
Doctrine\ORM\PersistentCollection,
|
||||||
Doctrine\ORM\Query,
|
Doctrine\ORM\Query,
|
||||||
|
Doctrine\ORM\Event\LifecycleEventArgs,
|
||||||
|
Doctrine\ORM\Events,
|
||||||
Doctrine\Common\Collections\ArrayCollection,
|
Doctrine\Common\Collections\ArrayCollection,
|
||||||
Doctrine\Common\Collections\Collection;
|
Doctrine\Common\Collections\Collection;
|
||||||
|
|
||||||
@ -235,7 +237,21 @@ class ObjectHydrator extends AbstractHydrator
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->_hints['fetchAlias'] = $dqlAlias;
|
$this->_hints['fetchAlias'] = $dqlAlias;
|
||||||
return $this->_uow->createEntity($className, $data, $this->_hints);
|
|
||||||
|
$entity = $this->_uow->createEntity($className, $data, $this->_hints);
|
||||||
|
|
||||||
|
//TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
|
||||||
|
if (isset($this->_ce[$className]->lifecycleCallbacks[Events::postLoad])) {
|
||||||
|
$this->_ce[$className]->invokeLifecycleCallbacks(Events::postLoad, $entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
$evm = $this->_em->getEventManager();
|
||||||
|
|
||||||
|
if ($evm->hasListeners(Events::postLoad)) {
|
||||||
|
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function _getEntityFromIdentityMap($className, array $data)
|
private function _getEntityFromIdentityMap($className, array $data)
|
||||||
|
@ -19,10 +19,12 @@
|
|||||||
|
|
||||||
namespace Doctrine\ORM\Internal\Hydration;
|
namespace Doctrine\ORM\Internal\Hydration;
|
||||||
|
|
||||||
use \PDO;
|
use \PDO,
|
||||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
Doctrine\DBAL\Types\Type,
|
||||||
use Doctrine\DBAL\Types\Type;
|
Doctrine\ORM\Mapping\ClassMetadata,
|
||||||
use Doctrine\ORM\Query;
|
Doctrine\ORM\Event\LifecycleEventArgs,
|
||||||
|
Doctrine\ORM\Events,
|
||||||
|
Doctrine\ORM\Query;
|
||||||
|
|
||||||
class SimpleObjectHydrator extends AbstractHydrator
|
class SimpleObjectHydrator extends AbstractHydrator
|
||||||
{
|
{
|
||||||
@ -125,7 +127,21 @@ class SimpleObjectHydrator extends AbstractHydrator
|
|||||||
$this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
|
$this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
$result[] = $this->_em->getUnitOfWork()->createEntity($entityName, $data, $this->_hints);
|
$uow = $this->_em->getUnitOfWork();
|
||||||
|
$entity = $uow->createEntity($entityName, $data, $this->_hints);
|
||||||
|
|
||||||
|
//TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
|
||||||
|
if (isset($this->class->lifecycleCallbacks[Events::postLoad])) {
|
||||||
|
$this->class->invokeLifecycleCallbacks(Events::postLoad, $entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
$evm = $this->_em->getEventManager();
|
||||||
|
|
||||||
|
if ($evm->hasListeners(Events::postLoad)) {
|
||||||
|
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[] = $entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,6 +21,7 @@ namespace Doctrine\ORM;
|
|||||||
|
|
||||||
use Doctrine\ORM\Mapping\ClassMetadata,
|
use Doctrine\ORM\Mapping\ClassMetadata,
|
||||||
Doctrine\Common\Collections\Collection,
|
Doctrine\Common\Collections\Collection,
|
||||||
|
Doctrine\Common\Collections\ArrayCollection,
|
||||||
Closure;
|
Closure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,8 +115,8 @@ final class PersistentCollection implements Collection
|
|||||||
*/
|
*/
|
||||||
public function __construct(EntityManager $em, $class, $coll)
|
public function __construct(EntityManager $em, $class, $coll)
|
||||||
{
|
{
|
||||||
$this->coll = $coll;
|
$this->coll = $coll;
|
||||||
$this->em = $em;
|
$this->em = $em;
|
||||||
$this->typeClass = $class;
|
$this->typeClass = $class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,8 +130,8 @@ final class PersistentCollection implements Collection
|
|||||||
*/
|
*/
|
||||||
public function setOwner($entity, array $assoc)
|
public function setOwner($entity, array $assoc)
|
||||||
{
|
{
|
||||||
$this->owner = $entity;
|
$this->owner = $entity;
|
||||||
$this->association = $assoc;
|
$this->association = $assoc;
|
||||||
$this->backRefFieldName = $assoc['inversedBy'] ?: $assoc['mappedBy'];
|
$this->backRefFieldName = $assoc['inversedBy'] ?: $assoc['mappedBy'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,16 +161,18 @@ final class PersistentCollection implements Collection
|
|||||||
public function hydrateAdd($element)
|
public function hydrateAdd($element)
|
||||||
{
|
{
|
||||||
$this->coll->add($element);
|
$this->coll->add($element);
|
||||||
|
|
||||||
// If _backRefFieldName is set and its a one-to-many association,
|
// If _backRefFieldName is set and its a one-to-many association,
|
||||||
// we need to set the back reference.
|
// we need to set the back reference.
|
||||||
if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) {
|
if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) {
|
||||||
// Set back reference to owner
|
// Set back reference to owner
|
||||||
$this->typeClass->reflFields[$this->backRefFieldName]
|
$this->typeClass->reflFields[$this->backRefFieldName]->setValue(
|
||||||
->setValue($element, $this->owner);
|
$element, $this->owner
|
||||||
|
);
|
||||||
|
|
||||||
$this->em->getUnitOfWork()->setOriginalEntityProperty(
|
$this->em->getUnitOfWork()->setOriginalEntityProperty(
|
||||||
spl_object_hash($element),
|
spl_object_hash($element), $this->backRefFieldName, $this->owner
|
||||||
$this->backRefFieldName,
|
);
|
||||||
$this->owner);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,12 +186,14 @@ final class PersistentCollection implements Collection
|
|||||||
public function hydrateSet($key, $element)
|
public function hydrateSet($key, $element)
|
||||||
{
|
{
|
||||||
$this->coll->set($key, $element);
|
$this->coll->set($key, $element);
|
||||||
|
|
||||||
// If _backRefFieldName is set, then the association is bidirectional
|
// If _backRefFieldName is set, then the association is bidirectional
|
||||||
// and we need to set the back reference.
|
// and we need to set the back reference.
|
||||||
if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) {
|
if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) {
|
||||||
// Set back reference to owner
|
// Set back reference to owner
|
||||||
$this->typeClass->reflFields[$this->backRefFieldName]
|
$this->typeClass->reflFields[$this->backRefFieldName]->setValue(
|
||||||
->setValue($element, $this->owner);
|
$element, $this->owner
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,23 +203,31 @@ final class PersistentCollection implements Collection
|
|||||||
*/
|
*/
|
||||||
public function initialize()
|
public function initialize()
|
||||||
{
|
{
|
||||||
if ( ! $this->initialized && $this->association) {
|
if ($this->initialized || ! $this->association) {
|
||||||
if ($this->isDirty) {
|
return;
|
||||||
// Has NEW objects added through add(). Remember them.
|
|
||||||
$newObjects = $this->coll->toArray();
|
|
||||||
}
|
|
||||||
$this->coll->clear();
|
|
||||||
$this->em->getUnitOfWork()->loadCollection($this);
|
|
||||||
$this->takeSnapshot();
|
|
||||||
// Reattach NEW objects added through add(), if any.
|
|
||||||
if (isset($newObjects)) {
|
|
||||||
foreach ($newObjects as $obj) {
|
|
||||||
$this->coll->add($obj);
|
|
||||||
}
|
|
||||||
$this->isDirty = true;
|
|
||||||
}
|
|
||||||
$this->initialized = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Has NEW objects added through add(). Remember them.
|
||||||
|
$newObjects = array();
|
||||||
|
|
||||||
|
if ($this->isDirty) {
|
||||||
|
$newObjects = $this->coll->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->coll->clear();
|
||||||
|
$this->em->getUnitOfWork()->loadCollection($this);
|
||||||
|
$this->takeSnapshot();
|
||||||
|
|
||||||
|
// Reattach NEW objects added through add(), if any.
|
||||||
|
if ($newObjects) {
|
||||||
|
foreach ($newObjects as $obj) {
|
||||||
|
$this->coll->add($obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->isDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -224,7 +237,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function takeSnapshot()
|
public function takeSnapshot()
|
||||||
{
|
{
|
||||||
$this->snapshot = $this->coll->toArray();
|
$this->snapshot = $this->coll->toArray();
|
||||||
$this->isDirty = false;
|
$this->isDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -246,8 +259,11 @@ final class PersistentCollection implements Collection
|
|||||||
*/
|
*/
|
||||||
public function getDeleteDiff()
|
public function getDeleteDiff()
|
||||||
{
|
{
|
||||||
return array_udiff_assoc($this->snapshot, $this->coll->toArray(),
|
return array_udiff_assoc(
|
||||||
function($a, $b) {return $a === $b ? 0 : 1;});
|
$this->snapshot,
|
||||||
|
$this->coll->toArray(),
|
||||||
|
function($a, $b) { return $a === $b ? 0 : 1; }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -258,8 +274,11 @@ final class PersistentCollection implements Collection
|
|||||||
*/
|
*/
|
||||||
public function getInsertDiff()
|
public function getInsertDiff()
|
||||||
{
|
{
|
||||||
return array_udiff_assoc($this->coll->toArray(), $this->snapshot,
|
return array_udiff_assoc(
|
||||||
function($a, $b) {return $a === $b ? 0 : 1;});
|
$this->coll->toArray(),
|
||||||
|
$this->snapshot,
|
||||||
|
function($a, $b) { return $a === $b ? 0 : 1; }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -277,12 +296,17 @@ final class PersistentCollection implements Collection
|
|||||||
*/
|
*/
|
||||||
private function changed()
|
private function changed()
|
||||||
{
|
{
|
||||||
if ( ! $this->isDirty) {
|
if ($this->isDirty) {
|
||||||
$this->isDirty = true;
|
return;
|
||||||
if ($this->association !== null && $this->association['isOwningSide'] && $this->association['type'] == ClassMetadata::MANY_TO_MANY &&
|
}
|
||||||
$this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
|
|
||||||
$this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner);
|
$this->isDirty = true;
|
||||||
}
|
|
||||||
|
if ($this->association !== null &&
|
||||||
|
$this->association['isOwningSide'] &&
|
||||||
|
$this->association['type'] == ClassMetadata::MANY_TO_MANY &&
|
||||||
|
$this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
|
||||||
|
$this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,6 +355,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function first()
|
public function first()
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->first();
|
return $this->coll->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,6 +363,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function last()
|
public function last()
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->last();
|
return $this->coll->last();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,13 +377,19 @@ final class PersistentCollection implements Collection
|
|||||||
// not used we can issue a straight SQL delete/update on the
|
// not used we can issue a straight SQL delete/update on the
|
||||||
// association (table). Without initializing the collection.
|
// association (table). Without initializing the collection.
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
$removed = $this->coll->remove($key);
|
$removed = $this->coll->remove($key);
|
||||||
if ($removed) {
|
|
||||||
$this->changed();
|
if ( ! $removed) {
|
||||||
if ($this->association !== null && $this->association['type'] == ClassMetadata::ONE_TO_MANY &&
|
return $removed;
|
||||||
$this->association['orphanRemoval']) {
|
}
|
||||||
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
|
|
||||||
}
|
$this->changed();
|
||||||
|
|
||||||
|
if ($this->association !== null &&
|
||||||
|
$this->association['type'] == ClassMetadata::ONE_TO_MANY &&
|
||||||
|
$this->association['orphanRemoval']) {
|
||||||
|
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $removed;
|
return $removed;
|
||||||
@ -368,25 +400,36 @@ final class PersistentCollection implements Collection
|
|||||||
*/
|
*/
|
||||||
public function removeElement($element)
|
public function removeElement($element)
|
||||||
{
|
{
|
||||||
// TODO: Assuming the identity of entities in a collection is always based
|
if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||||
// on their primary key (there is no equals/hashCode in PHP),
|
if ($this->coll->contains($element)) {
|
||||||
// if the collection is not initialized, we could issue a straight
|
return $this->coll->removeElement($element);
|
||||||
// SQL DELETE/UPDATE on the association (table) without initializing
|
}
|
||||||
// the collection.
|
|
||||||
/*if ( ! $this->initialized) {
|
$persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
|
||||||
$this->em->getUnitOfWork()->getCollectionPersister($this->association)
|
|
||||||
->deleteRows($this, $element);
|
if ($persister->removeElement($this, $element)) {
|
||||||
}*/
|
return $element;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
$removed = $this->coll->removeElement($element);
|
$removed = $this->coll->removeElement($element);
|
||||||
if ($removed) {
|
|
||||||
$this->changed();
|
if ( ! $removed) {
|
||||||
if ($this->association !== null && $this->association['type'] == ClassMetadata::ONE_TO_MANY &&
|
return $removed;
|
||||||
$this->association['orphanRemoval']) {
|
|
||||||
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->changed();
|
||||||
|
|
||||||
|
if ($this->association !== null &&
|
||||||
|
$this->association['type'] == ClassMetadata::ONE_TO_MANY &&
|
||||||
|
$this->association['orphanRemoval']) {
|
||||||
|
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
|
||||||
|
}
|
||||||
|
|
||||||
return $removed;
|
return $removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,6 +439,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function containsKey($key)
|
public function containsKey($key)
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->containsKey($key);
|
return $this->coll->containsKey($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,14 +448,14 @@ final class PersistentCollection implements Collection
|
|||||||
*/
|
*/
|
||||||
public function contains($element)
|
public function contains($element)
|
||||||
{
|
{
|
||||||
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
if ( ! $this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||||
return $this->coll->contains($element) ||
|
$persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
|
||||||
$this->em->getUnitOfWork()
|
|
||||||
->getCollectionPersister($this->association)
|
return $this->coll->contains($element) || $persister->contains($this, $element);
|
||||||
->contains($this, $element);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->contains($element);
|
return $this->coll->contains($element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,6 +465,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function exists(Closure $p)
|
public function exists(Closure $p)
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->exists($p);
|
return $this->coll->exists($p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,6 +475,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function indexOf($element)
|
public function indexOf($element)
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->indexOf($element);
|
return $this->coll->indexOf($element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,6 +485,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function get($key)
|
public function get($key)
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->get($key);
|
return $this->coll->get($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,6 +495,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function getKeys()
|
public function getKeys()
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->getKeys();
|
return $this->coll->getKeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,6 +505,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function getValues()
|
public function getValues()
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->getValues();
|
return $this->coll->getValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,13 +514,14 @@ final class PersistentCollection implements Collection
|
|||||||
*/
|
*/
|
||||||
public function count()
|
public function count()
|
||||||
{
|
{
|
||||||
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
if ( ! $this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||||
return $this->em->getUnitOfWork()
|
$persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
|
||||||
->getCollectionPersister($this->association)
|
|
||||||
->count($this) + ($this->isDirty ? $this->coll->count() : 0);
|
return $persister->count($this) + ($this->isDirty ? $this->coll->count() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->count();
|
return $this->coll->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,7 +531,9 @@ final class PersistentCollection implements Collection
|
|||||||
public function set($key, $value)
|
public function set($key, $value)
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
$this->coll->set($key, $value);
|
$this->coll->set($key, $value);
|
||||||
|
|
||||||
$this->changed();
|
$this->changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,7 +543,9 @@ final class PersistentCollection implements Collection
|
|||||||
public function add($value)
|
public function add($value)
|
||||||
{
|
{
|
||||||
$this->coll->add($value);
|
$this->coll->add($value);
|
||||||
|
|
||||||
$this->changed();
|
$this->changed();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,6 +555,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function isEmpty()
|
public function isEmpty()
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->isEmpty();
|
return $this->coll->isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -510,6 +565,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function getIterator()
|
public function getIterator()
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->getIterator();
|
return $this->coll->getIterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,6 +575,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function map(Closure $func)
|
public function map(Closure $func)
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->map($func);
|
return $this->coll->map($func);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,6 +585,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function filter(Closure $p)
|
public function filter(Closure $p)
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->filter($p);
|
return $this->coll->filter($p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,6 +595,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function forAll(Closure $p)
|
public function forAll(Closure $p)
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->forAll($p);
|
return $this->coll->forAll($p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,6 +605,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function partition(Closure $p)
|
public function partition(Closure $p)
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->partition($p);
|
return $this->coll->partition($p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,6 +615,7 @@ final class PersistentCollection implements Collection
|
|||||||
public function toArray()
|
public function toArray()
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->toArray();
|
return $this->coll->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -566,19 +627,28 @@ final class PersistentCollection implements Collection
|
|||||||
if ($this->initialized && $this->isEmpty()) {
|
if ($this->initialized && $this->isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$uow = $this->em->getUnitOfWork();
|
||||||
|
|
||||||
if ($this->association['type'] == ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) {
|
if ($this->association['type'] == ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) {
|
||||||
// we need to initialize here, as orphan removal acts like implicit cascadeRemove,
|
// we need to initialize here, as orphan removal acts like implicit cascadeRemove,
|
||||||
// hence for event listeners we need the objects in memory.
|
// hence for event listeners we need the objects in memory.
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
foreach ($this->coll as $element) {
|
foreach ($this->coll as $element) {
|
||||||
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
|
$uow->scheduleOrphanRemoval($element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->coll->clear();
|
$this->coll->clear();
|
||||||
|
|
||||||
$this->initialized = true; // direct call, {@link initialize()} is too expensive
|
$this->initialized = true; // direct call, {@link initialize()} is too expensive
|
||||||
|
|
||||||
if ($this->association['isOwningSide']) {
|
if ($this->association['isOwningSide']) {
|
||||||
$this->changed();
|
$this->changed();
|
||||||
$this->em->getUnitOfWork()->scheduleCollectionDeletion($this);
|
|
||||||
|
$uow->scheduleCollectionDeletion($this);
|
||||||
|
|
||||||
$this->takeSnapshot();
|
$this->takeSnapshot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -622,6 +692,7 @@ final class PersistentCollection implements Collection
|
|||||||
if ( ! isset($offset)) {
|
if ( ! isset($offset)) {
|
||||||
return $this->add($value);
|
return $this->add($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->set($offset, $value);
|
return $this->set($offset, $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,6 +727,8 @@ final class PersistentCollection implements Collection
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the wrapped Collection instance.
|
* Retrieves the wrapped Collection instance.
|
||||||
|
*
|
||||||
|
* @return Doctrine\Common\Collections\Collection
|
||||||
*/
|
*/
|
||||||
public function unwrap()
|
public function unwrap()
|
||||||
{
|
{
|
||||||
@ -671,20 +744,19 @@ final class PersistentCollection implements Collection
|
|||||||
*
|
*
|
||||||
* @param int $offset
|
* @param int $offset
|
||||||
* @param int $length
|
* @param int $length
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function slice($offset, $length = null)
|
public function slice($offset, $length = null)
|
||||||
{
|
{
|
||||||
if ( ! $this->initialized &&
|
if ( ! $this->initialized && ! $this->isDirty && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||||
! $this->isDirty &&
|
$persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
|
||||||
$this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
|
||||||
|
|
||||||
return $this->em->getUnitOfWork()
|
return $persister->slice($this, $offset, $length);
|
||||||
->getCollectionPersister($this->association)
|
|
||||||
->slice($this, $offset, $length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
return $this->coll->slice($offset, $length);
|
return $this->coll->slice($offset, $length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,6 +151,16 @@ abstract class AbstractCollectionPersister
|
|||||||
throw new \BadMethodCallException("Checking for existance of a key is not supported by this CollectionPersister.");
|
throw new \BadMethodCallException("Checking for existance of a key is not supported by this CollectionPersister.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function removeElement(PersistentCollection $coll, $element)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException("Removing an element is not supported by this CollectionPersister.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeKey(PersistentCollection $coll, $key)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException("Removing a key is not supported by this CollectionPersister.");
|
||||||
|
}
|
||||||
|
|
||||||
public function get(PersistentCollection $coll, $index)
|
public function get(PersistentCollection $coll, $index)
|
||||||
{
|
{
|
||||||
throw new \BadMethodCallException("Selecting a collection by index is not supported by this CollectionPersister.");
|
throw new \BadMethodCallException("Selecting a collection by index is not supported by this CollectionPersister.");
|
||||||
|
@ -27,8 +27,9 @@ use Doctrine\ORM\PersistentCollection,
|
|||||||
/**
|
/**
|
||||||
* Persister for many-to-many collections.
|
* Persister for many-to-many collections.
|
||||||
*
|
*
|
||||||
* @author Roman Borschel <roman@code-factory.org>
|
* @author Roman Borschel <roman@code-factory.org>
|
||||||
* @since 2.0
|
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||||
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
class ManyToManyPersister extends AbstractCollectionPersister
|
class ManyToManyPersister extends AbstractCollectionPersister
|
||||||
{
|
{
|
||||||
@ -79,8 +80,10 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
|||||||
$columns = $mapping['joinTableColumns'];
|
$columns = $mapping['joinTableColumns'];
|
||||||
$class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
|
$class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
|
||||||
|
|
||||||
return 'INSERT INTO ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform())
|
$joinTable = $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform());
|
||||||
. ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
|
|
||||||
|
return 'INSERT INTO ' . $joinTable . ' (' . implode(', ', $columns) . ')'
|
||||||
|
. ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,27 +121,21 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||||
if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) {
|
$isRelationToSource = isset($mapping['relationToSourceKeyColumns'][$joinTableColumn]);
|
||||||
if ($isComposite) {
|
|
||||||
if ($class1->containsForeignIdentifier) {
|
if ( ! $isComposite) {
|
||||||
$params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
|
$params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2);
|
||||||
} else {
|
|
||||||
$params[] = $identifier1[$class1->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
|
continue;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$params[] = array_pop($identifier1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($isComposite) {
|
|
||||||
if ($class2->containsForeignIdentifier) {
|
|
||||||
$params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
|
|
||||||
} else {
|
|
||||||
$params[] = $identifier2[$class2->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$params[] = array_pop($identifier2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($isRelationToSource) {
|
||||||
|
$params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $params;
|
return $params;
|
||||||
@ -151,19 +148,11 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
|||||||
*/
|
*/
|
||||||
protected function _getDeleteSQL(PersistentCollection $coll)
|
protected function _getDeleteSQL(PersistentCollection $coll)
|
||||||
{
|
{
|
||||||
$mapping = $coll->getMapping();
|
$class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
|
||||||
$class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
|
$mapping = $coll->getMapping();
|
||||||
$joinTable = $mapping['joinTable'];
|
|
||||||
$whereClause = '';
|
|
||||||
|
|
||||||
foreach ($mapping['relationToSourceKeyColumns'] as $relationColumn => $srcColumn) {
|
|
||||||
if ($whereClause !== '') $whereClause .= ' AND ';
|
|
||||||
|
|
||||||
$whereClause .= $relationColumn . ' = ?';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'DELETE FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform())
|
return 'DELETE FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform())
|
||||||
. ' WHERE ' . $whereClause;
|
. ' WHERE ' . implode(' = ? AND ', array_keys($mapping['relationToSourceKeyColumns'])) . ' = ?';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -175,18 +164,22 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
|||||||
*/
|
*/
|
||||||
protected function _getDeleteSQLParameters(PersistentCollection $coll)
|
protected function _getDeleteSQLParameters(PersistentCollection $coll)
|
||||||
{
|
{
|
||||||
$params = array();
|
|
||||||
$mapping = $coll->getMapping();
|
|
||||||
$identifier = $this->_uow->getEntityIdentifier($coll->getOwner());
|
$identifier = $this->_uow->getEntityIdentifier($coll->getOwner());
|
||||||
|
$mapping = $coll->getMapping();
|
||||||
|
$params = array();
|
||||||
|
|
||||||
if (count($mapping['relationToSourceKeyColumns']) > 1) {
|
// Optimization for single column identifier
|
||||||
$sourceClass = $this->_em->getClassMetadata(get_class($mapping->getOwner()));
|
if (count($mapping['relationToSourceKeyColumns']) === 1) {
|
||||||
|
$params[] = array_pop($identifier);
|
||||||
|
|
||||||
foreach ($mapping['relationToSourceKeyColumns'] as $relColumn => $srcColumn) {
|
return $params;
|
||||||
$params[] = $identifier[$sourceClass->fieldNames[$srcColumn]];
|
}
|
||||||
}
|
|
||||||
} else {
|
// Composite identifier
|
||||||
$params[] = array_pop($identifier);
|
$sourceClass = $this->_em->getClassMetadata(get_class($mapping->getOwner()));
|
||||||
|
|
||||||
|
foreach ($mapping['relationToSourceKeyColumns'] as $relColumn => $srcColumn) {
|
||||||
|
$params[] = $identifier[$sourceClass->fieldNames[$srcColumn]];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $params;
|
return $params;
|
||||||
@ -197,7 +190,6 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
|||||||
*/
|
*/
|
||||||
public function count(PersistentCollection $coll)
|
public function count(PersistentCollection $coll)
|
||||||
{
|
{
|
||||||
$params = array();
|
|
||||||
$mapping = $coll->getMapping();
|
$mapping = $coll->getMapping();
|
||||||
$class = $this->_em->getClassMetadata($mapping['sourceEntity']);
|
$class = $this->_em->getClassMetadata($mapping['sourceEntity']);
|
||||||
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
|
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
|
||||||
@ -209,25 +201,24 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
|||||||
$joinColumns = $mapping['relationToTargetKeyColumns'];
|
$joinColumns = $mapping['relationToTargetKeyColumns'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$whereClause = '';
|
$whereClauses = array();
|
||||||
|
$params = array();
|
||||||
|
|
||||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||||
if (isset($joinColumns[$joinTableColumn])) {
|
if ( ! isset($joinColumns[$joinTableColumn])) {
|
||||||
if ($whereClause !== '') {
|
continue;
|
||||||
$whereClause .= ' AND ';
|
|
||||||
}
|
|
||||||
|
|
||||||
$whereClause .= "$joinTableColumn = ?";
|
|
||||||
|
|
||||||
$params[] = ($class->containsForeignIdentifier)
|
|
||||||
? $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])]
|
|
||||||
: $id[$class->fieldNames[$joinColumns[$joinTableColumn]]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$whereClauses[] = $joinTableColumn . ' = ?';
|
||||||
|
|
||||||
|
$params[] = ($class->containsForeignIdentifier)
|
||||||
|
? $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])]
|
||||||
|
: $id[$class->fieldNames[$joinColumns[$joinTableColumn]]];
|
||||||
}
|
}
|
||||||
|
|
||||||
$sql = 'SELECT COUNT(*)'
|
$sql = 'SELECT COUNT(*)'
|
||||||
. ' FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform())
|
. ' FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform())
|
||||||
. ' WHERE ' . $whereClause;
|
. ' WHERE ' . implode(' AND ', $whereClauses);
|
||||||
|
|
||||||
return $this->_conn->fetchColumn($sql, $params);
|
return $this->_conn->fetchColumn($sql, $params);
|
||||||
}
|
}
|
||||||
@ -248,6 +239,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
|||||||
/**
|
/**
|
||||||
* @param PersistentCollection $coll
|
* @param PersistentCollection $coll
|
||||||
* @param object $element
|
* @param object $element
|
||||||
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function contains(PersistentCollection $coll, $element)
|
public function contains(PersistentCollection $coll, $element)
|
||||||
{
|
{
|
||||||
@ -258,7 +250,42 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$params = array();
|
list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($coll, $element);
|
||||||
|
|
||||||
|
$sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
|
||||||
|
|
||||||
|
return (bool) $this->_conn->fetchColumn($sql, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
|
||||||
|
list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($coll, $element);
|
||||||
|
|
||||||
|
$sql = 'DELETE FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
|
||||||
|
|
||||||
|
return (bool) $this->_conn->executeUpdate($sql, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Doctrine\ORM\PersistentCollection $coll
|
||||||
|
* @param object $element
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getJoinTableRestrictions(PersistentCollection $coll, $element)
|
||||||
|
{
|
||||||
|
$uow = $this->_em->getUnitOfWork();
|
||||||
$mapping = $coll->getMapping();
|
$mapping = $coll->getMapping();
|
||||||
|
|
||||||
if ( ! $mapping['isOwningSide']) {
|
if ( ! $mapping['isOwningSide']) {
|
||||||
@ -275,36 +302,26 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
|||||||
$targetId = $uow->getEntityIdentifier($element);
|
$targetId = $uow->getEntityIdentifier($element);
|
||||||
}
|
}
|
||||||
|
|
||||||
$whereClause = '';
|
$quotedJoinTable = $sourceClass->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform());
|
||||||
|
$whereClauses = array();
|
||||||
|
$params = array();
|
||||||
|
|
||||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||||
|
$whereClauses[] = $joinTableColumn . ' = ?';
|
||||||
|
|
||||||
if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
|
if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
|
||||||
if ($whereClause !== '') {
|
|
||||||
$whereClause .= ' AND ';
|
|
||||||
}
|
|
||||||
|
|
||||||
$whereClause .= $joinTableColumn . ' = ?';
|
|
||||||
|
|
||||||
$params[] = ($targetClass->containsForeignIdentifier)
|
$params[] = ($targetClass->containsForeignIdentifier)
|
||||||
? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]
|
? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]
|
||||||
: $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
|
: $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
|
||||||
} else if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) {
|
continue;
|
||||||
if ($whereClause !== '') {
|
|
||||||
$whereClause .= ' AND ';
|
|
||||||
}
|
|
||||||
|
|
||||||
$whereClause .= $joinTableColumn . ' = ?';
|
|
||||||
|
|
||||||
$params[] = ($sourceClass->containsForeignIdentifier)
|
|
||||||
? $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]
|
|
||||||
: $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// relationToSourceKeyColumns
|
||||||
|
$params[] = ($sourceClass->containsForeignIdentifier)
|
||||||
|
? $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]
|
||||||
|
: $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
|
||||||
}
|
}
|
||||||
|
|
||||||
$sql = 'SELECT 1'
|
return array($quotedJoinTable, $whereClauses, $params);
|
||||||
. ' FROM ' . $sourceClass->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform())
|
|
||||||
. ' WHERE ' . $whereClause;
|
|
||||||
|
|
||||||
return (bool) $this->_conn->fetchColumn($sql, $params);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -27,13 +27,9 @@ use Doctrine\ORM\PersistentCollection,
|
|||||||
/**
|
/**
|
||||||
* Persister for one-to-many collections.
|
* Persister for one-to-many collections.
|
||||||
*
|
*
|
||||||
* IMPORTANT:
|
* @author Roman Borschel <roman@code-factory.org>
|
||||||
* This persister is only used for uni-directional one-to-many mappings on a foreign key
|
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||||
* (which are not yet supported). So currently this persister is not used.
|
* @since 2.0
|
||||||
*
|
|
||||||
* @since 2.0
|
|
||||||
* @author Roman Borschel <roman@code-factory.org>
|
|
||||||
* @todo Remove
|
|
||||||
*/
|
*/
|
||||||
class OneToManyPersister extends AbstractCollectionPersister
|
class OneToManyPersister extends AbstractCollectionPersister
|
||||||
{
|
{
|
||||||
@ -48,24 +44,19 @@ class OneToManyPersister extends AbstractCollectionPersister
|
|||||||
protected function _getDeleteRowSQL(PersistentCollection $coll)
|
protected function _getDeleteRowSQL(PersistentCollection $coll)
|
||||||
{
|
{
|
||||||
$mapping = $coll->getMapping();
|
$mapping = $coll->getMapping();
|
||||||
$targetClass = $this->_em->getClassMetadata($mapping->getTargetEntityName());
|
$class = $this->_em->getClassMetadata($mapping['targetEntity']);
|
||||||
$table = $targetClass->getTableName();
|
|
||||||
|
|
||||||
$ownerMapping = $targetClass->getAssociationMapping($mapping['mappedBy']);
|
return 'DELETE FROM ' . $class->getQuotedTableName($this->_conn->getDatabasePlatform())
|
||||||
|
. ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?';
|
||||||
|
}
|
||||||
|
|
||||||
$setClause = '';
|
/**
|
||||||
foreach ($ownerMapping->sourceToTargetKeyColumns as $sourceCol => $targetCol) {
|
* {@inheritdoc}
|
||||||
if ($setClause != '') $setClause .= ', ';
|
*
|
||||||
$setClause .= "$sourceCol = NULL";
|
*/
|
||||||
}
|
protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element)
|
||||||
|
{
|
||||||
$whereClause = '';
|
return array_values($this->_uow->getEntityIdentifier($element));
|
||||||
foreach ($targetClass->getIdentifierColumnNames() as $idColumn) {
|
|
||||||
if ($whereClause != '') $whereClause .= ' AND ';
|
|
||||||
$whereClause .= "$idColumn = ?";
|
|
||||||
}
|
|
||||||
|
|
||||||
return array("UPDATE $table SET $setClause WHERE $whereClause", $this->_uow->getEntityIdentifier($element));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function _getInsertRowSQL(PersistentCollection $coll)
|
protected function _getInsertRowSQL(PersistentCollection $coll)
|
||||||
@ -73,6 +64,16 @@ class OneToManyPersister extends AbstractCollectionPersister
|
|||||||
return "UPDATE xxx SET foreign_key = yyy WHERE foreign_key = zzz";
|
return "UPDATE xxx SET foreign_key = yyy WHERE foreign_key = zzz";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
{}
|
||||||
|
|
||||||
/* Not used for OneToManyPersister */
|
/* Not used for OneToManyPersister */
|
||||||
protected function _getUpdateRowSQL(PersistentCollection $coll)
|
protected function _getUpdateRowSQL(PersistentCollection $coll)
|
||||||
{
|
{
|
||||||
@ -98,52 +99,31 @@ class OneToManyPersister extends AbstractCollectionPersister
|
|||||||
protected function _getDeleteSQLParameters(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)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function count(PersistentCollection $coll)
|
public function count(PersistentCollection $coll)
|
||||||
{
|
{
|
||||||
$mapping = $coll->getMapping();
|
$mapping = $coll->getMapping();
|
||||||
$targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
|
$targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
|
||||||
$sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
|
$sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
|
||||||
|
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
|
||||||
|
|
||||||
$params = array();
|
$whereClauses = array();
|
||||||
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
|
$params = array();
|
||||||
|
|
||||||
$where = '';
|
|
||||||
foreach ($targetClass->associationMappings[$mapping['mappedBy']]['joinColumns'] AS $joinColumn) {
|
foreach ($targetClass->associationMappings[$mapping['mappedBy']]['joinColumns'] AS $joinColumn) {
|
||||||
if ($where != '') {
|
$whereClauses[] = $joinColumn['name'] . ' = ?';
|
||||||
$where .= ' AND ';
|
|
||||||
}
|
$params[] = ($targetClass->containsForeignIdentifier)
|
||||||
$where .= $joinColumn['name'] . " = ?";
|
? $id[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])]
|
||||||
if ($targetClass->containsForeignIdentifier) {
|
: $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]];
|
||||||
$params[] = $id[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])];
|
|
||||||
} else {
|
|
||||||
$params[] = $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$sql = "SELECT count(*) FROM " . $targetClass->getQuotedTableName($this->_conn->getDatabasePlatform()) . " WHERE " . $where;
|
$sql = 'SELECT count(*)'
|
||||||
|
. ' FROM ' . $targetClass->getQuotedTableName($this->_conn->getDatabasePlatform())
|
||||||
|
. ' WHERE ' . implode(' AND ', $whereClauses);
|
||||||
|
|
||||||
return $this->_conn->fetchColumn($sql, $params);
|
return $this->_conn->fetchColumn($sql, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,19 +135,45 @@ class OneToManyPersister extends AbstractCollectionPersister
|
|||||||
*/
|
*/
|
||||||
public function slice(PersistentCollection $coll, $offset, $length = null)
|
public function slice(PersistentCollection $coll, $offset, $length = null)
|
||||||
{
|
{
|
||||||
$mapping = $coll->getMapping();
|
$mapping = $coll->getMapping();
|
||||||
return $this->_em->getUnitOfWork()
|
$uow = $this->_em->getUnitOfWork();
|
||||||
->getEntityPersister($mapping['targetEntity'])
|
$persister = $uow->getEntityPersister($mapping['targetEntity']);
|
||||||
->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length);
|
|
||||||
|
return $persister->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param PersistentCollection $coll
|
* @param PersistentCollection $coll
|
||||||
* @param object $element
|
* @param object $element
|
||||||
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function contains(PersistentCollection $coll, $element)
|
public function contains(PersistentCollection $coll, $element)
|
||||||
{
|
{
|
||||||
$mapping = $coll->getMapping();
|
$mapping = $coll->getMapping();
|
||||||
|
$uow = $this->_em->getUnitOfWork();
|
||||||
|
|
||||||
|
// shortcut for new entities
|
||||||
|
if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param PersistentCollection $coll
|
||||||
|
* @param object $element
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function removeElement(PersistentCollection $coll, $element)
|
||||||
|
{
|
||||||
$uow = $this->_em->getUnitOfWork();
|
$uow = $this->_em->getUnitOfWork();
|
||||||
|
|
||||||
// shortcut for new entities
|
// shortcut for new entities
|
||||||
@ -175,11 +181,11 @@ class OneToManyPersister extends AbstractCollectionPersister
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only works with single id identifier entities. Will throw an exception in Entity Persisters
|
$mapping = $coll->getMapping();
|
||||||
// if that is not the case for the 'mappedBy' field.
|
$class = $this->_em->getClassMetadata($mapping['targetEntity']);
|
||||||
$id = current( $uow->getEntityIdentifier($coll->getOwner()) );
|
$sql = 'DELETE FROM ' . $class->getQuotedTableName($this->_conn->getDatabasePlatform())
|
||||||
|
. ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?';
|
||||||
|
|
||||||
return $uow->getEntityPersister($mapping['targetEntity'])
|
return (bool) $this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element));
|
||||||
->exists($element, array($mapping['mappedBy'] => $id));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1790,6 +1790,7 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
public function detach($entity)
|
public function detach($entity)
|
||||||
{
|
{
|
||||||
$visited = array();
|
$visited = array();
|
||||||
|
|
||||||
$this->doDetach($entity, $visited);
|
$this->doDetach($entity, $visited);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1803,6 +1804,7 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
private function doDetach($entity, array &$visited, $noCascade = false)
|
private function doDetach($entity, array &$visited, $noCascade = false)
|
||||||
{
|
{
|
||||||
$oid = spl_object_hash($entity);
|
$oid = spl_object_hash($entity);
|
||||||
|
|
||||||
if (isset($visited[$oid])) {
|
if (isset($visited[$oid])) {
|
||||||
return; // Prevent infinite recursion
|
return; // Prevent infinite recursion
|
||||||
}
|
}
|
||||||
@ -1814,16 +1816,22 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
if ($this->isInIdentityMap($entity)) {
|
if ($this->isInIdentityMap($entity)) {
|
||||||
$this->removeFromIdentityMap($entity);
|
$this->removeFromIdentityMap($entity);
|
||||||
}
|
}
|
||||||
unset($this->entityInsertions[$oid], $this->entityUpdates[$oid],
|
|
||||||
$this->entityDeletions[$oid], $this->entityIdentifiers[$oid],
|
unset(
|
||||||
$this->entityStates[$oid], $this->originalEntityData[$oid]);
|
$this->entityInsertions[$oid],
|
||||||
|
$this->entityUpdates[$oid],
|
||||||
|
$this->entityDeletions[$oid],
|
||||||
|
$this->entityIdentifiers[$oid],
|
||||||
|
$this->entityStates[$oid],
|
||||||
|
$this->originalEntityData[$oid]
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case self::STATE_NEW:
|
case self::STATE_NEW:
|
||||||
case self::STATE_DETACHED:
|
case self::STATE_DETACHED:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$noCascade) {
|
if ( ! $noCascade) {
|
||||||
$this->cascadeDetach($entity, $visited);
|
$this->cascadeDetach($entity, $visited);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1838,6 +1846,7 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
public function refresh($entity)
|
public function refresh($entity)
|
||||||
{
|
{
|
||||||
$visited = array();
|
$visited = array();
|
||||||
|
|
||||||
$this->doRefresh($entity, $visited);
|
$this->doRefresh($entity, $visited);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1851,6 +1860,7 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
private function doRefresh($entity, array &$visited)
|
private function doRefresh($entity, array &$visited)
|
||||||
{
|
{
|
||||||
$oid = spl_object_hash($entity);
|
$oid = spl_object_hash($entity);
|
||||||
|
|
||||||
if (isset($visited[$oid])) {
|
if (isset($visited[$oid])) {
|
||||||
return; // Prevent infinite recursion
|
return; // Prevent infinite recursion
|
||||||
}
|
}
|
||||||
@ -1858,15 +1868,16 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
$visited[$oid] = $entity; // mark visited
|
$visited[$oid] = $entity; // mark visited
|
||||||
|
|
||||||
$class = $this->em->getClassMetadata(get_class($entity));
|
$class = $this->em->getClassMetadata(get_class($entity));
|
||||||
if ($this->getEntityState($entity) == self::STATE_MANAGED) {
|
|
||||||
$this->getEntityPersister($class->name)->refresh(
|
if ($this->getEntityState($entity) !== self::STATE_MANAGED) {
|
||||||
array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
|
|
||||||
$entity
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
throw new InvalidArgumentException("Entity is not MANAGED.");
|
throw new InvalidArgumentException("Entity is not MANAGED.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->getEntityPersister($class->name)->refresh(
|
||||||
|
array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
|
||||||
|
$entity
|
||||||
|
);
|
||||||
|
|
||||||
$this->cascadeRefresh($entity, $visited);
|
$this->cascadeRefresh($entity, $visited);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1879,21 +1890,33 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
private function cascadeRefresh($entity, array &$visited)
|
private function cascadeRefresh($entity, array &$visited)
|
||||||
{
|
{
|
||||||
$class = $this->em->getClassMetadata(get_class($entity));
|
$class = $this->em->getClassMetadata(get_class($entity));
|
||||||
|
|
||||||
foreach ($class->associationMappings as $assoc) {
|
foreach ($class->associationMappings as $assoc) {
|
||||||
if ( ! $assoc['isCascadeRefresh']) {
|
if ( ! $assoc['isCascadeRefresh']) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
|
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
|
||||||
if ($relatedEntities instanceof Collection) {
|
|
||||||
if ($relatedEntities instanceof PersistentCollection) {
|
switch (true) {
|
||||||
|
case ($relatedEntities instanceof PersistentCollection):
|
||||||
// Unwrap so that foreach() does not initialize
|
// Unwrap so that foreach() does not initialize
|
||||||
$relatedEntities = $relatedEntities->unwrap();
|
$relatedEntities = $relatedEntities->unwrap();
|
||||||
}
|
// break; is commented intentionally!
|
||||||
foreach ($relatedEntities as $relatedEntity) {
|
|
||||||
$this->doRefresh($relatedEntity, $visited);
|
case ($relatedEntities instanceof Collection):
|
||||||
}
|
case (is_array($relatedEntities)):
|
||||||
} else if ($relatedEntities !== null) {
|
foreach ($relatedEntities as $relatedEntity) {
|
||||||
$this->doRefresh($relatedEntities, $visited);
|
$this->doRefresh($relatedEntity, $visited);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ($relatedEntities !== null):
|
||||||
|
$this->doRefresh($relatedEntities, $visited);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1907,21 +1930,33 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
private function cascadeDetach($entity, array &$visited)
|
private function cascadeDetach($entity, array &$visited)
|
||||||
{
|
{
|
||||||
$class = $this->em->getClassMetadata(get_class($entity));
|
$class = $this->em->getClassMetadata(get_class($entity));
|
||||||
|
|
||||||
foreach ($class->associationMappings as $assoc) {
|
foreach ($class->associationMappings as $assoc) {
|
||||||
if ( ! $assoc['isCascadeDetach']) {
|
if ( ! $assoc['isCascadeDetach']) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
|
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
|
||||||
if ($relatedEntities instanceof Collection) {
|
|
||||||
if ($relatedEntities instanceof PersistentCollection) {
|
switch (true) {
|
||||||
|
case ($relatedEntities instanceof PersistentCollection):
|
||||||
// Unwrap so that foreach() does not initialize
|
// Unwrap so that foreach() does not initialize
|
||||||
$relatedEntities = $relatedEntities->unwrap();
|
$relatedEntities = $relatedEntities->unwrap();
|
||||||
}
|
// break; is commented intentionally!
|
||||||
foreach ($relatedEntities as $relatedEntity) {
|
|
||||||
$this->doDetach($relatedEntity, $visited);
|
case ($relatedEntities instanceof Collection):
|
||||||
}
|
case (is_array($relatedEntities)):
|
||||||
} else if ($relatedEntities !== null) {
|
foreach ($relatedEntities as $relatedEntity) {
|
||||||
$this->doDetach($relatedEntities, $visited);
|
$this->doDetach($relatedEntity, $visited);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ($relatedEntities !== null):
|
||||||
|
$this->doDetach($relatedEntities, $visited);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1969,22 +2004,33 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
private function cascadePersist($entity, array &$visited)
|
private function cascadePersist($entity, array &$visited)
|
||||||
{
|
{
|
||||||
$class = $this->em->getClassMetadata(get_class($entity));
|
$class = $this->em->getClassMetadata(get_class($entity));
|
||||||
|
|
||||||
foreach ($class->associationMappings as $assoc) {
|
foreach ($class->associationMappings as $assoc) {
|
||||||
if ( ! $assoc['isCascadePersist']) {
|
if ( ! $assoc['isCascadePersist']) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
|
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
|
||||||
if (($relatedEntities instanceof Collection || is_array($relatedEntities))) {
|
|
||||||
if ($relatedEntities instanceof PersistentCollection) {
|
switch (true) {
|
||||||
|
case ($relatedEntities instanceof PersistentCollection):
|
||||||
// Unwrap so that foreach() does not initialize
|
// Unwrap so that foreach() does not initialize
|
||||||
$relatedEntities = $relatedEntities->unwrap();
|
$relatedEntities = $relatedEntities->unwrap();
|
||||||
}
|
// break; is commented intentionally!
|
||||||
foreach ($relatedEntities as $relatedEntity) {
|
|
||||||
$this->doPersist($relatedEntity, $visited);
|
case ($relatedEntities instanceof Collection):
|
||||||
}
|
case (is_array($relatedEntities)):
|
||||||
} else if ($relatedEntities !== null) {
|
foreach ($relatedEntities as $relatedEntity) {
|
||||||
$this->doPersist($relatedEntities, $visited);
|
$this->doPersist($relatedEntity, $visited);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ($relatedEntities !== null):
|
||||||
|
$this->doPersist($relatedEntities, $visited);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2010,13 +2056,21 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
|
|
||||||
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
|
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
|
||||||
|
|
||||||
if ($relatedEntities instanceof Collection || is_array($relatedEntities)) {
|
switch (true) {
|
||||||
// If its a PersistentCollection initialization is intended! No unwrap!
|
case ($relatedEntities instanceof Collection):
|
||||||
foreach ($relatedEntities as $relatedEntity) {
|
case (is_array($relatedEntities)):
|
||||||
$this->doRemove($relatedEntity, $visited);
|
// If its a PersistentCollection initialization is intended! No unwrap!
|
||||||
}
|
foreach ($relatedEntities as $relatedEntity) {
|
||||||
} else if ($relatedEntities !== null) {
|
$this->doRemove($relatedEntity, $visited);
|
||||||
$this->doRemove($relatedEntities, $visited);
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ($relatedEntities !== null):
|
||||||
|
$this->doRemove($relatedEntities, $visited);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2037,29 +2091,40 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
$entityName = get_class($entity);
|
$entityName = get_class($entity);
|
||||||
$class = $this->em->getClassMetadata($entityName);
|
$class = $this->em->getClassMetadata($entityName);
|
||||||
|
|
||||||
if ($lockMode == \Doctrine\DBAL\LockMode::OPTIMISTIC) {
|
switch ($lockMode) {
|
||||||
if (!$class->isVersioned) {
|
case \Doctrine\DBAL\LockMode::OPTIMISTIC;
|
||||||
throw OptimisticLockException::notVersioned($entityName);
|
if ( ! $class->isVersioned) {
|
||||||
}
|
throw OptimisticLockException::notVersioned($entityName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($lockVersion === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ($lockVersion != null) {
|
|
||||||
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
|
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
|
||||||
|
|
||||||
if ($entityVersion != $lockVersion) {
|
if ($entityVersion != $lockVersion) {
|
||||||
throw OptimisticLockException::lockFailedVersionMissmatch($entity, $lockVersion, $entityVersion);
|
throw OptimisticLockException::lockFailedVersionMissmatch($entity, $lockVersion, $entityVersion);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if (in_array($lockMode, array(\Doctrine\DBAL\LockMode::PESSIMISTIC_READ, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE))) {
|
|
||||||
|
|
||||||
if (!$this->em->getConnection()->isTransactionActive()) {
|
break;
|
||||||
throw TransactionRequiredException::transactionRequired();
|
|
||||||
}
|
|
||||||
|
|
||||||
$oid = spl_object_hash($entity);
|
case \Doctrine\DBAL\LockMode::PESSIMISTIC_READ:
|
||||||
|
case \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE:
|
||||||
|
if (!$this->em->getConnection()->isTransactionActive()) {
|
||||||
|
throw TransactionRequiredException::transactionRequired();
|
||||||
|
}
|
||||||
|
|
||||||
$this->getEntityPersister($class->name)->lock(
|
$oid = spl_object_hash($entity);
|
||||||
array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
|
|
||||||
$lockMode
|
$this->getEntityPersister($class->name)->lock(
|
||||||
);
|
array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
|
||||||
|
$lockMode
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2187,6 +2252,7 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
|
|
||||||
if ($class->isIdentifierComposite) {
|
if ($class->isIdentifierComposite) {
|
||||||
$id = array();
|
$id = array();
|
||||||
|
|
||||||
foreach ($class->identifier as $fieldName) {
|
foreach ($class->identifier as $fieldName) {
|
||||||
if (isset($class->associationMappings[$fieldName])) {
|
if (isset($class->associationMappings[$fieldName])) {
|
||||||
$id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']];
|
$id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']];
|
||||||
@ -2194,28 +2260,26 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
$id[$fieldName] = $data[$fieldName];
|
$id[$fieldName] = $data[$fieldName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$idHash = implode(' ', $id);
|
$idHash = implode(' ', $id);
|
||||||
} else {
|
} else {
|
||||||
if (isset($class->associationMappings[$class->identifier[0]])) {
|
if (isset($class->associationMappings[$class->identifier[0]])) {
|
||||||
$idHash = $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']];
|
$idHash = $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']];
|
||||||
} else {
|
} else {
|
||||||
/*echo $className;
|
|
||||||
\Doctrine\Common\Util\Debug::dump($data);
|
|
||||||
\Doctrine\Common\Util\Debug::dump($class->identifier);
|
|
||||||
ob_end_flush();
|
|
||||||
ob_start();*/
|
|
||||||
|
|
||||||
$idHash = $data[$class->identifier[0]];
|
$idHash = $data[$class->identifier[0]];
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = array($class->identifier[0] => $idHash);
|
$id = array($class->identifier[0] => $idHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->identityMap[$class->rootEntityName][$idHash])) {
|
if (isset($this->identityMap[$class->rootEntityName][$idHash])) {
|
||||||
$entity = $this->identityMap[$class->rootEntityName][$idHash];
|
$entity = $this->identityMap[$class->rootEntityName][$idHash];
|
||||||
$oid = spl_object_hash($entity);
|
$oid = spl_object_hash($entity);
|
||||||
|
|
||||||
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
|
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
|
||||||
$entity->__isInitialized__ = true;
|
$entity->__isInitialized__ = true;
|
||||||
$overrideLocalValues = true;
|
$overrideLocalValues = true;
|
||||||
|
|
||||||
if ($entity instanceof NotifyPropertyChanged) {
|
if ($entity instanceof NotifyPropertyChanged) {
|
||||||
$entity->addPropertyChangedListener($this);
|
$entity->addPropertyChangedListener($this);
|
||||||
}
|
}
|
||||||
@ -2239,156 +2303,175 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
} else {
|
} else {
|
||||||
$entity = $this->newInstance($class);
|
$entity = $this->newInstance($class);
|
||||||
$oid = spl_object_hash($entity);
|
$oid = spl_object_hash($entity);
|
||||||
|
|
||||||
$this->entityIdentifiers[$oid] = $id;
|
$this->entityIdentifiers[$oid] = $id;
|
||||||
$this->entityStates[$oid] = self::STATE_MANAGED;
|
$this->entityStates[$oid] = self::STATE_MANAGED;
|
||||||
$this->originalEntityData[$oid] = $data;
|
$this->originalEntityData[$oid] = $data;
|
||||||
$this->identityMap[$class->rootEntityName][$idHash] = $entity;
|
$this->identityMap[$class->rootEntityName][$idHash] = $entity;
|
||||||
|
|
||||||
if ($entity instanceof NotifyPropertyChanged) {
|
if ($entity instanceof NotifyPropertyChanged) {
|
||||||
$entity->addPropertyChangedListener($this);
|
$entity->addPropertyChangedListener($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
$overrideLocalValues = true;
|
$overrideLocalValues = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($overrideLocalValues) {
|
if ( ! $overrideLocalValues) {
|
||||||
foreach ($data as $field => $value) {
|
return $entity;
|
||||||
if (isset($class->fieldMappings[$field])) {
|
}
|
||||||
$class->reflFields[$field]->setValue($entity, $value);
|
|
||||||
}
|
foreach ($data as $field => $value) {
|
||||||
|
if (isset($class->fieldMappings[$field])) {
|
||||||
|
$class->reflFields[$field]->setValue($entity, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loading the entity right here, if its in the eager loading map get rid of it there.
|
||||||
|
unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
|
||||||
|
|
||||||
|
if (isset($this->eagerLoadingEntities[$class->rootEntityName]) && ! $this->eagerLoadingEntities[$class->rootEntityName]) {
|
||||||
|
unset($this->eagerLoadingEntities[$class->rootEntityName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Properly initialize any unfetched associations, if partial objects are not allowed.
|
||||||
|
if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
|
||||||
|
return $entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($class->associationMappings as $field => $assoc) {
|
||||||
|
// Check if the association is not among the fetch-joined associations already.
|
||||||
|
if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loading the entity right here, if its in the eager loading map get rid of it there.
|
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
|
||||||
unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
|
|
||||||
|
|
||||||
if (isset($this->eagerLoadingEntities[$class->rootEntityName]) &&
|
switch (true) {
|
||||||
! $this->eagerLoadingEntities[$class->rootEntityName]) {
|
case ($assoc['type'] & ClassMetadata::TO_ONE):
|
||||||
unset($this->eagerLoadingEntities[$class->rootEntityName]);
|
if ( ! $assoc['isOwningSide']) {
|
||||||
}
|
// Inverse side of x-to-one can never be lazy
|
||||||
|
$class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity));
|
||||||
|
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
$associatedId = array();
|
||||||
|
|
||||||
|
// TODO: Is this even computed right in all cases of composite keys?
|
||||||
|
foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
|
||||||
|
$joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
|
||||||
|
|
||||||
|
if ($joinColumnValue !== null) {
|
||||||
|
if ($targetClass->containsForeignIdentifier) {
|
||||||
|
$associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
|
||||||
|
} else {
|
||||||
|
$associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $associatedId) {
|
||||||
|
// Foreign key is NULL
|
||||||
|
$class->reflFields[$field]->setValue($entity, null);
|
||||||
|
$this->originalEntityData[$oid][$field] = null;
|
||||||
|
|
||||||
// Properly initialize any unfetched associations, if partial objects are not allowed.
|
|
||||||
if ( ! isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
|
|
||||||
foreach ($class->associationMappings as $field => $assoc) {
|
|
||||||
// Check if the association is not among the fetch-joined associations already.
|
|
||||||
if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
|
if ( ! isset($hints['fetchMode'][$class->name][$field])) {
|
||||||
|
$hints['fetchMode'][$class->name][$field] = $assoc['fetch'];
|
||||||
if ($assoc['type'] & ClassMetadata::TO_ONE) {
|
|
||||||
if ($assoc['isOwningSide']) {
|
|
||||||
$associatedId = array();
|
|
||||||
// TODO: Is this even computed right in all cases of composite keys?
|
|
||||||
foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
|
|
||||||
$joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
|
|
||||||
if ($joinColumnValue !== null) {
|
|
||||||
if ($targetClass->containsForeignIdentifier) {
|
|
||||||
$associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
|
|
||||||
} else {
|
|
||||||
$associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( ! $associatedId) {
|
|
||||||
// Foreign key is NULL
|
|
||||||
$class->reflFields[$field]->setValue($entity, null);
|
|
||||||
$this->originalEntityData[$oid][$field] = null;
|
|
||||||
} else {
|
|
||||||
if (!isset($hints['fetchMode'][$class->name][$field])) {
|
|
||||||
$hints['fetchMode'][$class->name][$field] = $assoc['fetch'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Foreign key is set
|
|
||||||
// Check identity map first
|
|
||||||
// FIXME: Can break easily with composite keys if join column values are in
|
|
||||||
// wrong order. The correct order is the one in ClassMetadata#identifier.
|
|
||||||
$relatedIdHash = implode(' ', $associatedId);
|
|
||||||
if (isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash])) {
|
|
||||||
$newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash];
|
|
||||||
|
|
||||||
// if this is an uninitialized proxy, we are deferring eager loads,
|
|
||||||
// this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
|
|
||||||
// then we cann append this entity for eager loading!
|
|
||||||
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER &&
|
|
||||||
isset($hints['deferEagerLoad']) &&
|
|
||||||
!$targetClass->isIdentifierComposite &&
|
|
||||||
$newValue instanceof Proxy &&
|
|
||||||
$newValue->__isInitialized__ === false) {
|
|
||||||
|
|
||||||
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($targetClass->subClasses) {
|
|
||||||
// If it might be a subtype, it can not be lazy. There isn't even
|
|
||||||
// a way to solve this with deferred eager loading, which means putting
|
|
||||||
// an entity with subclasses at a *-to-one location is really bad! (performance-wise)
|
|
||||||
$newValue = $this->getEntityPersister($assoc['targetEntity'])
|
|
||||||
->loadOneToOneEntity($assoc, $entity, $associatedId);
|
|
||||||
} else {
|
|
||||||
// Deferred eager load only works for single identifier classes
|
|
||||||
|
|
||||||
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER) {
|
|
||||||
if (isset($hints['deferEagerLoad']) && !$targetClass->isIdentifierComposite) {
|
|
||||||
// TODO: Is there a faster approach?
|
|
||||||
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
|
|
||||||
|
|
||||||
$newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
|
|
||||||
} else {
|
|
||||||
// TODO: This is very imperformant, ignore it?
|
|
||||||
$newValue = $this->em->find($assoc['targetEntity'], $associatedId);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
|
|
||||||
}
|
|
||||||
// PERF: Inlined & optimized code from UnitOfWork#registerManaged()
|
|
||||||
$newValueOid = spl_object_hash($newValue);
|
|
||||||
$this->entityIdentifiers[$newValueOid] = $associatedId;
|
|
||||||
$this->identityMap[$targetClass->rootEntityName][$relatedIdHash] = $newValue;
|
|
||||||
$this->entityStates[$newValueOid] = self::STATE_MANAGED;
|
|
||||||
// make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->originalEntityData[$oid][$field] = $newValue;
|
|
||||||
$class->reflFields[$field]->setValue($entity, $newValue);
|
|
||||||
|
|
||||||
if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
|
|
||||||
$inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
|
|
||||||
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Inverse side of x-to-one can never be lazy
|
|
||||||
$class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])
|
|
||||||
->loadOneToOneEntity($assoc, $entity));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Inject collection
|
|
||||||
$pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection);
|
|
||||||
$pColl->setOwner($entity, $assoc);
|
|
||||||
|
|
||||||
$reflField = $class->reflFields[$field];
|
|
||||||
$reflField->setValue($entity, $pColl);
|
|
||||||
|
|
||||||
if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
|
|
||||||
$this->loadCollection($pColl);
|
|
||||||
$pColl->takeSnapshot();
|
|
||||||
} else {
|
|
||||||
$pColl->setInitialized(false);
|
|
||||||
}
|
|
||||||
$this->originalEntityData[$oid][$field] = $pColl;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Foreign key is set
|
||||||
|
// Check identity map first
|
||||||
|
// FIXME: Can break easily with composite keys if join column values are in
|
||||||
|
// wrong order. The correct order is the one in ClassMetadata#identifier.
|
||||||
|
$relatedIdHash = implode(' ', $associatedId);
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case (isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash])):
|
||||||
|
$newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash];
|
||||||
|
|
||||||
|
// if this is an uninitialized proxy, we are deferring eager loads,
|
||||||
|
// this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
|
||||||
|
// then we cann append this entity for eager loading!
|
||||||
|
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER &&
|
||||||
|
isset($hints['deferEagerLoad']) &&
|
||||||
|
!$targetClass->isIdentifierComposite &&
|
||||||
|
$newValue instanceof Proxy &&
|
||||||
|
$newValue->__isInitialized__ === false) {
|
||||||
|
|
||||||
|
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ($targetClass->subClasses):
|
||||||
|
// If it might be a subtype, it can not be lazy. There isn't even
|
||||||
|
// a way to solve this with deferred eager loading, which means putting
|
||||||
|
// an entity with subclasses at a *-to-one location is really bad! (performance-wise)
|
||||||
|
$newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
switch (true) {
|
||||||
|
// We are negating the condition here. Other cases will assume it is valid!
|
||||||
|
case ($hints['fetchMode'][$class->name][$field] !== ClassMetadata::FETCH_EAGER):
|
||||||
|
$newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Deferred eager load only works for single identifier classes
|
||||||
|
case (isset($hints['deferEagerLoad']) && ! $targetClass->isIdentifierComposite):
|
||||||
|
// TODO: Is there a faster approach?
|
||||||
|
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
|
||||||
|
|
||||||
|
$newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// TODO: This is very imperformant, ignore it?
|
||||||
|
$newValue = $this->em->find($assoc['targetEntity'], $associatedId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PERF: Inlined & optimized code from UnitOfWork#registerManaged()
|
||||||
|
$newValueOid = spl_object_hash($newValue);
|
||||||
|
$this->entityIdentifiers[$newValueOid] = $associatedId;
|
||||||
|
$this->identityMap[$targetClass->rootEntityName][$relatedIdHash] = $newValue;
|
||||||
|
$this->entityStates[$newValueOid] = self::STATE_MANAGED;
|
||||||
|
// make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->originalEntityData[$oid][$field] = $newValue;
|
||||||
|
$class->reflFields[$field]->setValue($entity, $newValue);
|
||||||
|
|
||||||
|
if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
|
||||||
|
$inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
|
||||||
|
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Inject collection
|
||||||
|
$pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection);
|
||||||
|
$pColl->setOwner($entity, $assoc);
|
||||||
|
$pColl->setInitialized(false);
|
||||||
|
|
||||||
|
$reflField = $class->reflFields[$field];
|
||||||
|
$reflField->setValue($entity, $pColl);
|
||||||
|
|
||||||
|
if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
|
||||||
|
$this->loadCollection($pColl);
|
||||||
|
$pColl->takeSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->originalEntityData[$oid][$field] = $pColl;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
|
|
||||||
if (isset($class->lifecycleCallbacks[Events::postLoad])) {
|
|
||||||
$class->invokeLifecycleCallbacks(Events::postLoad, $entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->evm->hasListeners(Events::postLoad)) {
|
|
||||||
$this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->em));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $entity;
|
return $entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
|||||||
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup');
|
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup');
|
||||||
$class->associationMappings['users']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
|
$class->associationMappings['users']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
|
||||||
|
|
||||||
|
|
||||||
$this->loadFixture();
|
$this->loadFixture();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,17 +255,18 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
|||||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||||
$this->assertFalse($user->groups->isInitialized(), "Pre-Condition: Collection is not initialized.");
|
$this->assertFalse($user->groups->isInitialized(), "Pre-Condition: Collection is not initialized.");
|
||||||
|
|
||||||
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
|
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
|
||||||
|
|
||||||
$queryCount = $this->getCurrentQueryCount();
|
$queryCount = $this->getCurrentQueryCount();
|
||||||
|
|
||||||
$this->assertTrue($user->groups->contains($group));
|
$this->assertTrue($user->groups->contains($group));
|
||||||
$this->assertEquals($queryCount+1, $this->getCurrentQueryCount(), "Checking for contains of managed entity should cause one query to be executed.");
|
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Checking for contains of managed entity should cause one query to be executed.");
|
||||||
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||||
|
|
||||||
$group = new \Doctrine\Tests\Models\CMS\CmsGroup();
|
$group = new \Doctrine\Tests\Models\CMS\CmsGroup();
|
||||||
$group->name = "A New group!";
|
$group->name = "A New group!";
|
||||||
|
|
||||||
$queryCount = $this->getCurrentQueryCount();
|
$queryCount = $this->getCurrentQueryCount();
|
||||||
|
|
||||||
$this->assertFalse($user->groups->contains($group));
|
$this->assertFalse($user->groups->contains($group));
|
||||||
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Checking for contains of new entity should cause no query to be executed.");
|
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Checking for contains of new entity should cause no query to be executed.");
|
||||||
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||||
@ -275,8 +275,9 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
|||||||
$this->_em->flush();
|
$this->_em->flush();
|
||||||
|
|
||||||
$queryCount = $this->getCurrentQueryCount();
|
$queryCount = $this->getCurrentQueryCount();
|
||||||
|
|
||||||
$this->assertFalse($user->groups->contains($group));
|
$this->assertFalse($user->groups->contains($group));
|
||||||
$this->assertEquals($queryCount+1, $this->getCurrentQueryCount(), "Checking for contains of managed entity should cause one query to be executed.");
|
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Checking for contains of managed entity should cause one query to be executed.");
|
||||||
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,6 +305,107 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
|||||||
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function testRemoveElementOneToMany()
|
||||||
|
{
|
||||||
|
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||||
|
$this->assertFalse($user->articles->isInitialized(), "Pre-Condition: Collection is not initialized.");
|
||||||
|
|
||||||
|
$article = $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId);
|
||||||
|
$queryCount = $this->getCurrentQueryCount();
|
||||||
|
|
||||||
|
$user->articles->removeElement($article);
|
||||||
|
|
||||||
|
$this->assertFalse($user->articles->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||||
|
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
|
||||||
|
|
||||||
|
$article = new \Doctrine\Tests\Models\CMS\CmsArticle();
|
||||||
|
$article->topic = "Testnew";
|
||||||
|
$article->text = "blub";
|
||||||
|
|
||||||
|
$queryCount = $this->getCurrentQueryCount();
|
||||||
|
|
||||||
|
$user->articles->removeElement($article);
|
||||||
|
|
||||||
|
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Removing a new entity should cause no query to be executed.");
|
||||||
|
|
||||||
|
$this->_em->persist($article);
|
||||||
|
$this->_em->flush();
|
||||||
|
|
||||||
|
$queryCount = $this->getCurrentQueryCount();
|
||||||
|
|
||||||
|
$user->articles->removeElement($article);
|
||||||
|
|
||||||
|
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a managed entity should cause one query to be executed.");
|
||||||
|
$this->assertFalse($user->articles->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function testRemoveElementManyToMany()
|
||||||
|
{
|
||||||
|
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||||
|
$this->assertFalse($user->groups->isInitialized(), "Pre-Condition: Collection is not initialized.");
|
||||||
|
|
||||||
|
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
|
||||||
|
$queryCount = $this->getCurrentQueryCount();
|
||||||
|
|
||||||
|
$user->groups->removeElement($group);
|
||||||
|
|
||||||
|
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a managed entity should cause one query to be executed.");
|
||||||
|
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||||
|
|
||||||
|
$group = new \Doctrine\Tests\Models\CMS\CmsGroup();
|
||||||
|
$group->name = "A New group!";
|
||||||
|
|
||||||
|
$queryCount = $this->getCurrentQueryCount();
|
||||||
|
|
||||||
|
$user->groups->removeElement($group);
|
||||||
|
|
||||||
|
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Removing new entity should cause no query to be executed.");
|
||||||
|
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||||
|
|
||||||
|
$this->_em->persist($group);
|
||||||
|
$this->_em->flush();
|
||||||
|
|
||||||
|
$queryCount = $this->getCurrentQueryCount();
|
||||||
|
|
||||||
|
$user->groups->removeElement($group);
|
||||||
|
|
||||||
|
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a managed entity should cause one query to be executed.");
|
||||||
|
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function testRemoveElementManyToManyInverse()
|
||||||
|
{
|
||||||
|
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
|
||||||
|
$this->assertFalse($group->users->isInitialized(), "Pre-Condition: Collection is not initialized.");
|
||||||
|
|
||||||
|
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||||
|
$queryCount = $this->getCurrentQueryCount();
|
||||||
|
|
||||||
|
$group->users->removeElement($user);
|
||||||
|
|
||||||
|
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a managed entity should cause one query to be executed.");
|
||||||
|
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||||
|
|
||||||
|
$newUser = new \Doctrine\Tests\Models\CMS\CmsUser();
|
||||||
|
$newUser->name = "A New group!";
|
||||||
|
|
||||||
|
$queryCount = $this->getCurrentQueryCount();
|
||||||
|
|
||||||
|
$group->users->removeElement($newUser);
|
||||||
|
|
||||||
|
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Removing a new entity should cause no query to be executed.");
|
||||||
|
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group DDC-1399
|
* @group DDC-1399
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user