DDC-1425: Strange behaviour of EntityManager::merge and EntityManager::remove methods when a cascading delete constraint is declared #1787

Closed
opened 2026-01-22 13:25:58 +01:00 by admin · 3 comments
Owner

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

Originally assigned to: @beberlei on GitHub.

Jira issue originally created by user dalvarez:

Here is an isolated test to illustrate some IMO ill behaviour of the EntityManager::merge and EntityManager::remove methods, which is a side observation of another test case I am writing. I cannot explain why this should not work. Please have a look.

Here are two test entities:

<?php

/****
 * @Entity
 */

class A {

   /****
    * @Id
    * @Column(type="bigint")
    * @GeneratedValue
    */

   protected $dbID;


   /****
    * @OneToOne(targetEntity="\B", cascade={"persist", "remove", "detach", "merge"})
    * @JoinColumn(name="b_dbID", referencedColumnName="dbID")
    */

   protected $b;


   /****
    * Accessor method for Doctrine proxies to hook in and lazy-load the data object.
    * This provides a simple record with accessible properties.
    */

   public function & **get($name) {

      $referenceHolderVar = $this->$name;

      return $referenceHolderVar;
   }


   /****
    * Mutator method for Doctrine proxies to hook in and lazy-load the data object.
    * This provides a simple record with accessible properties.
    */

   public function **set($name, $value) {

      $this->$name = $value;
   }


   /****
    * **isset method for Doctrine proxies to hook in and lazy-load the data object.
    * This provides a simple record with accessible properties.
    */

   public function **isset($name) {

      return isset ($this->$name);
   }


   /****
    * **unset method for Doctrine proxies to hook in and lazy-load the data object.
    * This provides a simple record with accessible properties.
    */

   public function **unset($name) {

      unset ($this->$name);
   }
}



/****
 * @Entity
 */

class B {

   /****
    * @Id
    * @Column(type="bigint")
    * @GeneratedValue
    */

   protected $dbID;


   /****
    * Accessor method for Doctrine proxies to hook in and lazy-load the data object.
    * This provides a simple record with accessible properties.
    */

   public function & **get($name) {

      $referenceHolderVar = $this->$name;

      return $referenceHolderVar;
   }


   /****
    * Mutator method for Doctrine proxies to hook in and lazy-load the data object.
    * This provides a simple record with accessible properties.
    */

   public function **set($name, $value) {

      $this->$name = $value;
   }


   /****
    * **isset method for Doctrine proxies to hook in and lazy-load the data object.
    * This provides a simple record with accessible properties.
    */

   public function **isset($name) {

      return isset ($this->$name);
   }


   /****
    * **unset method for Doctrine proxies to hook in and lazy-load the data object.
    * This provides a simple record with accessible properties.
    */

   public function **unset($name) {

      unset ($this->$name);
   }
}

?>

Here is the test code, starting from a configured entity manager (which needs to be initialized according to your setup):

<?php

// Create two entity instances, $a of type A, and $b of Type B, such that $a contains $b.

$b = new \B();

$entityManager->persist($b);



$a = new \A();

$a->b = $b;

$entityManager->persist($a);



// Now we flush the session, and clear the entity manager

$entityManager->flush();

$entityManager->clear();



// Now, merge $a and $b back in. Then try to delete $a, which has a cascading delete one-to-one association to $b.
// The call to EntityManager::remove should fail with "Fatal error: Uncaught exception 'InvalidArgumentException' with
// message 'A detached entity can not be removed". This is what I do not understand. Both a and b have are merged
// before the call to EntityManager::remove and therefore none of them should be detached at that moment.
// Removing both the call to EntityManager::clear, and the two calls to EntityManager::merge will make the call to
// EntityManager::remove work.

$entityManager->merge($a);

$entityManager->merge($b);

$entityManager->remove($a);

?>
Originally created by @doctrinebot on GitHub (Oct 16, 2011). Originally assigned to: @beberlei on GitHub. Jira issue originally created by user dalvarez: Here is an isolated test to illustrate some IMO ill behaviour of the EntityManager::merge and EntityManager::remove methods, which is a side observation of another test case I am writing. I cannot explain why this should not work. Please have a look. Here are two test entities: ``` <?php /**** * @Entity */ class A { /**** * @Id * @Column(type="bigint") * @GeneratedValue */ protected $dbID; /**** * @OneToOne(targetEntity="\B", cascade={"persist", "remove", "detach", "merge"}) * @JoinColumn(name="b_dbID", referencedColumnName="dbID") */ protected $b; /**** * Accessor method for Doctrine proxies to hook in and lazy-load the data object. * This provides a simple record with accessible properties. */ public function & **get($name) { $referenceHolderVar = $this->$name; return $referenceHolderVar; } /**** * Mutator method for Doctrine proxies to hook in and lazy-load the data object. * This provides a simple record with accessible properties. */ public function **set($name, $value) { $this->$name = $value; } /**** * **isset method for Doctrine proxies to hook in and lazy-load the data object. * This provides a simple record with accessible properties. */ public function **isset($name) { return isset ($this->$name); } /**** * **unset method for Doctrine proxies to hook in and lazy-load the data object. * This provides a simple record with accessible properties. */ public function **unset($name) { unset ($this->$name); } } /**** * @Entity */ class B { /**** * @Id * @Column(type="bigint") * @GeneratedValue */ protected $dbID; /**** * Accessor method for Doctrine proxies to hook in and lazy-load the data object. * This provides a simple record with accessible properties. */ public function & **get($name) { $referenceHolderVar = $this->$name; return $referenceHolderVar; } /**** * Mutator method for Doctrine proxies to hook in and lazy-load the data object. * This provides a simple record with accessible properties. */ public function **set($name, $value) { $this->$name = $value; } /**** * **isset method for Doctrine proxies to hook in and lazy-load the data object. * This provides a simple record with accessible properties. */ public function **isset($name) { return isset ($this->$name); } /**** * **unset method for Doctrine proxies to hook in and lazy-load the data object. * This provides a simple record with accessible properties. */ public function **unset($name) { unset ($this->$name); } } ?> ``` Here is the test code, starting from a configured entity manager (which needs to be initialized according to your setup): ``` <?php // Create two entity instances, $a of type A, and $b of Type B, such that $a contains $b. $b = new \B(); $entityManager->persist($b); $a = new \A(); $a->b = $b; $entityManager->persist($a); // Now we flush the session, and clear the entity manager $entityManager->flush(); $entityManager->clear(); // Now, merge $a and $b back in. Then try to delete $a, which has a cascading delete one-to-one association to $b. // The call to EntityManager::remove should fail with "Fatal error: Uncaught exception 'InvalidArgumentException' with // message 'A detached entity can not be removed". This is what I do not understand. Both a and b have are merged // before the call to EntityManager::remove and therefore none of them should be detached at that moment. // Removing both the call to EntityManager::clear, and the two calls to EntityManager::merge will make the call to // EntityManager::remove work. $entityManager->merge($a); $entityManager->merge($b); $entityManager->remove($a); ?> ```
admin added the Bug label 2026-01-22 13:25:58 +01:00
admin closed this issue 2026-01-22 13:25:58 +01:00
Author
Owner

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

Comment created by dalvarez:

Duh!...

Just realized the merge method does not work by reference. I should have better saved the return value...

Sometimes problems have simple solutions...

@doctrinebot commented on GitHub (Oct 16, 2011): Comment created by dalvarez: Duh!... Just realized the merge method does not work by reference. I should have better saved the return value... Sometimes problems have simple solutions...
Author
Owner

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

Comment created by dalvarez:

Just stupid.

@doctrinebot commented on GitHub (Oct 16, 2011): Comment created by dalvarez: Just stupid.
Author
Owner

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

Issue was closed with resolution "Invalid"

@doctrinebot commented on GitHub (Oct 16, 2011): Issue was closed with resolution "Invalid"
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#1787