5

我正在使用 Doctrine 来保存用户数据,并且我想要一个last modification字段。这是用户按下后我希望如何保存表单的伪代码Save

  • 开始交易
  • 做了很多事情,可能是查询数据库,也可能不是
  • 如果此交易有任何改变
    • 修改last updated字段
  • 提交事务

有问题的部分是if anything will be changed by this transaction。Doctrine 可以给我这样的信息吗?

如何判断当前交易中的实体是否发生了变化?

编辑

只是为了澄清问题,我正在尝试修改一个lastUpdated在实体中调用的字段,User如果任何实体(包括但不限于User)将在提交当前事务后更改。换句话说,如果我开始一个事务并修改一个名为nbCars的实体的字段Garage,我希望更新该实体的lastUpdated字段,User即使该实体没有被修改。

4

4 回答 4

6

这是一个必要的回复,旨在纠正@ColinMorelli 发布的内容(因为不允许在生命周期事件侦听器中刷新 - 是的,文档中有一个位置另有说明,但我们将摆脱它,所以请不要去做吧!)。

您可以简单地onFlush使用如下监听器收听:

use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Events;

class UpdateUserEventSubscriber implements EventSubscriber
{
    protected $user;

    public function __construct(User $user)
    {
        // assuming the user is managed here
        $this->user = $user;
    }

    public function onFlush(OnFlushEventArgs $args)
    {
        $em  = $args->getEntityManager();
        $uow = $em->getUnitOfWork();

        // before you ask, `(bool) array()` with empty array is `false`
        if (
            $uow->getScheduledEntityInsertions()
            || $uow->getScheduledEntityUpdates()
            || $uow->getScheduledEntityDeletions()
            || $uow->getScheduledCollectionUpdates()
            || $uow->getScheduledCollectionDeletions()
        ) {
            // update the user here
            $this->user->setLastModifiedDate(new DateTime());
            $uow->recomputeSingleEntityChangeSet(
                $em->getClassMetadata(get_class($this->user)), 
                $this->user
            );
        }
    }

    public function getSubscribedEvents()
    {
        return array(Events::onFlush);
    }
}

User只有当包含要提交到数据库的更改时,这才会将更改应用于配置的对象UnitOfWork(工作单元实际上是您可能定义为应用程序级状态事务的内容)。

您可以随时通过调用向 ORM 注册此订阅者

$user         = $entityManager->find('User', 123);
$eventManager = $entityManager->getEventManager();
$subscriber   = new UpdateUserEventSubscriber($user);

$eventManager->addEventSubscriber($subscriber);
于 2013-03-13T00:00:29.913 回答
2

很抱歉一开始给您错误的答案,这应该会引导您朝着正确的方向前进(请注意,它并不完美)。

您需要实现两个事件。一个监听 OnFlush 事件,行为如下:

// This should listen to OnFlush events
public function updateLastModifiedTime(OnFlushEventArgs $event) {
    $entity = $event->getEntity();
    $entityManager = $event->getEntityManager();
    $unitOfWork = $entityManager->getUnitOfWork();

    if (count($unitOfWork->getScheduledEntityInsertions()) > 0 || count($unitOfWork->getScheduledEntityUpdates()) > 0) {
        // update the user here
        $this->user->setLastModifiedDate(new \DateTime());
    }
}

我们需要等待 OnFlush 事件,因为这是我们访问所有将要完成的工作的唯一机会。请注意,我没有在上面包含它,但是$unitOfWork->getScheduledEntityDeletions()如果您想跟踪它,也有。

接下来,您需要另一个监听 PostFlush 事件的最终事件监听器,如下所示:

// This should listen to PostFlush events
public function writeLastUserUpdate(PostFlushEventArgs $event) {
    $entityManager = $event->getEntityManager();
    $entityManager->persist($this->user);
    $entityManager->flush($this->user);
}

不幸的是,一旦交易开始,就为时已晚,无法获得拯救另一个实体的教义。因此,我们可以在 OnFlush 处理程序中对对象的字段进行更新User,但实际上我们不能将其保存在那里。(您可能可以找到一种方法来做到这一点,但 Doctrine 不支持它,并且必须使用 UnitOfWork 的一些受保护的 API)。

但是,一旦事务完成,您可以立即执行另一个快速事务来更新用户的日期时间。是的,这确实有一个不幸的副作用,即不在单个事务中执行。

于 2013-03-07T21:00:42.850 回答
1

与其他 2 个答案类似,我的猜测是,在有或不会发生变化时说出你想做的任何事情,使用事件侦听器。

但是如果你只想在交易开始之前Doctrine_Record::getModified()知道,你可以使用(链接)。

于 2013-03-07T21:07:34.630 回答
1

@PreUpdate如果实体没有更改,则不会调用事件。

于 2013-03-07T21:00:49.053 回答