Upgrade to Pro — share decks privately, control downloads, hide ads and more …

EventDispatcher— простой и эффективный способ с...

Avatar for Semyon Semyon
November 28, 2012

EventDispatcher— простой и эффективный способ сделать ваше приложение по-настоящему расширяемым

Avatar for Semyon

Semyon

November 28, 2012

Other Decks in Programming

Transcript

  1. Содержание • Знакомство с теорией «Observer» • Где используется EventDispatcher

    и как взаимодействовать с ним, на примерах компонентов Symfony • Разница между Subscriber и Listener • Расширяем свой код через Subscriber • Listener в виде tagged service
  2. Observer — это идея! Научить систему генерировать события и реагировать

    на них Расширять функционал без наследования и без непосредственной модификации кода
  3. Как это можно применить? • Собрать журнал действий • Предоставить

    интерфейс расширения • Выделить часто используемый код • Остановить дальнейшую обработку события
  4. Где уже используется? • ядре — kernel.* • генераторе форм

    — form.* • компоненте Security — security.* а также в: • knp-components • KnpMenuBundle • ExtDirectBundle
  5. Уже хочу попробовать! $dispatcher = $this->container->get('event_dispatcher'); получили диспетчер $event =

    new GenericEvent($this); $dispatcher->dispatch('foo.action', $event); описали событие свершилось событие!
  6. Создаем Listener и подключаем use Symfony\Component\EventDispatcher\Event; class AcmeListener { //

    ... public function onFooAction(Event $event) { // ... сделаем что-то } } $listener = new AcmeListener(); $this->container->dispatcher ->addListener('foo.action', array($listener, 'onFooAction')); call_user_func
  7. А есть реальный пример? class ClientType extends AbstractType { public

    function buildForm(FormBuilderInterface $builder, array $options) { $subscriber = new AddLoginFieldSubscriber($builder- >getFormFactory()); $builder->addEventSubscriber($subscriber); $builder->add('email', 'email'); $builder->add('password', 'password'); // ... и другие поля } // ... } Есть! Динамическая форма! добавили немного магии
  8. Разоблачение фокуса class AddLoginFieldSubscriber implements EventSubscriberInterface { private $factory; public

    function __construct(FormFactoryInterface $factory) { $this->factory = $factory; } получаем форму, нам с ней работать
  9. Разоблачение фокуса class AddLoginFieldSubscriber implements EventSubscriberInterface { private $factory; public

    function __construct(FormFactoryInterface $factory) { $this->factory = $factory; } public static function getSubscribedEvents() { return array(FormEvents::PRE_SET_DATA => 'preSetData'); } когда будем запускать и кого
  10. Разоблачение фокуса class AddLoginFieldSubscriber implements EventSubscriberInterface { private $factory; public

    function __construct(FormFactoryInterface $factory) { $this->factory = $factory; } public static function getSubscribedEvents() { return array(FormEvents::PRE_SET_DATA => 'preSetData'); } public function preSetData(FormEvent $event) { if (null === $event->getData()) return; if (!$event->getData()->getId()) $this->getForm() ->add($this->factory->createNamed('login', 'text')); } } У нового объекта нет id
  11. Два! class LogUserAgentListener { private $logger; public function __construct(Logger $logger)

    { $this->logger = $logger; } public function onKernelRequest(GetResponseEvent $event) { $request = $event->getRequest(); $headers = $request->headers; if($headers->has('user-agent')) $this->logger->info($headers->get('user-agent')); } } Записываем всех входящих
  12. Три! acme_demo.log_user_agent: class: Acme\DemoBundle\Event\Listener\LogUserAgentListener arguments: [@logger] tags: - {name: kernel.event_listener,

    event: kernel.request, method: onKernelRequest, priority: 100} Конфигурируем сервис:
  13. Нам нужен свой CompilerPass class CustomCompilerPass implements CompilerPassInterface { public

    function process(ContainerBuilder $container) { $eventDispatcher = $container->getDefinition('event_dispatcher'); взяли диспетчер
  14. Нам нужен свой CompilerPass class CustomCompilerPass implements CompilerPassInterface { public

    function process(ContainerBuilder $container) { $eventDispatcher = $container->getDefinition('event_dispatcher'); foreach ($container->findTaggedServiceIds('acme.product.update') as $id => $attributes) { ищем нужные сервисы
  15. Нам нужен свой CompilerPass class CustomCompilerPass implements CompilerPassInterface { public

    function process(ContainerBuilder $container) { $eventDispatcher = $container->getDefinition('event_dispatcher'); foreach ($container->findTaggedServiceIds('acme.product.update') as $id => $attributes) { // ... $eventDispatcher->addMethodCall('addListenerService', array($attributes['event'], array($id, $attributes['method']), $attributes['priority'])); } } } добавим найденные
  16. Осталось только подключить! class AcmeDemoBundle extends Bundle { public function

    build(ContainerBuilder $container) { parent::build($container); $container->addCompilerPass(new CustomCompilerPass()); } } Больше кода не будет наш герой
  17. Можете пользоваться! (с) Теперь ваш код может быть модифицирован так-же

    легко и быстро как и ядро symfony! P.S. Вспомним слайд №18:
  18. Плюсы, минусы, подводные камни Но при большом количестве обработчиков на

    одно событие: • усложнение логики • трудности в отладке Менеджер событий Обработчик Обработчик Обработчик