DDC-3375: UnitOfWork: new operation attach(): merge without persist #4171

Closed
opened 2026-01-22 14:36:33 +01:00 by admin · 4 comments
Owner

Originally created by @doctrinebot on GitHub (Nov 6, 2014).

Originally assigned to: @Ocramius on GitHub.

Jira issue originally created by user mathieudz:

Currently UnitOfWork provides the following three methods that make entities cross the boundaries of being managed or not:

  • persist()
  • merge()
  • detach()

In my opinion, there's an operation missing: attach(). It would be similar to merge(), but it would not call persist(). It would be the counterpart of detach().

Example 1: Simple case
{quote}
if (...)
$person = new Person;
else
$person = $em->find('Person', $id);

// At some point I decide that I don't want to store the person, so I don't call persist()

$dog = new Dog;
$em->persist($dog);

$em->flush(); // OK, commits only the dog
{quote}

Example 2: Same, but the person is serialized along the way
{quote}
if (...)
$person = new Person;
else
$person = $em->find('Person', $id);

// Serialize the person
$serializedPerson = serialize($person);

// At some point I want to restore the person
$person = unserialize($serializedPerson);

// Because it could have been a managed entity before, I need to merge it.
$person = $em->merge($person);

// But yet again, I decide that I don't want to store the person, so I don't call persist()

$dog = new Dog;
$em->persist($dog);

$em->flush(); // Oops, if the person was NEW, it has become managed, so it is commited now!
{quote}

If merge() was replaced by attach(), flush() would not have commited the person.

Originally created by @doctrinebot on GitHub (Nov 6, 2014). Originally assigned to: @Ocramius on GitHub. Jira issue originally created by user mathieudz: Currently UnitOfWork provides the following three methods that make entities cross the boundaries of being managed or not: - persist() - merge() - detach() In my opinion, there's an operation missing: attach(). It would be similar to merge(), but it would not call persist(). It would be the counterpart of detach(). Example 1: Simple case {quote} if (...) $person = new Person; else $person = $em->find('Person', $id); // At some point I decide that I don't want to store the person, so I don't call persist() $dog = new Dog; $em->persist($dog); $em->flush(); // OK, commits only the dog {quote} Example 2: Same, but the person is serialized along the way {quote} if (...) $person = new Person; else $person = $em->find('Person', $id); // Serialize the person $serializedPerson = serialize($person); // At some point I want to restore the person $person = unserialize($serializedPerson); // Because it could have been a managed entity before, I need to merge it. $person = $em->merge($person); // But yet again, I decide that I don't want to store the person, so I don't call persist() $dog = new Dog; $em->persist($dog); $em->flush(); // Oops, if the person was NEW, it has become managed, so it is commited now! {quote} If merge() was replaced by attach(), flush() would not have commited the person.
admin added the Improvement label 2026-01-22 14:36:33 +01:00
admin closed this issue 2026-01-22 14:36:33 +01:00
Author
Owner

@doctrinebot commented on GitHub (Nov 6, 2014):

Comment created by stof:

I don't understand what attach would do in your case. flush is about flushing the whole unit of work. I don't understand what attach() would do if it does not attach the entity to the unit of work.

@doctrinebot commented on GitHub (Nov 6, 2014): Comment created by stof: I don't understand what attach would do in your case. flush is about flushing the whole unit of work. I don't understand what attach() would do if it does not attach the entity to the unit of work.
Author
Owner

@doctrinebot commented on GitHub (Nov 6, 2014):

Comment created by stof:

And actually, the opposite of detach() already exists: it is persist(). It makes the object enter the unit of work, while detach makes it leave the unit of work.

Note that merge is not making the object cross the boundary. The object passed to merge() never enters the unit of work. what happens is that the data inside this object are applied to an object inside the boundary (potentially a new one if there was no object with this identifier).
When using merge, you are explicitly asking to apply the data inside the unit of work, so it is logical that something gets flushed.

and note that in your example 1, your person is managed half of the time (when coming from find() rather than a new instance). And in such case, it will be flushed. You are not flushing only the dog.

@doctrinebot commented on GitHub (Nov 6, 2014): Comment created by stof: And actually, the opposite of detach() already exists: it is persist(). It makes the object enter the unit of work, while detach makes it leave the unit of work. Note that merge is not making the object cross the boundary. The object passed to merge() never enters the unit of work. what happens is that the **data** inside this object are applied to an object inside the boundary (potentially a new one if there was no object with this identifier). When using merge, you are explicitly asking to apply the data inside the unit of work, so it is logical that something gets flushed. and note that in your example 1, your person is managed half of the time (when coming from find() rather than a new instance). And in such case, it will be flushed. You are not flushing only the dog.
Author
Owner

@doctrinebot commented on GitHub (Nov 7, 2014):

Issue was closed with resolution "Won't Fix"

@doctrinebot commented on GitHub (Nov 7, 2014): Issue was closed with resolution "Won't Fix"
Author
Owner

@doctrinebot commented on GitHub (Nov 8, 2014):

Comment created by mathieudz:

OK, makes sense.

@doctrinebot commented on GitHub (Nov 8, 2014): Comment created by mathieudz: OK, makes sense.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#4171