Bug Report
Summary
We're getting Warning: Undefined array key "id_..." during hydration of a lazy ManyToMany
collection that uses indexBy: 'id', where the target entity is part of a JOINED inheritance
hierarchy and the id is declared on the root class.
It only happens when a SQL filter (a soft-delete filter in our case) is enabled/disabled between
queries in the same request. It looks like the same family of bug as #11783 and #12225, but we're
on 2.20.13 which already contains both fixes (#11792, #12373) - so this seems to be a case they
don't cover. Setting fetch: "EAGER" on the association makes the error go away, which matches
the filter-toggle theory (the collection then loads before the filter changes).
Current behavior
Initializing the indexed collection (e.g. $garage->getCars()->toArray()) fails with:
ErrorException: Warning: Undefined array key "id_2"
#0 src/Internal/Hydration/ObjectHydrator.php(509): Doctrine\ORM\Internal\Hydration\ObjectHydrator->hydrateRowData()
#1 src/Internal/Hydration/ObjectHydrator.php(149): Doctrine\ORM\Internal\Hydration\ObjectHydrator->hydrateAllData()
#2 src/Internal/Hydration/AbstractHydrator.php(276): Doctrine\ORM\Internal\Hydration\AbstractHydrator->hydrateAll()
#3 src/Persisters/Entity/BasicEntityPersister.php(1057): Doctrine\ORM\Persisters\Entity\BasicEntityPersister->loadCollectionFromStatement()
#4 src/Persisters/Entity/BasicEntityPersister.php(1067): Doctrine\ORM\Persisters\Entity\BasicEntityPersister->loadManyToManyCollection()
#5 src/UnitOfWork.php(3322): Doctrine\ORM\UnitOfWork->loadCollection()
#6 src/PersistentCollection.php(706): Doctrine\ORM\PersistentCollection->doInitialize()
#7 src/PersistentCollection.php(218): Doctrine\ORM\PersistentCollection->initialize()
#8 doctrine/collections/src/AbstractLazyCollection.php(159): Doctrine\Common\Collections\AbstractLazyCollection->toArray()
#9 {application code calling toArray() on the indexed lazy collection}
Expected behavior
The collection hydrates and is indexed by the inherited identifier no matter how the SQL filters
changed between queries same as already works for SINGLE_TABLE with a non-inherited indexBy
field (#12225) and for ManyToMany without inheritance (#11783).
How to reproduce
A minimal setup - JOINED inheritance, the identifier on the root class, and a ManyToMany indexed
by that identifier:
#[Entity, InheritanceType('JOINED'), DiscriminatorColumn(name: 'discr', type: 'string')]
#[DiscriminatorMap(['car' => Car::class])]
abstract class Vehicle
{
#[Id, GeneratedValue, Column(type: 'integer')]
private int $id;
#[Column(type: 'datetime_immutable', nullable: true)]
private ?\DateTimeImmutable $deletedAt = null;
}
#[Entity]
class Car extends Vehicle
{
}
#[Entity]
class Garage
{
#[Id, GeneratedValue, Column(type: 'integer')]
private int $id;
#[ManyToMany(targetEntity: Car::class, indexBy: 'id'), JoinTable(name: 'garage_cars')]
private Collection $cars; // lazy
}
A simple soft-delete filter:
class SoftDeleteFilter extends SQLFilter
{
public function addFilterConstraint(ClassMetadata $targetEntity, $alias): string
{
return $targetEntity->hasField('deletedAt') ? $alias . '.deleted_at IS NULL' : '';
}
}
The important part is that the Car persister runs two queries under different filter states -
the first one populates its ResultSetMapping, the second one loads the indexed collection:
$em->getConfiguration()->addFilter('soft_delete', SoftDeleteFilter::class);
$em->getFilters()->enable('soft_delete');
// persist a Garage with two Cars in its collection, flush, clear ...
$em->getFilters()->suspend('soft_delete'); // state A
$em->getRepository(Car::class)->findBy([]); // primes the Car ResultSetMapping
$em->getFilters()->restore('soft_delete'); // state B
$garage = $em->getRepository(Garage::class)->findOneBy([]);
$garage->getCars()->toArray(); // => Warning: Undefined array key "id_2"
Bug Report
Summary
We're getting
Warning: Undefined array key "id_..."during hydration of a lazy ManyToManycollection that uses
indexBy: 'id', where the target entity is part of a JOINED inheritancehierarchy and the
idis declared on the root class.It only happens when a SQL filter (a soft-delete filter in our case) is enabled/disabled between
queries in the same request. It looks like the same family of bug as #11783 and #12225, but we're
on 2.20.13 which already contains both fixes (#11792, #12373) - so this seems to be a case they
don't cover. Setting
fetch: "EAGER"on the association makes the error go away, which matchesthe filter-toggle theory (the collection then loads before the filter changes).
Current behavior
Initializing the indexed collection (e.g.
$garage->getCars()->toArray()) fails with:Expected behavior
The collection hydrates and is indexed by the inherited identifier no matter how the SQL filters
changed between queries same as already works for SINGLE_TABLE with a non-inherited
indexByfield (#12225) and for ManyToMany without inheritance (#11783).
How to reproduce
A minimal setup - JOINED inheritance, the identifier on the root class, and a ManyToMany indexed
by that identifier:
A simple soft-delete filter:
The important part is that the
Carpersister runs two queries under different filter states -the first one populates its
ResultSetMapping, the second one loads the indexed collection: