5
votes

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
        }
}
1
What is your $subscription->getPaymentValidUntil() ?Michael Sivolobov
it's returning a datetime i'm modifying. i'm adding plus one month.. As you ca&n see from my dump, it's set to 12/05 before the modification, to 12/06 after, but nothing is sheduled for update.Sébastien
Why do you fetch getScheduledEntityInsertions while you need getScheduledEntityUpdates?Michael Sivolobov
yes, sorry, the same with update, I tried what I could, it's empty too. I'll correct thisSébastien
Post your entity code and mapping info (if it is not in annotations).Michael Sivolobov

1 Answers

5
votes

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.