From f7f7511fecd8b2691edc827176c6052175f2384e Mon Sep 17 00:00:00 2001 From: Zacharias Luiten Date: Sat, 9 Jun 2018 00:48:04 +0200 Subject: [PATCH] Fix #4252 Cascade first before refreshing the entity itself so toMany associations are not reset to empty collections --- lib/Doctrine/ORM/UnitOfWork.php | 4 +- .../ORM/Functional/Ticket/GH4252Test.php | 212 ++++++++++++++++++ 2 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/GH4252Test.php diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 6767e7ef310..f4a2467533e 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -2149,12 +2149,12 @@ private function doRefresh($entity, array &$visited) throw ORMInvalidArgumentException::entityNotManaged($entity); } + $this->cascadeRefresh($entity, $visited); + $this->getEntityPersister($class->name)->refresh( array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]), $entity ); - - $this->cascadeRefresh($entity, $visited); } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH4252Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH4252Test.php new file mode 100644 index 00000000000..efd7802674f --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH4252Test.php @@ -0,0 +1,212 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(GH4252City::class), + $this->_em->getClassMetadata(GH4252Resident::class), + $this->_em->getClassMetadata(GH4252Address::class), + )); + } + + public function testIssue() + { + $city = new GH4252City([new GH4252Resident([new GH4252Address()])]); + + $this->_em->persist($city); + $this->_em->flush(); + $this->_em->clear(); + + /** @var GH4252City $city */ + $city = $this->_em->find(GH4252City::class, $city->getId()); + $city->setFlag(false); + /** @var GH4252Resident $resident */ + $resident = $city->getResidents()->first(); + $resident->setFlag(false); + /** @var GH4252Address $address */ + $address = $resident->getAddresses()->first(); + $address->setFlag(false); + + $this->_em->refresh($city); + + $resident = $city->getResidents()->first(); + $address = $resident->getAddresses()->first(); + + $this->assertTrue($city->getFlag()); + $this->assertTrue($resident->getFlag()); + $this->assertTrue($address->getFlag()); + } +} + +/** + * @Entity + */ +class GH4252City +{ + /** + * @var int + * @Id @Column(type="integer") @GeneratedValue + */ + private $id; + + /** + * @var bool + * @Column(type="boolean") + */ + private $flag; + + /** + * @var GH4252Resident[]|Collection + * + * @OneToMany(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\GH4252Resident", mappedBy="city", cascade={"persist","refresh"}) + */ + private $residents; + + public function __construct(array $residents) + { + $this->residents = new ArrayCollection(); + foreach ($residents as $resident) { + $this->residents->add($resident); + $resident->setCity($this); + } + $this->flag = true; + } + + public function getId() : int + { + return $this->id; + } + + public function getFlag() : bool + { + return $this->flag; + } + + public function setFlag(bool $flag) : void + { + $this->flag = $flag; + } + + public function getResidents() : Collection + { + return $this->residents; + } +} + +/** + * @Entity + */ +class GH4252Resident +{ + /** + * @var int + * @Id @Column(type="integer") @GeneratedValue + */ + private $id; + + /** + * @var GH4252City + * @ManyToOne(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\GH4252City", inversedBy="residents") + */ + private $city; + + /** + * @var bool + * @Column(type="boolean") + */ + private $flag; + + /** + * @var GH4252Address[]|Collection + * + * @ManyToMany(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\GH4252Address", fetch="EXTRA_LAZY", cascade={"persist","refresh"}) + */ + private $addresses; + + public function __construct(array $addresses) + { + $this->addresses = new ArrayCollection(); + foreach ($addresses as $address) { + $this->addresses->add($address); + } + $this->flag = true; + } + + public function getId() : int + { + return $this->id; + } + + public function getCity() : GH4252City + { + return $this->city; + } + + public function setCity(GH4252City $city) : void + { + $this->city = $city; + } + + public function getFlag() : bool + { + return $this->flag; + } + + public function setFlag(bool $flag) : void + { + $this->flag = $flag; + } + + public function getAddresses() : Collection + { + return $this->addresses; + } +} + +/** @Entity */ +class GH4252Address +{ + /** + * @var int + * @Id @Column(type="integer") @GeneratedValue + */ + private $id; + + /** + * @var bool + * @Column(type="boolean") + */ + private $flag; + + public function __construct() + { + $this->flag = true; + } + + public function getId() : int + { + return $this->id; + } + + public function getFlag() : bool + { + return $this->flag; + } + + public function setFlag(bool $flag) : void + { + $this->flag = $flag; + } +}