Symfony 2.0 在实体内部获取服务

2022-08-30 12:39:20

我叽叽喳喳我的应用程序中有数据库角色模型。用户可以具有角色,但此角色必须存储在数据库中。

但是,用户需要从数据库中添加默认角色。所以我创建了一个服务:

<?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%]

现在我想在两个地方使用它:和实体。我如何将它们放入实体中?至于控制器,我认为我只需要:UserControllerUser

$this->get('alef.role_service');

但是如何在实体内部获得服务呢?


答案 1

你没有。这是一个非常常见的问题。实体应该只了解其他实体,而不能了解实体管理器或其他高级服务。过渡到这种开发方式可能有点挑战,但这通常是值得的。

您要做的是在加载用户时加载角色。通常,您最终会得到一个执行此类操作的用户提供程序。您是否通读了有关安全性的部分?这应该是你的起点:

http://symfony.com/doc/current/book/security.html


答案 2

首先将服务放入实体之所以如此困难,是因为Symfony被明确设计为永远不应该在实体内部使用服务。因此,最佳做法的答案是重新设计应用程序,使其不需要在实体中使用服务。

但是,我发现有一种方法可以做到这一点,而不涉及弄乱全局内核。

Doctrine 实体具有 lifeCycle 事件,您可以将事件侦听器挂接到这些事件,请参阅 http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#lifecycle-events 在本例中,我将使用 postLoad,它在创建实体后不久触发。

事件列表可以作为服务,您将其他服务注入其中。

Add to 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);
         }
     }
}

请记住,此解决方案附带一个警告,即这仍然是快速而肮脏的方式,实际上您应该考虑以正确的方式重新设计应用程序。


推荐