Skip to content

Commit

Permalink
Make EntityPersisters tell the UoW about post insert IDs early
Browse files Browse the repository at this point in the history
This refactoring does two things:

* We can avoid collecting the post insert IDs in a cumbersome array structure that will be returned by the EntityPersisters and processed by the UoW right after. Instead, use a more expressive API: Make the EntityPersisters tell the UoW about the IDs immediately.
* IDs will be available in inserted entities a tad sooner. That may help to resolve #10735, where we can use the IDs to skip extra updates.
  • Loading branch information
mpdude committed Jun 21, 2023
1 parent 41f704c commit f93004c
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 82 deletions.
6 changes: 6 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Upgrade to 2.16

## Deprecated returning post insert IDs from `EntityPersister::executeInserts()`

Persisters implementing `\Doctrine\ORM\Persisters\Entity\EntityPersister` should no longer
return an array of post insert IDs from their `::executeInserts()` method. Make the
persister call `Doctrine\ORM\UnitOfWork::assignPostInsertId()` instead.

## Changing the way how reflection-based mapping drivers report fields, deprecated the "old" mode

In ORM 3.0, a change will be made regarding how the `AttributeDriver` reports field mappings.
Expand Down
16 changes: 6 additions & 10 deletions lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,10 @@ public function getInserts()
public function executeInserts()
{
if (! $this->queuedInserts) {
return [];
return;
}

$postInsertIds = [];
$uow = $this->em->getUnitOfWork();
$idGenerator = $this->class->idGenerator;
$isPostInsertId = $idGenerator->isPostInsertGenerator();

Expand All @@ -280,12 +280,10 @@ public function executeInserts()
$stmt->executeStatement();

if ($isPostInsertId) {
$generatedId = $idGenerator->generateId($this->em, $entity);
$id = [$this->class->identifier[0] => $generatedId];
$postInsertIds[] = [
'generatedId' => $generatedId,
'entity' => $entity,
];
$generatedId = $idGenerator->generateId($this->em, $entity);
$id = [$this->class->identifier[0] => $generatedId];

$uow->assignPostInsertId($entity, $generatedId);
} else {
$id = $this->class->getIdentifierValues($entity);
}
Expand All @@ -296,8 +294,6 @@ public function executeInserts()
}

$this->queuedInserts = [];

return $postInsertIds;
}

/**
Expand Down
6 changes: 3 additions & 3 deletions lib/Doctrine/ORM/Persisters/Entity/EntityPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,12 @@ public function addInsert($entity);
*
* If no inserts are queued, invoking this method is a NOOP.
*
* @psalm-return list<array{
* @psalm-return void|list<array{
* generatedId: int,
* entity: object
* }> An array of any generated post-insert IDs. This will be
* }> Returning an array of any generated post-insert IDs. This will be
* an empty array if the entity class does not use the
* IDENTITY generation strategy.
* IDENTITY generation strategy. This is deprecated, call UnitOfWork::assignPostInsertId() instead.
*/
public function executeInserts();

Expand Down
16 changes: 6 additions & 10 deletions lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,10 @@ public function getOwningTable($fieldName)
public function executeInserts()
{
if (! $this->queuedInserts) {
return [];
return;
}

$postInsertIds = [];
$uow = $this->em->getUnitOfWork();
$idGenerator = $this->class->idGenerator;
$isPostInsertId = $idGenerator->isPostInsertGenerator();
$rootClass = $this->class->name !== $this->class->rootEntityName
Expand Down Expand Up @@ -157,12 +157,10 @@ public function executeInserts()
$rootTableStmt->executeStatement();

if ($isPostInsertId) {
$generatedId = $idGenerator->generateId($this->em, $entity);
$id = [$this->class->identifier[0] => $generatedId];
$postInsertIds[] = [
'generatedId' => $generatedId,
'entity' => $entity,
];
$generatedId = $idGenerator->generateId($this->em, $entity);
$id = [$this->class->identifier[0] => $generatedId];

$uow->assignPostInsertId($entity, $generatedId);
} else {
$id = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
}
Expand Down Expand Up @@ -194,8 +192,6 @@ public function executeInserts()
}

$this->queuedInserts = [];

return $postInsertIds;
}

/**
Expand Down
62 changes: 41 additions & 21 deletions lib/Doctrine/ORM/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -1143,30 +1143,24 @@ private function executeInserts(ClassMetadata $class): void

$postInsertIds = $persister->executeInserts();

if ($postInsertIds) {
if (is_array($postInsertIds)) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/10743/',
'Returning post insert IDs from \Doctrine\ORM\Persisters\Entity\EntityPersister::executeInserts() is deprecated and will not be supported in Doctrine ORM 3.0. Make the persister call Doctrine\ORM\UnitOfWork::assignPostInsertId() instead.'
);

// Persister returned post-insert IDs
foreach ($postInsertIds as $postInsertId) {
$idField = $class->getSingleIdentifierFieldName();
$idValue = $this->convertSingleFieldIdentifierToPHPValue($class, $postInsertId['generatedId']);

$entity = $postInsertId['entity'];
$oid = spl_object_id($entity);

$class->reflFields[$idField]->setValue($entity, $idValue);

$this->entityIdentifiers[$oid] = [$idField => $idValue];
$this->entityStates[$oid] = self::STATE_MANAGED;
$this->originalEntityData[$oid][$idField] = $idValue;

$this->addToIdentityMap($entity);
$this->assignPostInsertId($postInsertId['entity'], $postInsertId['generatedId']);
}
} 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 ($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);
}
}

Expand Down Expand Up @@ -3790,4 +3784,30 @@ private function normalizeIdentifier(ClassMetadata $targetClass, array $flatIden

return $normalizedAssociatedId;
}

/**
* Assign a post-insert generated ID to an entity
*
* This is used by EntityPersisters after they inserted entities into the database.
* It will place the assigned ID values in the entity's fields and start tracking
* the entity in the identity map.
*
* @param object $entity
* @param mixed $generatedId
*/
public function assignPostInsertId($entity, $generatedId): void
{
$class = $this->em->getClassMetadata(get_class($entity));
$idField = $class->getSingleIdentifierFieldName();
$idValue = $this->convertSingleFieldIdentifierToPHPValue($class, $generatedId);
$oid = spl_object_id($entity);

$class->reflFields[$idField]->setValue($entity, $idValue);

$this->entityIdentifiers[$oid] = [$idField => $idValue];
$this->entityStates[$oid] = self::STATE_MANAGED;
$this->originalEntityData[$oid][$idField] = $idValue;

$this->addToIdentityMap($entity);
}
}

This file was deleted.

0 comments on commit f93004c

Please sign in to comment.