2017-02-13 39 views
1

我有一些控制器定义为服务,我需要从路由名称中获得我的控制器的类名。Symfony2:从服务名称或路由对象获取控制器作为服务类名称

对于非服务控制器我可以得到路由集合与路由器服务:

$route = $this->router->getRouteCollection()->get($routeName); 
//Retrieve an object like that: 

Route { 
    -path: "/admin/dashboard" 
    -host: "" 
    -schemes: [] 
    -methods: [] 
    -defaults: array:1 [ 
    "_controller" => "AppBundle\Controller\Admin\AdminController::dashboardAction" 
    ] 
    -requirements: [] 
    -options: array:1 [] 
    -compiled: null 
    -condition: "" 
} 

我可以$route["defaults"]["_controller"]访问控制器类名,所以这是很好。

的问题是我的控制器作为服务的_controller属性是服务,而不是控制器类的名称(如app.controller.admin.user:listAction)我有服务的名字,但我需要有类名(AppBundle\Controller\Admin\UserController

我想出的唯一解决方案是从Container获得服务,并在服务上使用get_class(),但它只会对检索控制器/服务的类有巨大的性能影响。

有没有其他解决方案?

+0

我相信没有任何其他的选择可以更高效。你需要怎么处理类名? – Gerry

+0

我想重现本教程:https://www.trisoft.ro/blog/6-symfony2-advanced-menus我需要className才能读取元数据:$ this-> metadataReader-> loadMetadataForClass(new \ ReflectionClass($类)); – iBadGamer

+0

我想你可以添加一个_controller_classname参数给你的路线。但是需要控制器类名称来生成菜单似乎不是理想的设计。 – Cerad

回答

0

按照https://github.com/FriendsOfSymfony/FOSUserBundle/issues/2751的建议,我实现了一个缓存映射,以便将路由名解析为控制器类和方法。

<?php 
// src/Cache/RouteClassMapWarmer.php 
namespace App\Cache; 

use Symfony\Component\Cache\Simple\PhpFilesCache; 
use Symfony\Component\DependencyInjection\ContainerInterface; 
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; 
use Symfony\Component\Routing\RouterInterface; 

class RouteClassMapWarmer implements CacheWarmerInterface 
{ 
    /** @var ContainerInterface */ 
    protected $container; 
    /** @var RouterInterface */ 
    protected $router; 

    public function __construct(ContainerInterface $container, RouterInterface $router) 
    { 
     $this->container = $container; 
     $this->router = $router; 
    } 

    public function warmUp($cacheDirectory) 
    { 
     $cache = new PhpFilesCache('route_class_map', 0, $cacheDirectory); 
     $controllers = []; 
     foreach ($this->router->getRouteCollection() as $routeName => $route) { 
      $controller = $route->getDefault('_controller'); 
      if (false === strpos($controller, '::')) { 
       list($controllerClass, $controllerMethod) = explode(':', $controller, 2); 
       // service_id gets resolved here 
       $controllerClass = get_class($this->container->get($controllerClass)); 
      } 
      else { 
       list($controllerClass, $controllerMethod) = explode('::', $controller, 2); 
      } 
      $controllers[$routeName] = ['class' => $controllerClass, 'method' => $controllerMethod]; 
     } 
     unset($controller); 
     unset($route); 
     $cache->set('route_class_map', $controllers); 
    } 

    public function isOptional() 
    { 
     return false; 
    } 
} 

而且在我RouteHelper,阅读本实施看起来是这样的

$cache = new PhpFilesCache('route_class_map', 0, $this->cacheDirectory); 
    $controllers = $cache->get('route_class_map'); 
    if (!isset($controllers[$routeName])) { 
     throw new CacheException('No entry for route ' . $routeName . ' forund in RouteClassMap cache, please warmup first.'); 
    } 

    if (null !== $securityAnnotation = $this->annotationReader->getMethodAnnotation((new \ReflectionClass($controllers[$routeName]['class']))->getMethod($controllers[$routeName]['method']), Security::class)) 
    { 
     return $this->securityExpressionHelper->evaluate($securityAnnotation->getExpression(), ['myParameter' => $myParameter]); 
    } 

这应该是比获得routeCollection快得多和解决的service_id:方法谱写_controller的属性对每个容器请求。