Skip to content

Hydration error with ManyToMany relation indexed by inherited id when changing SQL filters #12505

Description

@igrizzli

Bug Report

Q A
Version 2.20.13

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"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions