Doctrine2 ORM non salva le modifiche in un campo DateTime

Ho un’ quadro utente:

use Doctrine\ORM\Mapping as ORM; /** * ExampleBundle\Entity\User * * @ORM\Entity() */ class User { // ... /** * @ORM\Column(type="service_expires_at", type="date", nullable=true) */ private $service_expires_at; public function getServiceExpiresAt() { return $this->service_expires_at; } public function setServiceExpiresAt(\DateTime $service_expires_at) { $this->service_expires_at = $service_expires_at; } } 

Quando service_expires_at dell’utente come segue, il valore service_expires_at aggiornato NON viene salvato nel database:

 $date = $user->getServiceExpiresAt(); var_dump($date->format('Ym-d')); // 2013-03-08 $date->modify('+10 days'); var_dump($date->format('Ym-d')); // 2013-03-18 $user->setServiceExpiresAt($date); $em->persist($user); $em->flush(); 

Tuttavia se passo un nuovo object DateTime a service_expires_at , il valore aggiornato viene salvato correttamente:

 $date = $user->getServiceExpiresAt(); $date->modify('+10 days'); $user->setServiceExpiresAt(new \DateTime($date->format('Ym-d')); $em->persist($user); $em->flush(); 

Perché sta succedendo?

Le istanze DateTime restituite da ExampleBundle\Entity\User#getServiceExpiresAt() sono gli stessi oggetti memorizzati nell’ quadro stessa, che interrompe l’ incapsulamento .

UnitOfWork in Doctrine ORM applica un confronto rigoroso per i changeset , che in pratica significa che nel caso di proprietà di entity framework contenenti oggetti, se l’istanza dell’object non è stata modificata, l’ORM non rileva una modifica.

In stretto confronto, seguire è vero:

 $dateTime1 = new \DateTime('@0'); $dateTime2 = new \DateTime('@0'); $dateTime3 = $dateTime1; var_dump($dateTime1 !== $dateTime2); // true var_dump($dateTime1 === $dateTime3); // true $dateTime1->modify('+1 day'); var_dump($dateTime1 === $dateTime3); // true 

Questo è un errore molto comune tra i nuovi arrivati ​​nella programmazione OOP e può essere risolto rapidamente risolvendo getter e setter in modo che l’istanza originale non venga mai condivisa al di fuori dell’object, come nell’esempio seguente:

 public function getServiceExpiresAt() { return clone $this->service_expires_at; } public function setServiceExpiresAt(\DateTime $service_expires_at) { $this->service_expires_at = clone $service_expires_at; } 

Questo risolverà anche il tuo problema con Doctrine ORM.

Inoltre, tieni presente che questo risolve possibili perdite nella tua logica. Ad esempio, il codice seguente è bacato e difficile da eseguire il debug (quando si applicano i getter / setter correntemente corretti):

 $bankTransaction1 = $someService->getTransaction(1); $bankTransaction2 = $someService->getTransaction(2); // leak! Now both objects reference the same DateTime instance! $bankTransaction2->setDateTime($bankTransaction1->getDateTime()); // bug! now both your objects were modified! $bankTransaction1->getDateTime()->modify('+1 day'); 

Quindi, indipendentemente dalla parte ORM nella domanda, si prega di non interrompere l’incapsulamento.

Prendi in considerazione l’utilizzo della class DateTimeImmutable per le proprietà data / ora. Con ciò, nota che DateTimeImmutable non è un’istanza di DateTime .

Ho esattamente lo stesso problema quando sto cercando di inserire un’entity framework con una data passata (sto provando a migrare un vecchio database in un nuovo schema con i suoi dati).

Ho provato a clonare l’object sia in setter che in getter ed è inutile. Doctrine 2 salva la data corrente. Controllato lo schema, il campo è data e ora non stampata e il valore predefinito è null.

Come può essere?

MODIFICARE:

per favore scusate la mia mancanza di attenzione, il mio collega collega ha aggiunto un evento prePersist:

 /** * @ORM\PrePersist */ function onPrePersist() { $this->created_at = new \DateTime('now'); }