📜 ⬆️ ⬇️

Initialized controllers in symfony and working with annotations

A long time ago, I was faced with the task of implementing the controller initialization mechanism in symfony, i.e. performing some default actions before each call of the controller action. The first thing that came to mind was to add an EventListener for the kernel.controller event, in which the controller's initialize method would be called, if there is one. I have been using this method for several years now.

Just the other day, I wondered: what if you need to perform different methods for different controllers before the action, several methods in a row, and some of them even several times and with different parameters? In this article I want to tell you how I solved this problem with annotations. I think this article will be useful, including those who have never worked with annotations.

First of all, I will clearly demonstrate how to implement the controller initialization mechanism.

First, we will create an interface that will help to catch those controllers that need initialization:
')
<?php namespace MyBundle\Controller; interface InitializableControllerInterface { } 

Then we will create an EventListener for the kernel.controller event, which will perform the initialization:

 <?php namespace MyBundle\EventListener; use MyBundle\Controller\InitializableControllerInterface; use Symfony\Component\HttpKernel\Event\FilterControllerEvent; class KernelControllerListener { // ,    kernel.controller public function onKernelController(FilterControllerEvent $event) { $controller = $event->getController(); //     InitializableControllerInterface if (is_array($controller) && $controller[0] instanseof InitializableControllerInterface) { //     } } } 

And add service configuration (services.xml) for it:

 <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <services> <service id="my_bundle.kernel_controller_listener" class="MyBundle\EventListener\KernelControllerListener"> <tag name="kernel.event_listener" event="kernel.controller" method="onKernelController" /> </service> </services> </container> 

In principle, this is already enough, but we want to make the initialization of the controllers more flexible, so we turn to annotations.

In fact, it is very easy to work with annotations, especially if you use Doctrine annotation reader. Let's start with creating an annotation class that should be applied to controller initialization methods:

 <?php namespace MyBundle\Annotation; /** * @Annotation * @Target({"METHOD"}) * *   @Annotation  ,       , *  @Target({"METHOD"}) -         . */ class Init { //  : /** * @var array * *    */ public $args = []; /** * @var int * *   ( ,    ) */ public $priority = 0; } 

I recommend specifying the type of annotation parameters to control Doctrine over incoming data types.

Now the annotation can be used in the controller:

 <?php namespace MyBundle\Controller; use MyBundle\Annotation\Init; use MyBundle\Controller\InitializableControllerInterface; use Symfony\Bundle\FrameworkBundle\Controller\Controller; class MyController extends Controller implements InitializableControllerInterface { /** * @Init(args = {"test"}, priority = 200) * *     (initialize("test");)     */ public function initialize($value) { // ... -  ... } } 

It remains only to add the processing of annotations to our KernelControllerListener:

 <?php namespace MyBundle\EventListener; use Doctrine\Common\Annotations\Reader; use MyBundle\Annotation\Init; use MyBundle\Controller\InitializableControllerInterface; use Symfony\Component\HttpKernel\Event\FilterControllerEvent; class KernelControllerListener { protected $annotationReader; //      public function __construct(Reader $annotationReader) { $this->annotationReader = $annotationReader; } public function onKernelController(FilterControllerEvent $event) { $controller = $event->getController(); if (is_array($controller) && $controller[0] instanceof InitializableControllerInterface) { //     $reflector = new \ReflectionClass($controller[0]); //       $methods = $reflector->getMethods(\ReflectionMethod::IS_PUBLIC); $initMethods = []; //    ,     @Init foreach ($methods as $method) { //     $annotations = $this->annotationReader->getMethodAnnotations($method); foreach ($annotations as $annotation) { //   - ,            if ($annotation instanceof Init) { $initMethods[] = [ 'method' => $method, 'args' => $annotation->args, 'priority' => $annotation->priority ]; } } } //         usort($initMethods, function($a, $b) { return $b['priority'] - $a['priority']; }); foreach ($initMethods as $initMethod) { $method = $initMethod['method']; //     ,        if (count($initMethod['args'])) { $method->invokeArgs($controller[0], $initMethod['args']); } else { $method->invoke($controller[0]); } } } } } 

And complement the configuration of the service:

 <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <services> <service id="my_bundle.kernel_controller_listener" class="MyBundle\EventListener\KernelControllerListener"> <argument type="service" id="annotation_reader" /> <!--      --> <tag name="kernel.event_listener" event="kernel.controller" method="onKernelController" /> </service> </services> </container> 

That's all. All my code can be viewed on GitHub , I will be glad to constructive criticism.

See also:

Source: https://habr.com/ru/post/277465/


All Articles