如果不满足要求,则重定向

时间:2016-05-20 09:39:08

标签: php symfony

我有许多控制器操作需要会话中的某些上下文,以便可以执行它们。

/**
 * @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课程,我该怎么做? 还是会有另一种明智的方式?

2 个答案:

答案 0 :(得分: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');
}

注意:处理自定义注释时,可能需要经常清除缓存以查看更改。

答案 1 :(得分:0)

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

注释功能不会成为Symfony,它来自Doctrine。首先需要在您的包中添加新的注释类

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