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

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

问题描述

我有一个用户实体:

use DoctrineORMMapping as ORM;

/**
 * ExampleBundleEntityUser
 *
 * @ORMEntity()
 */
class User
{
    // ...

    /**
     * @ORMColumn(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不会保存回数据库:

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();

但是,如果我将新的 DateTime 对象传递给 service_expires_at,则更新后的值会正确保存:

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();

为什么会这样?

推荐答案

ExampleBundleEntityUser#getServiceExpiresAt() 返回的 DateTime 实例与存储的对象相同在实体本身中,这打破了封装.

The DateTime instances returned by ExampleBundleEntityUser#getServiceExpiresAt() are the same objects stored in the entity itself, which breaks encapsulation.

Doctrine ORM 中的 UnitOfWork 应用严格的比较变更集,这基本上意味着在包含对象的实体的属性的情况下,如果对象实例没有改变,ORM 不会检测到变化.

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.

严格比较,以下为真:

$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 来快速解决该错误,这样原始实例永远不会在您的对象之外共享,如下例所示:

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;
}

这也将解决您的 Doctrine ORM 问题.

This will also fix your problem with Doctrine ORM.

另外,请注意,这修复了您的逻辑中可能存在的漏洞.例如,以下代码有问题且难以调试(在应用当前损坏的 getter/setter 时):

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');

因此,无论问题中的 ORM 部分如何,请不要破坏封装.

So, regardless of the ORM part in the question, please don't break encapsulation.

这篇关于Doctrine2 ORM 不会保存对 DateTime 字段的更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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