Symfony2 prePersist / preUpdate Listener Notice

时间:2012-09-27 14:39:26

标签: doctrine

使用Doctrine / Symfony2 prePersist / preUpdate Listeners时出现问题。

ManyToOne关系中有两个实体(一个位置可以被许多人引用)。我构建了一个Form,在那里我做了一些AJAX-Stuff来帮助用户为已经存在的新Person选择一个Location。因此,我使用的是“entity_id”Form-Field。 我还想让用户为数据库中不存在的新Person创建新位置。这由表单中的第二个字段处理,用户可以在其中插入新位置的名称。 当持久化Person的实体时,我正在检查数据库中是否存在引用的位置。如果没有,我正在生成一个新的Location-Entity。这是(简而言之)我在实体级人员中的prePersist-Lifecyclecallback:

public function prePersist() {
   if($this->ort == null) 
       $this->ort = new Ort($this->ortsname);
}

当我创建一个新Person时,这非常有效。问题是更新程序。因此,当之前有一个位置连接到该人,并且我想通过相同的过程(使用preUpdate-Listener)创建一个新位置时,我收到一条通知,如:

Notice: Undefined index: 000000004a0010250000000058426bec in ...Doctrine/ORM/UnitOfWork.php line 983

我不知道如何解决这个问题。我认为必须使用Location-Object,这是在引用Person对象之前,但我不知道如何告诉Entity-Manager,引用Person的Location-Object是一个新的Entity。 我也尝试使用Listener-Class,如:

if( $entity instanceof Person) {
    if( $entity->getLocation() == null ) {
         $entity->setLocation( new Location( $entity->getLocatioName() );
         $em->persist($entity->getLocation());
         $em->flush();
    }
}

$ em-> persist ...东西应该无关紧要,因为在映射中启用了“cascade = {persist}”表示法。

1 个答案:

答案 0 :(得分:2)

我认为你会遇到preUpdate的问题。有两件事意味着preUpdate不是这种情况下最好的事件处理程序:

  • 刷新操作无法识别对已传递实体的关联的更改。
  • 强烈建议不要对EntityManager#persist()或EntityManager #remove()进行任何调用,即使与UnitOfWork API结合使用也不会在刷新操作之外按预期工作。

(这两点取自本节的底部:http://docs.doctrine-project.org/en/2.0.x/reference/events.html#preupdate

此外,当在Doctrine的刷新操作中调用preUpdate时,再次调用flush会导致问题。

因此,我建议您改为使用onFlushhttp://docs.doctrine-project.org/en/2.0.x/reference/events.html#onflush

如果你这样做,你需要告诉Doctrine,一旦你为它添加了位置,Person实体就已经改变了。获取更改的Person对象本身也稍微复杂一些。在onFlush处理程序中尝试这样的事情:

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

    foreach ($uow->getScheduledEntityUpdates() AS $entity) {
        if ($entity instanceof Person) {
            if ($entity->getLocation() == null ) {
                $entity->setLocation( new Location( $entity->getLocationName() ));

                // have to tell Doctrine that the Location has been updated
                $cmf = $args->getEntityManager()->getMetadataFactory();
                $class = $cmf->getMetadataFor(get_class($entity));
                $args->getEntityManager()->getUnitOfWork()->computeChangeSet($class, $entity); // don't use recomputeSingleEntityChangeSet here - that doesn't care about relationships
            }
        }
    }
}