Symfony 2.0 在实体内部获取服务
Posted
技术标签:
【中文标题】Symfony 2.0 在实体内部获取服务【英文标题】:Symfony 2.0 getting service inside entity 【发布时间】:2012-05-07 00:36:38 【问题描述】:我正在搜索并找不到答案。 我的应用程序中有数据库角色模型。用户可以有一个角色,但这个角色必须存储到数据库中。
但是用户需要从数据库中添加默认角色。所以我创建了一个服务:
<?php
namespace Alef\UserBundle\Service;
use Alef\UserBundle\Entity\Role;
/**
* Description of RoleService
*
* @author oracle
*/
class RoleService
const ENTITY_NAME = 'AlefUserBundle:Role';
private $em;
public function __construct(EntityManager $em)
$this->em = $em;
public function findAll()
return $this->em->getRepository(self::ENTITY_NAME)->findAll();
public function create(User $user)
// possibly validation here
$this->em->persist($user);
$this->em->flush($user);
public function addRole($name, $role)
if (($newrole = findRoleByRole($role)) != null)
return $newrole;
if (($newrole = findRoleByName($name)) != null)
return $newrole;
//there is no existing role
$newrole = new Role();
$newrole->setName($name);
$newrole->setRole($role);
$em->persist($newrole);
$em->flush();
return $newrole;
public function getRoleByName($name)
return $this->em->getRepository(self::ENTITY_NAME)->findBy(array('name' => $name));
public function getRoleByRole($role)
return $this->em->getRepository(self::ENTITY_NAME)->findBy(array('role' => $role));
我的services.yml
是:
alef.role_service:
class: Alef\UserBundle\Service\RoleService
arguments: [%doctrine.orm.entity_manager%]
现在我想在两个地方使用它:
UserController
和 User
实体。我怎样才能让它们进入实体?
至于控制器,我想我只需要:
$this->get('alef.role_service');
但是如何在实体内部获取服务?
【问题讨论】:
【参考方案1】:你没有。这是一个很常见的问题。实体应该只知道其他实体,而不是实体管理器或其他高级服务。过渡到这种开发方式可能有点挑战,但通常是值得的。
您要做的是在加载用户时加载角色。通常,您最终会得到一个执行此类操作的 UserProvider。您是否阅读过有关安全的部分?这应该是你的起点:
http://symfony.com/doc/current/book/security.html
【讨论】:
【参考方案2】:首先将服务引入实体如此困难的原因在于,Symfony 的明确设计意图是永远不应该在实体内部使用服务。因此,最佳实践答案是重新设计您的应用程序,使其不需要在实体中使用服务。
但是,我发现是一种不涉及弄乱全局内核的方法。
Doctrine 实体具有生命周期事件,您可以将事件侦听器挂钩到,请参阅http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#lifecycle-events 为了示例,我将使用 postLoad,它会在实体创建后立即触发。
EventListeners可以作为你注入其他服务的服务。
添加到 app/config/config.yml:
services:
example.listener:
class: Alef\UserBundle\EventListener\ExampleListener
arguments:
- '@alef.role_service'
tags:
- name: doctrine.event_listener, event: postLoad
添加到您的实体:
use Alef\UserBundle\Service\RoleService;
private $roleService;
public function setRoleService(RoleService $roleService)
$this->roleService = $roleService;
并添加新的 EventListener:
namespace Alef\UserBundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Alef\UserBundle\Service\RoleService;
class ExampleListener
private $roleService;
public function __construct(RoleService $roleService)
$this->roleService = $roleService;
public function postLoad(LifecycleEventArgs $args)
$entity = $args->getEntity();
if(method_exists($entity, 'setRoleService'))
$entity->setRoleService($this->roleService);
请记住,此解决方案附带一个警告,即这仍然是一种快速而肮脏的方式,实际上您应该考虑以正确的方式重新设计您的应用程序。
【讨论】:
这是问题的最佳解决方案和最准确的答案。我讨厌“你不能”、“这是错误的设计模式”或“它会毁掉一切,你这个白痴!”这样的回答。由什么都没有的大师写的。 “更糟糕的是(有时)更好”;)再次感谢。 可悲的是,“错误的设计模式”、“MCV 模型”、“单独的业务层”等。当“老板”想要“立即”完成事情时,它们就消失了。感谢@Kai 的回答,这正是我想要的。 我不喜欢EventListener
的情况是它会为您拥有的每个实体调用。
我认为应该选择这个答案
关于不喜欢这个答案的评论,因为你拥有的每个实体都会调用事件监听器,我还要指出你已经违背了 Symfony 在实体内部提供服务的意图。真正的答案是,如果您希望以正确的方式完成,则重新设计您的应用程序以不使用实体中的服务。这只是一个快速而肮脏的解决方案,这是我发现的最好的方法来做你不应该做的事情。【参考方案3】:
感谢上面的Kai's answer 回答了这个问题,但它与 symfony 5.x 不兼容。
准确地说这是一种不好的做法,但在某些特殊情况下是必需的,例如遗留代码或糟糕的数据库设计(作为架构迁移之前的临时解决方案)
就我而言,我将此代码与邮件程序和翻译器一起使用,如果 Symfony >= 5.3 会引入私有属性的问题,因此这里是 symfony 最新版本的解决方案:
在 config/services.yaml 中:
services:
Alef\UserBundle\EventListener\ExampleListener:
tags:
- name: doctrine.event_listener, event: postLoad
示例监听器:
namespace Alef\UserBundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Alef\UserBundle\Entity\Role;
class ExampleListener
public function postLoad(LifecycleEventArgs $postLoad): void
$entity = $postLoad->getEntity();
if ($entity instanceof User)
$repository = ;
$entity->roleRepository(
$postLoad->getEntityManager()->getRepository(Role::class)
);
并且在您的实体中(或者如果您在多个实体中使用它,则在特征中):
use Alef\UserBundle\Service\RoleService;
/** @internal bridge for legacy schema */
public function roleRepository(?RoleRepository $repository = null)
static $roleRepository;
if (null !== $repository)
$roleRepository = $repository;
return $roleRepository;
public function getRoleByName($name)
return $this->roleRepository()->findBy(array('name' => $name));
【讨论】:
以上是关于Symfony 2.0 在实体内部获取服务的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Symfony 2.0 AJAX 应用程序中将 Doctrine 实体编码为 JSON?
Symfony2:使用 FOSUserBundle 时如何在控制器内获取用户对象?