在Symfony 2.5 preUpdate事件监听器中添加新的OneToMany关联实体

时间:2014-10-13 19:25:26

标签: symfony doctrine-orm

使用Symfony 2.5,Doctrine documentation表示preUpdate事件监听器是最严格的。您不能只对实体进行更改,而是必须使用特殊功能:

$entity->setNewValue($fieldName, $value)

但是,我有一个实体$view,它有一个到多个关联实体$routing,我在$view对象中有一个特殊的setter,它添加了多个路由:

class View { 

    protected $routing;

    public function getRouting() {
        return $this->routing;
    }

    public function addRouting(\Gutensite\CmsBundle\Entity\Routing\Routing $routing)
    {
        // Maintain Relationship
        $routing->setView($this);
        $this->routing[] = $routing;
        return $this;
    }
}

我在该实体中没有setRouting()函数,因为我有一个addRouting(),因为它是一个多oneToMany个关联。

所以...在我的preUpdate事件监听器中,我应该如何创建一个新的“默认”路由,并将其添加到视图中,因为$entity->setNewValue()函数不能完全适用于集合,并且显然我不能只调用$view->addRouting($route)(我试过了,但没有任何东西被持久化到数据库中)。

注意:从2.4开始我们应该使用LifeCycleEventArgs而不是PreUpdateEventArgs(evidently)。

public function preUpdate(LifeCycleEventArgs $eventArgs) {

    $entity = $eventArgs->getEntity();
    $em = $eventArgs->getEntityManager();

    if($entity instanceof View) {
        if(!$entity->getRouting() || !$entity->getRouting()[0]) {
            $routing = new Routing();
            // simplified code to Set Friendly URL
            $routing->setFurl('Unknown-Friendly-URL-'.$entity->getId());
            // Set Default Values for Route
            $routing->setFlagPrimary(1);
            $routing->setTime(time());
            $routing->setTimeMod(time());
            $routing->setStatus(1);
            $routing->setView($entity);

            // Add the route to the View Entity
            $entity->addRouting($routing);

            // Recalculate Entity Changes (not working)
            $uow = $em->getUnitOfWork();
            // This doesn't work whether I compute the changesets for $entity (which is $view) 
            // or if I calculate changes for $entity->getRouting() (which is the new route I'm adding).
            $classMetadata = $em->getClassMetadata(get_class($entity));
            // Recomputer Entire Change Set Because it's a New Record (I'm not sure this is necessary or not)
            $uow->computeChangeSet($classMetadata, $entity);
            // This doesn't work either
            $uow->recomputeSingleEntityChangeSet($classMetadata, $entity);

        }
    }
}

2 个答案:

答案 0 :(得分:2)

我最终只是在onFlush事件中实现了这一点。 preUpdate对我的用例来说限制太多了。所以这就引出了一个问题:为什么在onFlush更灵活时使用preUpdate?

preUpdate

的好处

将简单值插入实体,例如

$eventArgs->setNewValue('myField', $value);

您还可以访问实体的旧值和新值(这对于轻松比较更改的WHICH字段非常有用,这是我以前需要的。)

  

这个事件有一个强大的功能,但它是用一个执行的   PreUpdateEventArgs实例,其中包含对该引用的引用   计算此实体的变更集。这意味着您可以访问所有人   已为此实体更改过的旧字段和新字段   值。    - reference

注意:如果您确实使用了preUpdate,请记住您必须使用setNewValue()语法,而不是实体的常规设置器。无需重新计算更改集,您还 should NOT call persist

  

任何对EntityManager的调用#persist()或EntityManager #remove(),甚至   强烈建议不要与UnitOfWork API结合使用   在刷新操作之外不能按预期工作。

preUpdate

的问题
  

永远不允许对更新后的实体的关联进行更改   这个事件,因为Doctrine无法保证正确处理   刷新操作此时的参照完整性。    - reference

因此,如果您需要更新子级或父级值(例如更新父级的修改时间),则无法在preUpdate中执行此操作,因为在eventArgs上调用了setNewValue方法当前实体,而不是特定实体。

或者如果你需要添加一个集合,就像在我的问题中一样,你不能调用像addRouting这样的自定义设置器(由于我提到的原因)。我怀疑这意味着@ RomaKliuchko的建议是行不通的。

请添加其他优点和缺点。

答案 1 :(得分:0)

您是否尝试过实施setRouting(ArrayCollection $routing)方法,该方法将覆盖现有的集合并执行如下操作:

$routings = $entity->getRouting();
// add entities to routing collection if needed
$eventArgs->setNewValue('routing', $routings);