Skip to content

Instantly share code, notes, and snippets.

@unio-lss
Last active Apr 14, 2019
Embed
What would you like to do?
Symfony4: 自前のアノテーションでコントローラ制御 https://karafuru-ramune.com/blog/827c536bef00d652d1d08de69d49a1eb
# 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'
<?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() {...}
}
<?php
namespace App\Annotation;
use Doctrine\Common\Annotations\Annotation;
/**
* アノテーション
*
* @Annotation
*/
class XMLHttpRequest
{
public $value;
}
<?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