【问题标题】:Updating denormalised values on postFlush / preFlush?更新 postFlush / preFlush 上的非规范化值?
【发布时间】:2013-11-17 03:30:56
【问题描述】:

我正在尝试找出在数据库中更新非规范化值的最佳过程。

场景如下:

Scenario: Update Activity Duration
    Given the following Task:
    | id | name   | duration
    | 1  | Task 1 | 0
    And the following Activity:
    | id | name       | startedAt        | endedAt | task |
    | 1  | Activity 1 | 2013-01-01 12:00 | null    | 1    |
    When I set "Activity 1" endedAt to "2013-01-01 13:00"
    And persist "Activity 1"
    Then "Activity 1" duration should be 3600
    And "Task 1" duration should be 3600

从数据结构可以看出,Task.duration 是 Activity.duration 的反规范化总和。

我正在苦苦挣扎的是何时更新 Activity.duration 和 Task.duration。

Activity.duration 可以在 setEndedAt 方法中计算。

Task.duration 有点棘手。我想当 Activity 被修改时,我需要在 postFlush 事件中重新计算 Task.duration。我假设它必须是 postFlush 因为我想对数据库中的活动持续时间求和,而不是获取所有 Task.acitivities 并在 PHP 中循环它们。

此外,我希望能够一次更新多个活动(想想查看/编辑时间表)。在这种情况下,我认为我需要推迟刷新,以便工作单元包含所有修改后的活动。

文档实际上说:

postFlush 在 EntityManager#flush() 结束时调用。 EntityManager#flush() 不能在其侦听器中安全地调用。

这可能会完全排除这种方法。

建议的解决方案看起来有效吗?我看不到可以在 preFlush 或 onFlush 中完成此操作的方法,因为持续时间尚未保存到数据库中。

干杯李维

【问题讨论】:

    标签: symfony doctrine-orm


    【解决方案1】:

    您可能会使用 onFlushpreUpdate 来实现此功能,但我觉得这种方法会不必要地将您的域对象与 Doctrine 耦合。最好的解决方案是根本不使用 Doctrine 的事件。

    在更改ActivitystartedAtendedAt 值之前,您可以从Task 中减去之前的持续时间,然后重新添加。

    class Activity
    {
        public function setStartedAt(\DateTime $startedAt)
        {
            $oldDuration = $this->getDuration();
    
            $this->startedAt = $startedAt;
    
            $this->updateTaskDuration($oldDuration);
        }
    
        public function setEndedAt(\DateTime $endedAt)
        {
            $oldDuration = $this->getDuration();
    
            $this->endedAt = $endedAt;
    
            $this->updateTaskDuration($oldDuration);
        }
    
        private function updateTaskDuration($oldDuration)
        {
            $newDuration = $this->getDuration();
            $difference  = $newDuration - $oldDuration;
    
            $this->task->addDuration($difference);
        }   
    }
    

    当然,考虑到各种情况(没有开始时间、将活动从一项任务转移到另一项等),实际实现必须稍微复杂一些。

    【讨论】:

    • 我看到的唯一问题是 setStartedAt 和 setEndedAt 在 Activity 分配给 Task 之前被调用,这可能在表单绑定 (symfony2) 期间发生。我唯一可以保证设置任务的时间是在持久/更新事件期间。
    • @LeeviGraham,理论上您可以仅在设置 Task 对象后简单地更新持续时间。正如我所说,实际的解决方案会稍微复杂一些,但完全可行。
    • 感谢您迄今为止的反馈。我是否应该担心 Activity 正在修改任务并直接告诉它要做什么。它是“拥有方”,但可以说任务发生变化并且不再有持续时间。最好从 Activity 中触发一个事件,告诉听众某些事情发生了变化:notify change tracking。同样,这会将实现与 Doctrine 联系起来。
    • @LeeviGraham,您仍然需要考虑使用侦听器方法的所有边缘情况(如果触发事件,但没有任务并且稍后将活动添加到任务中会发生什么)作为再加上这个用例感觉有点过度设计。简单地广泛测试您的实体,您应该没问题。如果您担心在不久的将来会发生这种变化,您可以创建一个抽象活动并将所有持续时间更改逻辑封装在其中。
    猜你喜欢
    • 1970-01-01
    • 2016-01-31
    • 2017-06-17
    • 2023-03-13
    • 2011-09-24
    • 2016-10-22
    • 2017-03-01
    • 2017-10-23
    • 2013-08-21
    相关资源
    最近更新 更多