2016-05-20 78 views
2

我有许多控制器操作需要会话中的某些上下文,以便它们可以执行。如果不满足要求,重定向

/** 
* @Route("/some/route", name="some_route") 
*/ 
public function oneOfMyAction(Request $request) 
{ 
    if (!$request->getSession()->get('some_required_variable')) { 
     $this->redirectToRoute('some_other_route'); 
    } 

    return $this->render('AppBundle::protected-content.html.twig'); 
} 

有没有办法在注解或其他东西中分解这个需求,所以我可以很容易地在我的控制器中使用它?

/** 
* @Route("/some/route", name="some_route") 
* @SomeRequiredVariable() 
*/ 
public function oneOfMyAction(Request $request) 
{ 
    return $this->render('AppBundle::protected-content.html.twig'); 
} 

如果我可以实现一个SomeRequiredVariable类,我该怎么做呢?
还是会有另一种知道的方式?

回答

1

我一直发现有关自定义注释的文档有点欠缺。但希望以下将帮助你。

先决条件

  • 您需要标注为@Annotation类。对于annotion使用的所有参数,这将作为容器
  • 您需要一个适当的事件侦听器来查找并读取注释并按照相应的方式进行操作。

注解类

对于这个工作,你需要定义所有的属性,可以用来为您的自定义注释(这里route, route_params, required),作为类属性。

<?php 
// src/AppBundle/Annotation/RedirectOnMissing.php 
namespace AppBundle\Annotation; 

use Symfony\Component\OptionsResolver\OptionsResolver; 

/** 
* @Annotation 
*/ 
class RedirectOnMissing 
{ 
    /** 
    * @var string 
    */ 
    private $route; 

    /** 
    * @var array 
    */ 
    private $route_params = []; 

    /** 
    * @var array 
    */ 
    private $required = []; 

    /** 
    * @param array $options 
    */ 
    public function __construct(array $options) 
    { 
     $options = $this->configureOptions(new OptionsResolver())->resolve($options); 

     $this->route = $options['route']; 
     $this->route_params = $options['route_params']; 
     $this->required = $options['required']; 
    } 

    /** 
    * @param OptionsResolver $resolver 
    * 
    * @return OptionsResolver 
    */ 
    private function configureOptions(OptionsResolver $resolver) 
    { 
     return $resolver 
      ->setRequired(['route', 'required']) 
      ->setDefaults([ 
       'route_params' => [] 
      ]) 
      ->setAllowedTypes('route', 'string') 
      ->setAllowedTypes('required', 'array') 
      ->setAllowedTypes('route_params', 'array') 
     ; 
    } 

    /** 
    * Get `route` 
    * 
    * @return string 
    */ 
    public function getRoute() 
    { 
     return $this->route; 
    } 

    /** 
    * Get `route_params` 
    * 
    * @return array 
    */ 
    public function getRouteParams() 
    { 
     return $this->route_params; 
    } 

    /** 
    * Get `required` 
    * 
    * @return string[] 
    */ 
    public function getRequired() 
    { 
     return $this->required; 
    } 
} 

你需要听kernel.controller事件,否则您将无法访问活跃控制器的听众。

<?php 
// src/AppBundle/EventListener/FilterControllerListener.php 
namespace AppBundle\EventListener; 

use AppBundle\Annotation\RedirectOnMissing; 
use Doctrine\Common\Annotations\Reader; 
use Symfony\Component\HttpFoundation\Session\SessionInterface; 
use Symfony\Component\HttpKernel\Event\FilterControllerEvent; 
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 
use Symfony\Component\HttpFoundation as Http; 

class FilterControllerListener 
{ 
    /** 
    * @var Reader 
    */ 
    private $reader; 

    /** 
    * @var UrlGeneratorInterface 
    */ 
    private $urlGenerator; 

    /** 
    * @var SessionInterface 
    */ 
    private $session; 

    /** 
    * @param Reader    $reader 
    * @param UrlGeneratorInterface $urlGenerator 
    * @param SessionInterface  $session 
    */ 
    public function __construct(Reader $reader, UrlGeneratorInterface $urlGenerator, SessionInterface $session) 
    { 
     $this->reader = $reader; 
     $this->urlGenerator = $urlGenerator; 
     $this->session = $session; 
    } 

    /** 
    * @param FilterControllerEvent $event 
    */ 
    public function onKernelController(FilterControllerEvent $event) 
    { 
     $controller = $event->getController(); 
     if (!is_array($controller)) { 
      return; 
     } 

     /** @var $methodAnnotation RedirectOnMissing */ 
     $methodAnnotation = $this->reader->getMethodAnnotation(
      new \ReflectionMethod($controller[0], $controller[1]), 
      RedirectOnMissing::class 
     ); 

     if (null !== $methodAnnotation) { 
      foreach ($methodAnnotation->getRequired() as $key) { 
       if (!$this->session->has($key)) { 
        $event->setController(function() use($methodAnnotation) { 
         return new Http\RedirectResponse($this->urlGenerator->generate($methodAnnotation->getRoute(), $methodAnnotation->getRouteParams())); 
        }); 
        break; 
       } 
      } 
     } 
    } 
} 

配置

// src/AppBundle/Resources/config/services.yml 

services: 
    // ... 

    app.event_listner.controller_listener: 
     class: AppBundle\EventListener\FilterControllerListener 
     arguments: 
      - "@annotation_reader" 
      - "@router" 
      - "@session" 
     tags: 
      - { name: kernel.event_listener, event: kernel.controller, method: onKernelController } 

使用

<?php 

/** 
* @Cfg\Route("/test") 
* 
* @RedirectOnMissing(route="home", required={"foo", "bar"}) 
*/ 
public function testAction() 
{ 
    return new Http\Response('no redirect'); 
} 

注:使用定制标注处理,它可能是你需要清除缓存经常能看到的变化。

+0

哇!你应该提交一个公关与这个答案作为新的食谱:)谢谢你这个质量的答案。 –

0

您可以实现自己的注释并将您的逻辑放入kernel.request事件侦听器中。

注释功能不会变成Symfony,它来源于教义。首先需要在您的包中添加新注释类

<?php 

namespace Acme\Bundle\TestBundle\Annotation; 

/** 
* @Annotation 
* @Target({"METHOD"}) 
*/ 
class Custom 
{ 
} 

第二部分是使用您的逻辑创建订阅者。

<?php 

namespace Acme\Bundle\TestBundle\EventListener; 

use Doctrine\Common\Annotations\Reader; 
use Acme\Bundle\TestBundle\Annotations\Custom; 
use Symfony\Component\HttpFoundation\Response; 
use Symfony\Component\HttpKernel\KernelEvents; 
use Symfony\Component\HttpKernel\Event\FilterControllerEvent; 
use Symfony\Component\EventDispatcher\EventSubscriberInterface; 

class CustomEventSubscriber implements EventSubscriberInterface 
{ 
    protected $reader; 

    public function __construct(Reader $reader) 
    { 
     $this->reader = $reader; 
    } 

    public static function getSubscribedEvents() 
    { 
     return [ 
      KernelEvents::CONTROLLER => 'onKernelController', 
     ]; 
    } 

    public function onKernelController(FilterControllerEvent $event) 
    { 
     if (!is_array($controller = $event->getController())) { 
      return; 
     } 

     $method = new \ReflectionMethod($controller[0], $controller[1]); 
     foreach ($this->reader->getMethodAnnotations($method) as $annotation) { 
      if ($annotation instanceof Custom) { 
       // put your logic here 
      } 
     } 
    } 
} 

然后,您必须将用户定义为服务。

services: 
    custom_subscriber: 
     class: Acme\Bundle\TestBundle\EventListener\CustomEventSubscriber 
     arguments: [@annotation_reader] 
     tags: 
      - {name: kernel.event_subscriber} 

之后,您可以标记控制器的行为与您的注释。

欲了解更多信息,请参阅here

+2

尽管此链接可能回答问题,但最好在此处包含答案的基本部分,并提供供参考的链接。如果链接页面更改,仅链接答案可能会失效,导致您的答案无用,从而被删除。 –

+0

我实际上是在这个链接试图了解我如何能够做到我正在寻找它。虽然我并没有真正了解如何在示例中将'Permission'链接到'AnnotationDriver'。 –

+0

我修改了我的评论。 Doctrine注解驱动程序从方法/类解析注释和映射注释以更正描述注释的类 –