Symfony4: 自前のアノテーションでコントローラ制御 https://karafuru-ramune.com/blog/827c536bef00d652d1d08de69d49a1eb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# EventListenerはAutowiringではなくマニュアルで設定 | |
# ちなみにEventSubscriberはAutowiring可能 | |
# https://symfony.com/doc/current/doctrine/event_listeners_subscribers.html | |
services: | |
App\EventListener\XMLHttpRequestListener: | |
tags: | |
- { name: kernel.event_listener, event: kernel.request } | |
arguments: | |
- '@annotations.reader' | |
- '@controller_resolver' | |
- '@router' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Controller; | |
use App\Annotation\XMLHttpRequest; | |
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; | |
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | |
use Symfony\Bundle\FrameworkBundle\Controller\Controller; | |
use Symfony\Component\HttpFoundation\Request; | |
/** | |
* @XMLHttpRequest() | |
*/ | |
class XHRController extends Controller | |
{ | |
/** | |
* メソッドのアノテーションが優先される | |
* | |
* @Route("/xhr", name="list") | |
* @Method("GET") | |
* @XMLHttpRequest("index") | |
*/ | |
public function list() {...} | |
/** | |
* メソッドにアノテーションが付いていない場合は、クラスのアノテーションで動作 | |
* | |
* @Route("/xhr", name="post") | |
* @Method("POST") | |
*/ | |
public function post() {...} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Annotation; | |
use Doctrine\Common\Annotations\Annotation; | |
/** | |
* アノテーション | |
* | |
* @Annotation | |
*/ | |
class XMLHttpRequest | |
{ | |
public $value; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\EventListener; | |
use App\Annotation\XMLHttpRequest; | |
use Doctrine\Common\Annotations\AnnotationReader; | |
use Symfony\Bundle\FrameworkBundle\Routing\Router; | |
use Symfony\Component\HttpFoundation\RedirectResponse; | |
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; | |
use Symfony\Component\HttpKernel\Event\GetResponseEvent; | |
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; | |
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | |
class XMLHttpRequestListener | |
{ | |
/** @var AnnotationReader */ | |
private $reader; | |
/** @var ControllerResolverInterface */ | |
private $resolver; | |
/** @var Router */ | |
private $router; | |
public function __construct( | |
AnnotationReader $reader, | |
ControllerResolverInterface $resolver, | |
Router $router | |
) | |
{ | |
$this->reader = $reader; | |
$this->resolver = $resolver; | |
$this->router = $router; | |
} | |
public function onKernelRequest(GetResponseEvent $event) | |
{ | |
// twigのcontroller renderなど、サブリクエストはアノテーション処理しない | |
if (!$event->isMasterRequest()) { | |
return; | |
} | |
// アノテーション取得 | |
$annotations = $this->getAnnotations($event); | |
foreach ($annotations as $annotation) { | |
// XMLHttpRequestアノテーションでない場合はスキップ | |
if (!($annotation instanceof XMLHttpRequest)) { | |
continue; | |
} | |
// X-Requested-Withヘッダーを持っている場合はスキップ | |
if ($event->getRequest()->isXmlHttpRequest()) { | |
continue; | |
} | |
// 以下から、4xxや別ページに飛ばしたりする処理 | |
if (!is_string($annotation->value) || trim($annotation->value) === '') { | |
throw new BadRequestHttpException(); | |
} | |
try { | |
$url = $this->router->generate($annotation->value); | |
$event->setResponse(new RedirectResponse($url)); | |
} catch (\Exception $e) { | |
throw new NotFoundHttpException($e); | |
} | |
return; | |
} | |
} | |
private function getAnnotations(GetResponseEvent $event) | |
{ | |
// $controller[0]にコントローラのオブジェクト、$controller[1]にメソッド名 | |
$controller = $this->resolver->getController($event->getRequest()); | |
$class = get_class($controller[0]); | |
$methodName = $controller[1]; | |
if (!class_exists($class)) { | |
throw new \AssertionError('class does not exist: ' . $class); | |
} | |
$reflection = new \ReflectionClass($class); | |
return array_merge( | |
$this->reader->getMethodAnnotations($reflection->getMethod($methodName)), | |
$this->reader->getClassAnnotations($reflection) | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment