Doctrine2 ORM不保存对DateTime字段的更改 [英] Doctrine2 ORM does not save changes to a DateTime field
问题描述
我有一个用户实体:
use Doctrine \ ORM\映射为ORM;
/ **
* ExampleBundle\Entity\User
*
* @ ORM\Entity()
* /
用户
{
// ...
/ **
* @ 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;
}
}
当我更新用户的 service_expires_at
如下,更新的 service_expires_at
值 NOT 保存回数据库:
$ date = $ user-> getServiceExpiresAt();
var_dump($ date-> format('Y-m-d')); // 2013-03-08
$ date-> modify('+ 10 days');
var_dump($ date-> format('Y-m-d')); // 2013-03-18
$ user-> setServiceExpiresAt($ date);
$ em-> persist($ user);
$ em-> flush();
但是,如果我传递一个新的 DateTime
对象到 service_expires_at
,更新的值被正确保存:
$ date = $ user-> getServiceExpiresAt();
$ date-> modify('+ 10 days');
$ user-> setServiceExpiresAt(new \DateTime($ date-> format('Ym-d'));
$ em-> persist $ user $;
$ em-> flush();
?
DateTime
ExampleBundle\Entity\User#getServiceExpiresAt()是存储在实体本身中的相同对象,它打破了封装。
在Doctrine ORM中的UnOWork工作ORM对变更集应用严格的比较,这基本上意味着在包含对象的实体的属性的情况下,如果对象实例hasn' t更改,ORM不会检测到更改。
严格比较,以下是真实的:
$ 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
这是OOP编程中新手的一个非常常见的错误,可以快速解决通过修复您的getter和setter,使原始实例永远不会在对象之外共享,如下例所示:
public function getServiceExpiresAt()
{
return clone $ this-> service_expires_at;
}
public function setServiceExpiresAt(\DateTime $ service_expires_at)
{
$ this-> service_expires_at = clone $ service_expires_at;
}
这也将解决您的问题与Doctrine ORM。
此外,请注意,这会修复您的逻辑中可能的泄漏。例如,以下代码是错误的,很难调试(在应用当前破坏的getter / setter时):
$ bankTransaction1 = $ someService-> getTransaction(1);
$ bankTransaction2 = $ someService-> getTransaction(2);
// leak!现在两个对象引用相同的DateTime实例!
$ bankTransaction2-> setDateTime($ bankTransaction1> getDateTime());
// bug!现在你的对象都被修改了!
$ bankTransaction1> getDateTime() - > modify('+ 1天');
所以,不管问题中的ORM部分如何,请不要打破封装。 >
I have a User entity:
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;
}
}
When i update the User's service_expires_at
as following, the updated service_expires_at
value is NOT saved back into the database:
$date = $user->getServiceExpiresAt();
var_dump($date->format('Y-m-d')); // 2013-03-08
$date->modify('+10 days');
var_dump($date->format('Y-m-d')); // 2013-03-18
$user->setServiceExpiresAt($date);
$em->persist($user);
$em->flush();
However if i pass a new DateTime
object to service_expires_at
, the updated value is saved correctly:
$date = $user->getServiceExpiresAt();
$date->modify('+10 days');
$user->setServiceExpiresAt(new \DateTime($date->format('Y-m-d'));
$em->persist($user);
$em->flush();
Why is this happening?
The DateTime
instances returned by ExampleBundle\Entity\User#getServiceExpiresAt()
are the same objects stored in the entity itself, which breaks encapsulation.
The UnitOfWork in Doctrine ORM applies strict comparison for changesets, which basically means that in the case of properties of entities containing objects, if the object instance hasn't changed, the ORM does not detect a change.
In strict comparison, following is true:
$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
This is a very common mistake among newcomers in OOP programming, and it can be solved quickly by fixing your getters and setters so that the original instance is never shared outside of your object, like in following example:
public function getServiceExpiresAt()
{
return clone $this->service_expires_at;
}
public function setServiceExpiresAt(\DateTime $service_expires_at)
{
$this->service_expires_at = clone $service_expires_at;
}
This will also fix your problem with Doctrine ORM.
Also, please note that this fixes possible leaks in your logic. For example, following code is buggy and hard to debug (when applying your currently broken getters/setters):
$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');
So, regardless of the ORM part in the question, please don't break encapsulation.
这篇关于Doctrine2 ORM不保存对DateTime字段的更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!