首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Doctrine2 ORM不会保存对DateTime字段的更改

Doctrine2 ORM不会保存对DateTime字段的更改
EN

Stack Overflow用户
提问于 2013-03-19 04:34:40
回答 3查看 7.8K关注 0票数 24

我有一个用户实体:

代码语言:javascript
复制
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;
    }
}

当我按如下方式更新用户的service_expires_at时,更新后的service_expires_at值是,而不是保存回数据库的

代码语言:javascript
复制
$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,更新后的值将被正确保存:

代码语言:javascript
复制
$date = $user->getServiceExpiresAt(); 

$date->modify('+10 days');

$user->setServiceExpiresAt(new \DateTime($date->format('Y-m-d'));

$em->persist($user);
$em->flush();

为什么会发生这种情况?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-03-19 06:31:54

ExampleBundle\Entity\User#getServiceExpiresAt()返回的DateTime实例是存储在实体本身中的相同对象,这破坏了encapsulation

UnitOfWork in Doctrine ORM applies strict comparison for changesets,这基本上意味着在包含对象的实体的属性的情况下,如果对象实例没有更改,则不会检测到更改。

在严格的比较中,以下是正确的:

代码语言:javascript
复制
$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来快速解决这个问题,这样就不会在对象之外共享原始实例,如以下示例所示:

代码语言:javascript
复制
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的问题。

此外,请注意,这修复了您的逻辑中可能的泄漏。例如,以下代码有buggy且难以调试(当应用当前损坏的getter/setter时):

代码语言:javascript
复制
$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部分是什么,请不要破坏封装。

票数 86
EN

Stack Overflow用户

发布于 2014-07-30 17:10:18

考虑对日期/时间属性使用DateTimeImmutable类。因此,请注意,DateTimeImmutable不是DateTime的实例。

票数 1
EN

Stack Overflow用户

发布于 2014-01-23 16:42:53

当我试图插入一个具有过去日期的实体时,我也遇到了完全相同的问题(我也试图将一个旧数据库迁移到包含它的数据的新模式)。

我试图用setter和getter来克隆这个对象,但是没有用。原则2保存当前日期。已检查架构,字段为日期时间而不是时间戳,默认为null。

这怎么可能呢?

编辑:

请原谅我的疏忽,我的同事dev添加了一个prePersist事件:

代码语言:javascript
复制
/**
 * @ORM\PrePersist
 */
function onPrePersist() {
    $this->created_at = new \DateTime('now');
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15486402

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档