Doctrine2 ORM不保存对DateTime字段的更改 [英] Doctrine2 ORM does not save changes to a DateTime field

查看:119
本文介绍了Doctrine2 ORM不保存对DateTime字段的更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用户实体:

  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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆