DDC-1401: Unusable entity in postRemove hook when using SINGLE_TABLE inheritance #1758

Closed
opened 2026-01-22 13:24:52 +01:00 by admin · 5 comments
Owner

Originally created by @doctrinebot on GitHub (Oct 4, 2011).

Originally assigned to: @beberlei on GitHub.

Jira issue originally created by user dbenjamin:

In a postRemove hook (from a dedicated listener, not inside an entity), the entity retrieved through Lifecycle event args is unusable when using SINGLE_TABLE inheritance.

Use case :

// base Media entity
namespace My\Entity;

class Media
{
    protected $id;
    protected $name;

    // ...setters/getters
}
<!-- Media mapping -->
<entity name="My\Entity\Media" table="medias" inheritance-type="SINGLE_TABLE">
    <id name="id" type="integer" column="id">
        <generator strategy="AUTO" />
    </id>

    <discriminator-column name="discr" type="string" />
    <discriminator-map>
        <discriminator-mapping value="media" class="My\Entity\Media" />
        <discriminator-mapping value="picture" class="My\Entity\Picture" />
    </discriminator-map>
</entity>
// Picture entity
namespace My\Entity;

class Picture extends Media
{}
<!-- Picture mapping -->
<entity name="My\Entity\Picture" />
// User entity
namespace My\Entity;

class User
{
    private $id;
    private $name;   
    private $picture;

    // ...setters/getters
}
<!-- User mapping -->
<entity name="My\Entity\User" table="users">
    <id name="id" type="integer" column="id">
        <generator strategy="AUTO" />
    </id>

    <field name="name" column="name" type="string" length="80" />

    <one-to-one field="picture" target-entity="My\Entity\Picture">
        <cascade>
            <cascade-all />
        </cascade>
    </one-to-one>
</entity>
// Listener
namespace My\Listener;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;

class DoctrineMediaListener
{
    private $mediaManager;  // injected by IOC

    public function postRemove(LifecycleEventArgs $eventArgs)
    {
        $entity = $eventArgs->getEntity();
        if (!$entity instanceof Media) {
            return;
        }

        $this->mediaManager->removeMedia($entity);
    }   
}
// Media Manager
namespace My\Entity;

class MediaManager
{
    public function removeMedia(Media $media)
    {
        // try to read some $media property
        // and an exception (500) is thrown : Entity was not found.
    }
}

It works well if i use directly a Media object instead of a Picture one in my User class.
But as soon as i use a child object, here Picture, a 500 exception is systematically thrown when i try to access the entity given by the lifecycle event args.

This is really inconvenient since i can't perform any post remove operation when using inheritance system.

Originally created by @doctrinebot on GitHub (Oct 4, 2011). Originally assigned to: @beberlei on GitHub. Jira issue originally created by user dbenjamin: In a postRemove hook (from a dedicated listener, not inside an entity), the entity retrieved through Lifecycle event args is unusable when using SINGLE_TABLE inheritance. Use case : ``` // base Media entity namespace My\Entity; class Media { protected $id; protected $name; // ...setters/getters } ``` ``` xml <!-- Media mapping --> <entity name="My\Entity\Media" table="medias" inheritance-type="SINGLE_TABLE"> <id name="id" type="integer" column="id"> <generator strategy="AUTO" /> </id> <discriminator-column name="discr" type="string" /> <discriminator-map> <discriminator-mapping value="media" class="My\Entity\Media" /> <discriminator-mapping value="picture" class="My\Entity\Picture" /> </discriminator-map> </entity> ``` ``` // Picture entity namespace My\Entity; class Picture extends Media {} ``` ``` xml <!-- Picture mapping --> <entity name="My\Entity\Picture" /> ``` ``` // User entity namespace My\Entity; class User { private $id; private $name; private $picture; // ...setters/getters } ``` ``` xml <!-- User mapping --> <entity name="My\Entity\User" table="users"> <id name="id" type="integer" column="id"> <generator strategy="AUTO" /> </id> <field name="name" column="name" type="string" length="80" /> <one-to-one field="picture" target-entity="My\Entity\Picture"> <cascade> <cascade-all /> </cascade> </one-to-one> </entity> ``` ``` // Listener namespace My\Listener; use Doctrine\ORM\Event\LifecycleEventArgs; use Doctrine\ORM\Event\PreUpdateEventArgs; class DoctrineMediaListener { private $mediaManager; // injected by IOC public function postRemove(LifecycleEventArgs $eventArgs) { $entity = $eventArgs->getEntity(); if (!$entity instanceof Media) { return; } $this->mediaManager->removeMedia($entity); } } ``` ``` // Media Manager namespace My\Entity; class MediaManager { public function removeMedia(Media $media) { // try to read some $media property // and an exception (500) is thrown : Entity was not found. } } ``` It works well if i use directly a Media object instead of a Picture one in my User class. But as soon as i use a child object, here Picture, a 500 exception is systematically thrown when i try to access the entity given by the lifecycle event args. This is really inconvenient since i can't perform any post remove operation when using inheritance system.
admin added the Bug label 2026-01-22 13:24:52 +01:00
admin closed this issue 2026-01-22 13:24:52 +01:00
Author
Owner

@doctrinebot commented on GitHub (Oct 15, 2011):

Comment created by @beberlei:

In your case i think the $media is a proxy object and then cannot be loaded in "postRemove" anymore.

Cant you just do that in preRemove (find the path)? And then work on postRemove() to delete it from a stack in the Media manager.

@doctrinebot commented on GitHub (Oct 15, 2011): Comment created by @beberlei: In your case i think the $media is a proxy object and then cannot be loaded in "postRemove" anymore. Cant you just do that in preRemove (find the path)? And then work on postRemove() to delete it from a stack in the Media manager.
Author
Owner

@doctrinebot commented on GitHub (Oct 15, 2011):

Comment created by @beberlei:

Invalid since in postRemove this can happen when you have an proxy and there is a workaround.

@doctrinebot commented on GitHub (Oct 15, 2011): Comment created by @beberlei: Invalid since in postRemove this can happen when you have an proxy and there is a workaround.
Author
Owner

@doctrinebot commented on GitHub (Oct 15, 2011):

Issue was closed with resolution "Invalid"

@doctrinebot commented on GitHub (Oct 15, 2011): Issue was closed with resolution "Invalid"
Author
Owner

@doctrinebot commented on GitHub (Oct 16, 2011):

Comment created by dbenjamin:

bq. Cant you just do that in preRemove (find the path)? And then work on postRemove() to delete it from a stack in the Media manager.

No i can't because the remove behaviour is not just about removing files and i need the informations about my entity.
I could register entities to be removed in the "preRemove" hook and do something with them later in the "postRemove" hook, but i assume the entities i would register would be Proxy objects too, so same problem.

Is there a way to retrieve the actual entity and not its proxy object ?

For now there is no workaround for my use case.

@doctrinebot commented on GitHub (Oct 16, 2011): Comment created by dbenjamin: bq. Cant you just do that in preRemove (find the path)? And then work on postRemove() to delete it from a stack in the Media manager. No i can't because the remove behaviour is not just about removing files and i need the informations about my entity. I could register entities to be removed in the "preRemove" hook and do something with them later in the "postRemove" hook, but i assume the entities i would register would be Proxy objects too, so same problem. Is there a way to retrieve the actual entity and not its proxy object ? For now there is no workaround for my use case.
Author
Owner

@doctrinebot commented on GitHub (Sep 21, 2014):

Comment created by acolomitchi:

Please review/reconsider. I fail to understand the reason why setting the id to null is necessary.

In my case, the entity (derived) is loaded (so no proxy).
I'm asking the entityManager to delete it (not to modify it). As such, when getting to the postRemove to:
a. delete file system resources (based on the id)
b. adjust an audit record
I find the id field set to null, so everything blows.

True, intercepting preRemove and scheduling the actual deletion to postRemove/postFlush may be a workaround, but why should it be done in such a contorted way? If the EM absolutely need to "damage" the deleted entity (ubder the risk of loosing the consistency), then how about passing the pre-existing values as separate parameters in the event?

@doctrinebot commented on GitHub (Sep 21, 2014): Comment created by acolomitchi: Please review/reconsider. I fail to understand the reason why setting the id to null is necessary. In my case, the entity (derived) is loaded (so no proxy). I'm asking the entityManager to delete it (not to modify it). As such, when getting to the postRemove to: a. delete file system resources (based on the id) b. adjust an audit record I find the id field set to null, so everything blows. True, intercepting preRemove and scheduling the actual deletion to postRemove/postFlush **may** be a workaround, but why should it be done in such a contorted way? If the EM absolutely need to "damage" the deleted entity (ubder the risk of loosing the consistency), then how about passing the pre-existing values as separate parameters in the event?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#1758