Přejít k navigační liště
Webexpo

Zdroják » PHP » Symfony po krůčkách – Event Dispatcher

Symfony po krůčkách – Event Dispatcher

Články PHP

Dnes se spolu podíváme na EventDispatcher. Jde o komponentu, která dodá tvému kódu flexibilitu. Zároveň je jednou z nejdůležitějších součástek životního cyklu Symfony. Když pochopíš EventDispatcher, budeš zase o kousek blíž k tomu stát se opravdovým mistrem Symfony.

„Cesta dlouhá tisíc mil začíná prvním krokem.“                                                                                                                      Confucius

Hlavní pojmy

Event

…neboli událost. Jde o něco, co může nastat při běhu aplikace. Typickým příkladem je objednávka. Když dojde k odeslání objednávky, tak se zavolá Event. Na Event odeslání objednávky pak může slyšet několik EventSubscriberů.

EventSubscriber

…může poslat e-mail adminovi, přičíst kredity za úspěšný nákup, nebo poslat informační sms do skladu s pokynem k zabalení tvých vánočních dárků.

EventDispatcher

ten se stará o zavolání EventSubscriberů, když nastane určitý Event.

Kde můžeš EventDispatcher najít?

Co ti EventDispatcher umožní?

  • Dostat se na určité místo v kódu bez nutnosti jeho změny
  • Zvýšit flexibilitu a použitelnost tvé aplikace

Jak to aplikovat v kódu?

Symfony\EventDispatcher nainstaluješ pomocí Composeru:

$ composer require symfony/event-dispatcher

Vytvoříš si souborindex.php

<?php

require_once __DIR__ . '/vendor/autoload.php';

$eventDispatcher = new Symfony\Component\EventDispatcher\EventDispatcher;
// dispatchneme event někde v kódu na konci objednávky 
$eventDispatcher->dispatch('order.finish');

A spustíš:

$ php index.php

Dispatchneš Event, ale nic se nestane. Aby se něco stalo, bude potřeba ještě EventSubscriber – ten bude naslouchat na order.finish.

Přidáš tedy EventSubscriber.

<?php

class SendEmailToAdminEventSubscriber implements Symfony\Component\EventDispatcher\EventSubscriberInterface
{
   public $signal = 0;

   public static function getSubscribedEvents()
   {
      // tady budeme poslouchat "order.finish" event
      // a pokud nastane, použijeme metodu sendEmailToAdmin()
       return ['order.finish' => 'sendEmailToAdmin'];
   }

   public function sendEmailToAdmin()
   {
      // náš kód, který pošle e-mail adminovi
       $this->signal = 1;
   }
}

Nakonec přidáš EventSubscriber do EventDispatcheru:

<?php

$sendEmailToAdminEventSubscriber = new SendEmailToAdminEventSubscriber;

$eventDispatcher = new Symfony\Component\EventDispatcher\EventDispatcher;
$eventDispatcher->addSubscriber($sendEmailToAdminEventSubscriber);

var_dump($sendEmailToAdminEventSubscriber->signal);

$eventDispatcher->dispatch('order.finish');

var_dump($sendEmailToAdminEventSubscriber->signal);

A opět spustíš:

$ php index.php
int(0)
int(1)

Teď, když se ti dispatchne order.finish Event, zavolá se každý EventSubcriber, který se k němu zapsal. V něm se zavolá metoda, která je k němu přiřazena. Dojde tak ke změně $signal z 0 na 1.

Pro tip: Metoda getSubscribedEvents() může naslouchat více Eventům, více metodami. Může také určovat jejich pořadí.

Nyní už rozumíš Symfony komponentě EventDispatcher.

Event s argumenty

Při volání události obvykle potřebuješ předat i nějaká data. Například číslo objednávky. Taková třída Event je vlastně pouhý Value object – schránka na data.

<?php

class OrderEvent extends Symfony\Component\EventDispatcher\Event
{
   private $orderId;

   public function __construct($orderId)
   {
       $this->orderId = $orderId;
   }

   public function getOrderId()
   {
       return $this->orderId;
   }
}

Dispatchneš event i s potřebnými daty.

<?php

$orderEvent = new OrderEvent(123);
$eventDispatcher->dispatch('order.finish', $orderEvent);

Rozšíříš EventSubscriber o OrderEvent:

<?php

class SendEmailToAdminEventSubscriber
    implements Symfony\Component\EventDispatcher\EventSubscriberInterface
{
   public $signal = 0;

   public static function getSubscribedEvents()
   {
       return ['order.finish' => 'sendEmailToAdmin'];
   }

   public function sendEmailToAdmin(OrderEvent $orderEvent)
   {
       $this->signal = $orderEvent->getOrderId();
   }
}

A doplníš svůj výsledný kód:

$eventDispatcher = new Symfony\Component\EventDispatcher\EventDispatcher;
$sendEmailToAdminEventSubscriber = new SendEmailToAdminEventSubscriber;
$eventDispatcher->addSubscriber($sendEmailToAdminEventSubscriber);

var_dump($sendEmailToAdminEventSubscriber->signal);

$orderEvent = new OrderEvent(123);
$eventDispatcher->dispatch('order.finish', $orderEvent);

var_dump($sendEmailToAdminEventSubscriber->signal);

Výstup pak vypadá takto:

$  event-dispatcher  php index.php
int(0)
int(123)

 

Jsi zase o krok dál

Teď už:

  • rozumíš základním workflow událostí
  • znáš pojmy Event, EventSubscriber a EventDispatcher
  • víš, k čemu využít vlastní Event objekt
  • …a umíš použít EventDispatcher prakticky ve svém kódu

 

Potřebuješ víc?

Pokud bys potřeboval jít ve vysvětlování do větší hloubky, mrkni na oficiální dokumentaci EventDispatcheru.

Už v Symfony děláš léta a chceš posdílet zkušenosti?

Nebo tě Symfony zatím jen láká a rád by se o něm dozvěděl více? Přijd si pokecat na pravidelné měsíční srazy v Praze a Brně.

Komentáře

Odebírat
Upozornit na
guest


15 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
Zobrazit všechny komentáře
Martin Zeman

Díky za článek!
Jen by mě zajímalo, proč jsi začal zrovna s eventy?

Martin Zeman

Super super, děkuji :)

lenoch

Proč se to nedělá jednodušeji? Např. nějak podobně jako v Nette:

$order->onFinish = function() { sendMailToAdmin(); }
HonzaMarek

1) Nemusím do kódu orderu přidávat onFinish. 2) V momentě dispatchování akce nemusím mít vytvořený objekt $order.

lenoch

Ad 1) No ale musím v kódu orderu zavolat $eventDispatcher->dispatch(‚order.finish‘); ne? Tedy pokud tomu správně rozumím.
Ad 2) V momentě dispatchování snad order vytvořený být musí? Nemusí být vytvořený v momentě subscribování, to ano, v tom je rozdíl.

Já
  1. Ano, stejne jako se v nette vola onFinish…
  2. Callback v nette/object je mozna jednodussi, ale je omezen presne jen na nette/object. V dispatcheru muzete odesilat co chcete kdy chcete, klidne jen hole eventy pred vytvorenim nebo po smazani objednavky…
lenoch
  1. Ano, to byla reakce na Honzu Marka, tím jsem chtěl říct, že v tomhle nevidím rozdíl.
  2. Ano, tady je volaní/nastavování eventů centralizované v nějakém globálním? objektu. Jen nevím jestli eventuální výhody v praxi opodstatní tu větší složitost.

Mě totiž symfony obecně připadá (podobně jako jiné fw) zbytečně složité a komplikované (což může být můj problém), takže jsem na tomhle případu zajímal, co mi ta větší komplexita přinese.

Já
  1. v případě $order rozdíl není – osobně ale nejčastěji (a předpokládám většina programátorů) pracuji s eventama z doctrine nebo přimo ze symfony – tedy z cizího kódu
  2. je to service v di. Callback řeší „události“ objektu, který podědil nette/object, zato EventDispatcher řeší jakékoliv události v aplikaci (a předává v Eventu jakákoliv data). Je to něco jiného, nevím jestli se dá vůbec v nette najít něco srovnatelného…
lenoch

Hm, no tak v případě symfony musí ten objekt odněkud získat objekt EventDispatcher a použít ho, v případě Nette se musí podědit z Nette/Object. Je to trochu jiný typ závislosti, ale zásadní rozdíl v tom aspoň já nevidím. Jakékoliv události lze v Nette obsloužit taky, jakákoliv data lze předat v eventu taky. Ale ok, už toho nechme.

lenoch

Ok, tento důvod chápu – pokud mám spoustu kódu třetích stran, který využívá symfony EventDispatcher, tak pochopitelně musím používat EventDispatcher, když s ním pracuji.

lenoch

Tak je pravda, že Nette řešení využívá určitou magii rozšiřující vlastnosti php, ale přiřazení callbacku do onFinish a pak volání $this->onFinish($parametry) ve třídě mi připadá značně intuitivní a v řadě jazyků (javascript) lze něco podobného udělat implicitně.

Ale budiž, co je čitelné a srozumitelné je subjektivní věc, nechci se přit.

Já osobně vidím největší potenciální výhodu toho symfony systému v tom, (už to někdo zmiňoval), že ta obsluha výjjimek je mimo využívající objekt, který ji pouze odněkud převezme a pak použije a teoreticky mu můžu například dodat i nějakou jinou implementaci EventDispatcheru.

Takže shrnuto vidím to tak, že symfony je víc „decoupled“ a potenciálně o něco flexibilnější, nette je (alespoň pro mě) v tomhle jednodušší a stručnější.

Dvojak

Ja tomu asi moc nerozumim. Objekt subscriberu je do dispatcheru pridavan ve chvili, kdy je event „vyhozen“. Ale v tu chvili preci nemuze programator vedet, jake vsechny subscibery budou v budoucnu event odchytavat a novy programator, ktery subsciber vytvari, pak naopak (pravdepodobne) nemuze upravovat misto, kde byl event dispatchnut.

Přístupnost není jen o splnění norem: nový pohled na inkluzivní design

Přístupnost a inkluze možná nepatří mezi nejžhavější témata digitálního světa – dokud o nich nezačne mluvit Vitaly Friedman. Na WebExpo 2024 předvedl, že inkluzivní design není jen o splněných checkboxech, ale hlavně o lidech. S energií sobě vlastní obrátil zažité přístupy naruby a ukázal, že skutečně přístupný web je nejen možný, ale i nezbytný.

Efektivnější vývoj UI nebo API: Co si odnést z WebExpo 2025?

Různé
Komentáře: 0
Jak snadno implementovat moderní uživatelské rozhraní? Které funkce brzdí rychlost vašeho webu? A kdy raději sami přibrzdit, abychom využitím AI nepřekročili etické principy? Debatu aktuálních dev témat rozdmýchá sedmnáctý ročník technologické konference WebExpo, která proběhne v Praze od 28. do 30. května. Který talk či workshop si rozhodně nenechat ujít? Toto je náš redakční výběr z vývojářských hroznů.

Zapřáhněte AI jako nikdy předtím. Květnová konference WebExpo přivítá hvězdy technologického světa

Od 28. do 30. května 2025 promění pražský Palác Lucerna na tři dny technologická konference WebExpo. Na programu je více než 80 přednášek a workshopů od expertů z celého světa. WebExpo tradičně propojuje vývojáře, designéry, marketéry i byznysové lídry a nabízí praktické dovednosti, strategické myšlení a přináší nejnovější trendy nejen v oblasti AI.

Hádej co? Cookies!

Tento web používá cookies, pro základní fungování, poskytování funkcí sociálních médií a analýzu návštěvnosti. Více informací