Slide 1

Slide 1 text

Event Dispatcher Evercode Lab Adrov Igor @nuclear0 [email protected] 08.11.2012

Slide 2

Slide 2 text

Шаблон проектирования Observer event generators event dispatcher

Slide 3

Slide 3 text

Шаблон проектирования Observer event generators event dispatcher event handler event handler event handler

Slide 4

Slide 4 text

Для чего вообще нужны события? • 1. Расширение классов без их непосредственного изменения • 2. Вынесение кода часто-используемых действий. Например, установка значений created_at и updated_at • 3. Создание очередей и сбор данных по ходу исполнения кода

Slide 5

Slide 5 text

Event Dispatcher Component { "require": { "symfony/event-dispatcher": "2.1.*" } } composer install composer.json:

Slide 6

Slide 6 text

Event Dispatcher Component addListener( 'foo.action', array($listener, 'onFooAction') );

Slide 7

Slide 7 text

class AcmeListener { // ... public function onFooAction(Event $event) { // ... do something echo $event->getName(); } } $event = new Event(); $dispatcher->dispatch('foo.action', $event); Event Dispatcher Component

Slide 8

Slide 8 text

События в Symfony $dispatcher = $this->container->get('event_dispatcher'); $dispatcher->dispatch('foo_bundle.post.comment_added', new CommentEvent($post, $comment));

Slide 9

Slide 9 text

События в Symfony services: app.listener.location: class: App\DefaultBundle\Listener\LocationListener tags: - { name: kernel.event_listener, event: kernel.controller } - { name: kernel.event_listener, event: kernel.response, method: onKernelResponse } - { name: kernel.event_listener, event: security.interactive_login } $listener = new LocationListener(); $dispatcher->addListener('kernel.controller', array($listener, 'onKernelController')); $dispatcher->addListener('kernel.response', array($listener, 'onKernelResponse')); $dispatcher->addListener('security.interactive_login', array($listener, 'onSecurityInteractiveLogin'));

Slide 10

Slide 10 text

Где же можно применить события на практике? {% render "AppBannerBundle:Banner:top" with {'place': 'MAIN_HORIZONTAL' } %} Система баннеров

Slide 11

Slide 11 text

Система баннеров services: app.listener.banner: class: App\BannerBundle\Listener\BannerListener tags: - { name: kernel.event_listener, event: kernel.controller } arguments: [@doctrine.orm.entity_manager] class BannerListener { private $em; protected $banners; public function __construct($em) { $this->em = $em; } ... } config.yml: BannerListener.php:

Slide 12

Slide 12 text

public function onKernelController($event) { if (HttpKernel::MASTER_REQUEST != $event->getRequestType()) { // don't do anything if it's not the master request return; } $this->banners = $this->em ->getRepository('AppBannerBundle:Banner')->findByPlaces(); } public function getBanner($place) { return isset($this->banners[$place]) ? $this->banners[$place] : null; } $this->get('app.listener.banner')->getBanner($place); Система баннеров

Slide 13

Slide 13 text

Система баннеров Количество запросов до: 12 Количество запросов после: 1

Slide 14

Slide 14 text

public function onKernelRequest(GetResponseEvent $event) { if (HttpKernel::MASTER_REQUEST !== $event->getRequestType()) { // don't do anything if it's not the master request return; } $context = $this->router->getContext(); $request = $event->getRequest(); if ($request->get('_city')) { $context->setParameter('_city', true); } } Установка параметра для всех ссылок

Slide 15

Slide 15 text

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 MinAccess) { if (!$annotation->execute( $this->security, $this->session, $this->routing) ) { throw new MinAccessException(); } } } } Расширение метода с помощью аннотаций

Slide 16

Slide 16 text

public function onKernelException(GetResponseForExceptionEvent $event) { $exception = $event->getException(); if ($exception instanceof MinAccessException) { $event->setResponse( new RedirectResponse($this->routing->generate('address_edit') )); } } Расширение метода с помощью аннотаций

Slide 17

Slide 17 text

public function onSecurityInteractiveLogin($event) { $user = $this->securityContext->getToken()->getUser(); $request = $event->getRequest(); if ($city = $entity->getLocation()) { $url = $this->getCityUrl($city); $this->session->set('_security.target_path', $url); } } Перенаправление после входа

Slide 18

Slide 18 text

События Doctrine my.listener: class: App\CityBundle\Listener\CityListener tags: - { name: doctrine.event_listener, event: prePersist, method: prePersist } class CityListener { public function prePersist(LifecycleEventArgs $args) { $entity = $args->getEntity(); $entityManager = $args->getEntityManager(); if ($entity instanceof City) { $now = new \DateTime(); $entity->setCreatedAt($now); } } }

Slide 19

Slide 19 text

• Усложнение кода • Усложнение дебага Минусы использования событий event generators event dispatcher event handler event handler event handler

Slide 20

Slide 20 text

Thanks! Полезные ссылки: •http://symfony.com/doc/current/components/ event_dispatcher/introduction.html •https://speakerdeck.com/johnkary/writing- extensible-code-using-event-dispatcher