Merge pull request #6701 from vhenzl/pr/issue-6531-test
Add failing tests for #6531 Fixes https://github.com/doctrine/doctrine2/issues/6043 Fixes https://github.com/doctrine/doctrine2/issues/6531 Fixes https://github.com/doctrine/doctrine2/issues/7002 Fixes https://github.com/doctrine/doctrine2/pull/7003
This commit is contained in:
commit
30a063ef9d
@ -940,7 +940,11 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
$class->setIdentifierValues($entity, $idValue);
|
$class->setIdentifierValues($entity, $idValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->entityIdentifiers[$oid] = $idValue;
|
// Some identifiers may be foreign keys to new entities.
|
||||||
|
// In this case, we don't have the value yet and should treat it as if we have a post-insert generator
|
||||||
|
if (! $this->hasMissingIdsWhichAreForeignKeys($class, $idValue)) {
|
||||||
|
$this->entityIdentifiers[$oid] = $idValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->entityStates[$oid] = self::STATE_MANAGED;
|
$this->entityStates[$oid] = self::STATE_MANAGED;
|
||||||
@ -948,6 +952,20 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
$this->scheduleForInsert($entity);
|
$this->scheduleForInsert($entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $idValue
|
||||||
|
*/
|
||||||
|
private function hasMissingIdsWhichAreForeignKeys(ClassMetadata $class, array $idValue) : bool
|
||||||
|
{
|
||||||
|
foreach ($idValue as $idField => $idFieldValue) {
|
||||||
|
if ($idFieldValue === null && isset($class->associationMappings[$idField])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL:
|
* INTERNAL:
|
||||||
* Computes the changeset of an individual entity, independently of the
|
* Computes the changeset of an individual entity, independently of the
|
||||||
@ -1033,12 +1051,16 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
$persister = $this->getEntityPersister($className);
|
$persister = $this->getEntityPersister($className);
|
||||||
$invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist);
|
$invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist);
|
||||||
|
|
||||||
|
$insertionsForClass = [];
|
||||||
|
|
||||||
foreach ($this->entityInsertions as $oid => $entity) {
|
foreach ($this->entityInsertions as $oid => $entity) {
|
||||||
|
|
||||||
if ($this->em->getClassMetadata(get_class($entity))->name !== $className) {
|
if ($this->em->getClassMetadata(get_class($entity))->name !== $className) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$insertionsForClass[$oid] = $entity;
|
||||||
|
|
||||||
$persister->addInsert($entity);
|
$persister->addInsert($entity);
|
||||||
|
|
||||||
unset($this->entityInsertions[$oid]);
|
unset($this->entityInsertions[$oid]);
|
||||||
@ -1067,6 +1089,14 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
|
|
||||||
$this->addToIdentityMap($entity);
|
$this->addToIdentityMap($entity);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
foreach ($insertionsForClass as $oid => $entity) {
|
||||||
|
if (! isset($this->entityIdentifiers[$oid])) {
|
||||||
|
//entity was not added to identity map because some identifiers are foreign keys to new entities.
|
||||||
|
//add it now
|
||||||
|
$this->addToEntityIdentifiersAndEntityMap($class, $oid, $entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($entities as $entity) {
|
foreach ($entities as $entity) {
|
||||||
@ -1074,6 +1104,30 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param object $entity
|
||||||
|
*/
|
||||||
|
private function addToEntityIdentifiersAndEntityMap(ClassMetadata $class, string $oid, $entity): void
|
||||||
|
{
|
||||||
|
$identifier = [];
|
||||||
|
|
||||||
|
foreach ($class->getIdentifierFieldNames() as $idField) {
|
||||||
|
$value = $class->getFieldValue($entity, $idField);
|
||||||
|
|
||||||
|
if (isset($class->associationMappings[$idField])) {
|
||||||
|
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
|
||||||
|
$value = $this->getSingleIdentifierValue($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$identifier[$idField] = $this->originalEntityData[$oid][$idField] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->entityStates[$oid] = self::STATE_MANAGED;
|
||||||
|
$this->entityIdentifiers[$oid] = $identifier;
|
||||||
|
|
||||||
|
$this->addToIdentityMap($entity);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes all entity updates for entities of the specified type.
|
* Executes all entity updates for entities of the specified type.
|
||||||
*
|
*
|
||||||
|
190
tests/Doctrine/Tests/ORM/Functional/Ticket/GH6531Test.php
Normal file
190
tests/Doctrine/Tests/ORM/Functional/Ticket/GH6531Test.php
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||||
|
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
|
||||||
|
final class GH6531Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||||
|
{
|
||||||
|
protected function setUp() : void
|
||||||
|
{
|
||||||
|
parent::setup();
|
||||||
|
|
||||||
|
$this->setUpEntitySchema(
|
||||||
|
[
|
||||||
|
GH6531User::class,
|
||||||
|
GH6531Address::class,
|
||||||
|
GH6531Article::class,
|
||||||
|
GH6531ArticleAttribute::class,
|
||||||
|
GH6531Order::class,
|
||||||
|
GH6531OrderItem::class,
|
||||||
|
GH6531Product::class,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group 6531
|
||||||
|
*/
|
||||||
|
public function testSimpleDerivedIdentity() : void
|
||||||
|
{
|
||||||
|
$user = new GH6531User();
|
||||||
|
$address = new GH6531Address();
|
||||||
|
$address->user = $user;
|
||||||
|
|
||||||
|
$this->_em->persist($user);
|
||||||
|
$this->_em->persist($address);
|
||||||
|
$this->_em->flush();
|
||||||
|
|
||||||
|
self::assertSame($user, $this->_em->find(GH6531User::class, $user->id));
|
||||||
|
self::assertSame($address, $this->_em->find(GH6531Address::class, $user));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group 6531
|
||||||
|
*/
|
||||||
|
public function testDynamicAttributes() : void
|
||||||
|
{
|
||||||
|
$article = new GH6531Article();
|
||||||
|
$article->addAttribute('name', 'value');
|
||||||
|
|
||||||
|
$this->_em->persist($article);
|
||||||
|
$this->_em->flush();
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
$article->attributes['name'],
|
||||||
|
$this->_em->find(GH6531ArticleAttribute::class, ['article' => $article, 'attribute' => 'name'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group 6531
|
||||||
|
*/
|
||||||
|
public function testJoinTableWithMetadata() : void
|
||||||
|
{
|
||||||
|
$product = new GH6531Product();
|
||||||
|
$this->_em->persist($product);
|
||||||
|
$this->_em->flush();
|
||||||
|
|
||||||
|
$order = new GH6531Order();
|
||||||
|
$order->addItem($product, 2);
|
||||||
|
|
||||||
|
$this->_em->persist($order);
|
||||||
|
$this->_em->flush();
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
$order->items->first(),
|
||||||
|
$this->_em->find(GH6531OrderItem::class, ['product' => $product, 'order' => $order])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
*/
|
||||||
|
class GH6531User
|
||||||
|
{
|
||||||
|
/** @Id @Column(type="integer") @GeneratedValue */
|
||||||
|
public $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
*/
|
||||||
|
class GH6531Address
|
||||||
|
{
|
||||||
|
/** @Id @OneToOne(targetEntity=GH6531User::class) */
|
||||||
|
public $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
*/
|
||||||
|
class GH6531Article
|
||||||
|
{
|
||||||
|
/** @Id @Column(type="integer") @GeneratedValue */
|
||||||
|
public $id;
|
||||||
|
|
||||||
|
/** @OneToMany(targetEntity=GH6531ArticleAttribute::class, mappedBy="article", cascade={"ALL"}, indexBy="attribute") */
|
||||||
|
public $attributes;
|
||||||
|
|
||||||
|
public function addAttribute(string $name, string $value)
|
||||||
|
{
|
||||||
|
$this->attributes[$name] = new GH6531ArticleAttribute($name, $value, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
*/
|
||||||
|
class GH6531ArticleAttribute
|
||||||
|
{
|
||||||
|
/** @Id @ManyToOne(targetEntity=GH6531Article::class, inversedBy="attributes") */
|
||||||
|
public $article;
|
||||||
|
|
||||||
|
/** @Id @Column(type="string") */
|
||||||
|
public $attribute;
|
||||||
|
|
||||||
|
/** @Column(type="string") */
|
||||||
|
public $value;
|
||||||
|
|
||||||
|
public function __construct(string $name, string $value, GH6531Article $article)
|
||||||
|
{
|
||||||
|
$this->attribute = $name;
|
||||||
|
$this->value = $value;
|
||||||
|
$this->article = $article;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
*/
|
||||||
|
class GH6531Order
|
||||||
|
{
|
||||||
|
/** @Id @Column(type="integer") @GeneratedValue */
|
||||||
|
public $id;
|
||||||
|
|
||||||
|
/** @OneToMany(targetEntity=GH6531OrderItem::class, mappedBy="order", cascade={"ALL"}) */
|
||||||
|
public $items;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->items = new ArrayCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addItem(GH6531Product $product, int $amount) : void
|
||||||
|
{
|
||||||
|
$this->items->add(new GH6531OrderItem($this, $product, $amount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
*/
|
||||||
|
class GH6531Product
|
||||||
|
{
|
||||||
|
/** @Id @Column(type="integer") @GeneratedValue */
|
||||||
|
public $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
*/
|
||||||
|
class GH6531OrderItem
|
||||||
|
{
|
||||||
|
/** @Id @ManyToOne(targetEntity=GH6531Order::class) */
|
||||||
|
public $order;
|
||||||
|
|
||||||
|
/** @Id @ManyToOne(targetEntity=GH6531Product::class) */
|
||||||
|
public $product;
|
||||||
|
|
||||||
|
/** @Column(type="integer") */
|
||||||
|
public $amount = 1;
|
||||||
|
|
||||||
|
public function __construct(GH6531Order $order, GH6531Product $product, int $amount = 1)
|
||||||
|
{
|
||||||
|
$this->order = $order;
|
||||||
|
$this->product = $product;
|
||||||
|
$this->amount = $amount;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user