如何从实体获取存储库?

21

我有一个名为Game的实体,与之相关的存储库叫做GameRepository:

/**
 * @ORM\Entity(repositoryClass="...\GameRepository")
 * @ORM\HasLifecycleCallbacks()
 */
class Game {
    /**
     * @ORM\prePersist
     */
    public function setSlugValue() {
        $this->slug = $repo->createUniqueSlugForGame();
    }
}

在 prePersist 方法中,我需要确保 Game 的 slug 字段是唯一的,这需要进行数据库查询。要执行查询,我需要访问 EntityManager。我可以从 GameRepository 中获取该 EntityManager。所以:如何从 Game 中获取 GameRespository?

为什么不使用单独的服务来完成这个任务? - Nico Haase
4个回答

66
你实际上可以在实体中获取存储库,只需在生命周期回调期间进行。你离成功很近了,你只需要接收"LifecycleEventArgs"参数。
还可以参考http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html
use Doctrine\ORM\Event\PrePersistEventArgs;

/**
 * @ORM\Entity(repositoryClass="...\GameRepository")
 * @ORM\HasLifecycleCallbacks()
 */
class Game {
    /**
     * @ORM\prePersist
     */
    public function setSlugValue( PrePersistEventArgs $event ) {
        $entityManager = $event->getEntityManager();
        $repository    = $entityManager->getRepository( get_class($this) );
        
        $this->slug = $repository->createUniqueSlugForGame();
    }
}

PS. 我知道这是一个老问题,但我回答它是为了帮助未来的谷歌搜索者。

6
我很高兴你把它复活了 - 我是那些未来的谷歌员工之一。实际上,我已经在一个生命周期回调函数内工作,并且我对象的持久化需要创建或获取一个相关的对象。 - nealio82
3
在我看来(使用Symfony 2.4),我认为这将是被接受的答案! - timmz
这样做完全没问题,还是不好的实践? - TrtG
1
一般来说,你希望实体中的逻辑尽可能少。因此,我会将其移动到单独的事件监听器/订阅者中。请查看文档:http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html - Maurice
@Maurice,谢谢。但如果我理解正确,这份文档是用于创建通用监听器,以在每个 prepersist 操作上触发...因此,在继续之前,我们必须检查当前实体的类型。那可能会导致一些巨大的“switch”检查,以查看多个实体是否有 prepersist 操作...这真的是更好的实现方式还是我漏掉了什么? - TrtG
2
@TrtG 您可以为每个操作实体创建一个监听器。并以 if (!$entity instanceof SomeEntity::class){return;} 开始您的监听器方法。如果您需要为另一个实体监听相同的事件,只需创建另一个监听器即可。我会为每个监听器分配一个职责。 - Maurice

10
您不需要这样做。Doctrine 2中的实体应该“不知道”实体管理器或存储库。
解决您提出的情况的典型方法是向存储库(或服务类)添加一个方法,用于创建(或调用存储)新实例,并生成唯一的slug值。

3
为什么他们选择那样做?这似乎让所有的事情变得更加困难和不够DRY(不重复性的代码原则)。 - Jason Swett
@JasonSwett 是的,slug大小写的问题有点让人烦恼。在其他大多数情况下,分离会带来更少的不良影响,例如,在测试内容时如果没有棘手的依赖关系需要配置,则会更容易一些。 - Jani Hartikainen
2
仓储库中的一个方法是“创建新实例”...真的吗?仓储库不只是用来获取数据的吗? - Jean

4
你可以使用JMSDiExtraBundle将Doctrine实体管理器注入到你的实体中,使得你的repository看起来像这样:

/**
 * @InjectParams({
 *     "em" = @Inject("doctrine.orm.entity_manager")
 * })
 */
    public function setInitialStatus(\Doctrine\ORM\EntityManager $em) {


    $obj = $em->getRepository('AcmeSampleBundle:User')->functionInRepository();
    //...
}

看到这个:http://jmsyst.com/bundles/JMSDiExtraBundle/1.1/annotations


1
为了保持逻辑的封装性,而不必更改保存实体的方式,您需要查看使用更强大的Doctrine事件,而不是简单的prePersist生命周期事件,这些事件可以访问更多的内容,而不仅仅是实体本身。
您可能需要查看DoctrineSluggableBundleStofDoctrineExtensionsBundle捆绑包,它们可能正好符合您的需求。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接