doctrine / orm

Doctrine Object Relational Mapper (ORM)
https://www.doctrine-project.org/projects/orm.html
MIT License
9.86k stars 2.5k forks source link

Misleading ORMInvalidArgumentException after removal of referred entity of ManyToOne association with onDelete: 'CASCADE' #11539

Open tobiasgv opened 5 days ago

tobiasgv commented 5 days ago

Bug Report

Q A
BC Break no
Version 3.2.1

Summary

For a ManyToOne association where the owning side (class A) has the onDelete: 'CASCADE' option set: when a referred entity (of class B) is removed by the entity manager then a subsequent call to persist()+flush() causes a misleading ORMInvalidArgumentException:

Doctrine\ORM\ORMInvalidArgumentException: A new entity was found through the relationship 'A#b' that was not configured to cascade persist operations for entity: XXX. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example @ManyToOne(..,cascade={"persist"}).

for managed entities of class A (which were cascade deleted by the DB), since the referenced entity of class B is mis-interpreted as a new entity.

How to reproduce


        #[ORM\JoinColumn(name: 'b_identifier', referencedColumnName: 'identifier', onDelete: 'CASCADE')]
        #[ORM\ManyToOne(targetEntity: B::class)]
        private ?B $b = null;

        $b = new B();
        $this->entityManger->persist($b);
        $this->entityManger->flush();

        $a = new A();
        $a->setB($b);
        $this->entityManger->persist($a);
        $this->entityManger->flush();

        $this->entityManger->remove($b);
        $this->entityManger->flush();

        $b2 = new B();
        $this->entityManger->persist($b2);
        $this->entityManger->flush(); // throws

Expected behavior

No exception should be thrown.

Workaround

Unsetting the referenced entity before flushing prevents the exception: $a->setB(null);

stof commented 3 days ago

The issue in your case is that $a still reference $b in your UnitOfWork.

onDelete: 'CASCADE' is about configuring the database-level cascading on the foreign key. This will lead to the row being deleted from the database in the a table, but the ORM still has $a in its UnitOfWork as it does not know about this removal.

If your schema relies on onDelete: 'CASCADE' (or onDelete: 'SET NULL'), you should be careful when you keep using the UnitOfWork after a flush. To be reliable, you would have to clear the UnitOfWork after a flush involving removals (as such flushes might have triggered an onDelete behavior)