Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

doctrine persisted entity changed between entityManager->persist and unitOfWork->persist

In my symfony2/doctrine2 application, I have a weird case where when persisting a modified entity, the change is not flushed to the database, and I can't understand why.

Below is my code :

$date = $subscription->getPaymentValidUntil()->format('d/m/Y');
$period = $payment->getDetail('period');
$validDate = $subscription->getPaymentValidUntil()
    ->add(new\DateInterval($period == Subscription::MONTH ? 'P1M' : 'P1Y'))
;

$subscription->setPaymentValidUntil($validDate);
$this->em->persist($subscription);

exit(var_dump(array(
    $date,
    $this->em->getUnitOfWork()->getScheduledEntityUpdates(),
    $subscription->getPaymentValidUntil(),
)));

$this->em->flush();

The output of the vardump is the following:

array (size=3)
  0 => string '12/05/2015' (length=10)
  1 => 
    array (size=0)
      empty
  2 => 
    object(DateTime)[2295]
      public 'date' => string '2015-06-12 18:52:37' (length=19)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Paris' (length=12)

If I flush before the vardump or remove the vardump, indeed, the date value does not change in my database. Why?

As you can see, I add one month to this date value, it's reflected in the entity, but it's not scheduled for update. How can I solve this ?

EDIT : I've digged into the entity manager persist function and I figured out that when dumping the entity in Doctrine\ORM\EntityManager::persist the date was good but when dumping it in Doctrine\ORM\UnitOfWork::persist, the date had somehow been reseted to its original one :

Doctrine\ORM\EntityManager::persist : dumps the updated entity

public function persist($entity)
{
    if ( ! is_object($entity)) {
        throw ORMInvalidArgumentException::invalidObject('EntityManager#persist()' , $entity);
    }

    $this->errorIfClosed();

    if ($entity instanceof Subscription) exit(var_dump($entity));

    $this->unitOfWork->persist($entity);
}

Doctrine\ORM\UnitOfWork::persist : dumps the non-modified entity : WHY ?

private function doPersist($entity, array &$visited)
{
    if ($entity instanceof Subscription) exit(var_dump($entity));

        $oid = spl_object_hash($entity);

        if (isset($visited[$oid])) {
            return; // Prevent infinite recursion
        }
}
like image 515
Sébastien Avatar asked Apr 12 '15 20:04

Sébastien


People also ask

What is doctrine entity manager?

Doctrine's public interface is through the EntityManager . This class provides access points to the complete lifecycle management for your entities, and transforms entities from and back to persistence. You have to configure and create it to use your entities with Doctrine ORM.

What is persist flush?

Persist and Flush​ flush() . em. persist(entity) is used to mark new entities for future persisting. It will make the entity managed by given EntityManager and once flush will be called, it will be written to the database.

How does Doctrine work?

Doctrine uses the Identity Map pattern to track objects. Whenever you fetch an object from the database, Doctrine will keep a reference to this object inside its UnitOfWork. The array holding all the entity references is two-levels deep and has the keys root entity name and id.

What is a repository doctrine?

It means the place where our data can be accessed from, a repository of data. This is to distinguish it from a database as a repository does not care how its data is stored.


1 Answers

DateTime are object after all, and are always passed by reference. So, I strongly suggest you to edit your code like this:

$period = $payment->getDetail('period');
$validDate = clone $subscription->getPaymentValidUntil();
$validDate->add(new \DateInterval($period == Subscription::MONTH ? 'P1M' : 'P1Y'));
$subscription->setPaymentValidUntil($validDate);
$this->em->persist($subscription);
$this->em->flush();

This way, you ensure that you're passing a different object to the entity, and avoid any subtle bug in the EntityManager.

like image 60
Alessandro Lai Avatar answered Oct 19 '22 11:10

Alessandro Lai