1
0
mirror of synced 2025-02-02 21:41:45 +03:00
Luís Cobucci 2018-02-19 23:17:19 +01:00 committed by GitHub
commit 30a063ef9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 245 additions and 1 deletions

View File

@ -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.
* *

View 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;
}
}