I've been trying to figure out how to get this to work for along time but without any luck. Due to a complex logic in an app I'm working on, I need to create an isolated clone
of a entity collection without preserving what so ever relation to the database. Whatever changes I do on the cloned collection should not be tracked by Doctrine at all and should be treated as if it doesn't exist at all.
Here's an example code:
/*
* @ORM\Entity()
*/
class Person
{
/**
* @var integer
*
* @ORM\Id
* @ORM\Column(name="person_id", type="integer",nullable=false)
* @ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* @var ArrayCollection
*
* @ORM\OneToMany(targetEntity="Car", mappedBy="person", cascade={"persist"})
*/
public $cars;
}
/**
* @ORM\Entity()
* @ORM\HasLifecycleCallbacks()
*/
class Car
{
/**
* @var integer
*
* @ORM\Id
* @ORM\Column(name="car_id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/*
* @ORM\JoinColumn(name="person_id", referencedColumnName="person_id", nullable=true)
* @ORM\ManyToOne(targetEntity="Person", inversedBy="cars", cascade={"persist"})
*/
private $person;
}
I've already tried the following code in my controller to store the collection into the session but it still somehow stores the relationships:
$tmp = clone $person;
$this->get('session')->set('carCollection', $tmp->getCars());
$tmpCars = clone $person->getCars();
$tmpCollection = new ArrayCollection();
foreach($tmpCars as $car) {
$tmpCollection->add(clone $car);
}
$this->get('session')->set('carCollection', $tmpCollection);
$tmpCars = clone $person->getCars();
$tmpCollection = new ArrayCollection();
foreach($tmpCars as $car) {
$clone = clone $car;
$entityManager->detach($car);
$tmpCollection->add(clone $clone);
}
$this->get('session')->set('carCollection', $tmpCollection);
Apparently I'm doing something wrong here because I end up having more results in the Car
collection when flush
ing the entity even though the collection itself has the correct number of records. I have a suspicion that somewhere in the chain Doctrine doesn't compute correctly what needs to be done.
Any ideas or directions on how to solve or debug this?
Follow-up question: When retrieving back the cloned collection from the session will it still be an isolated clone or Doctrine will try merge it back?
I think hydrators/extractors would be the way to go for you.
They can extract the data from an entity and you can pass them to a newly created instance of that entity via the hydrator. The only thing you'll need to do in between is the unsetting of the relation properties. They should be fetchable via a metadata class via doctrine somehow.
I'm writing this answer to give directions to anybody who might have similar issues. I couldn't find many topics or documentation in this manner which is why I decided to share my experience. I am no deep expert on Doctrine an how it internally works, so I won't go into big details of "how it works". I will rather focus on the end result.
Storing entities which have relations to other entities into a session is quite problematic. When you retrieve it from the session, Doctrine loses track of the relationships (OneToMany, ManyToOne, etc). This leads to some undesired effects:
A new entity was found through the relationship 'Acme\MyBundle\Entity\Person#cars' that was not configured to cascade persist operations for entity: Opel. 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"}).
and at least 2 other types of exceptions which might seem totally irrelevant at first.Apparently when fetching a result from the database and it "as-is" in your session things get really messy, specially if the entity has relations to other entities (which was my case). Pay big attention if you have entity relationships - they might need to be "refreshed" if you start getting strange exceptions.
There are a couple of ways to overcome this issue. One of which is to use the data sent via the form (as @flec suggested) by using
$myForm->getData()
. This approach might work well for you, but unfortunately it was not the case with me (too complex to explain).What I ended up doing was implementing the
\Serializable
in the entity. I also created a method called__toArray()
which converted my entity into an array. What data you return in the__toArray()
method is totally up to you and your business logic. The array data is stored into the session and you use it to re-create a fresh object with all necessary relations.Hope this helps somebody.