www.hubleto.eu Reddit User Guide Stack Overflow LinkedIn Report an issue

Introduction to events and listeners

How it works

  1. Code that wants to announce something calls $this->eventManager()->fire('onModelAfterCreate', [$model, $savedRecord]).
  2. The EventManager calls the method onModelAfterCreate of every listener that was registered for this event.
  3. The listener does its work. Its return value is ignored.

The core fires the model events from the callbacks of Hubleto\Framework\Model. That is why the callbacks of your models must call parent::: without it the event is never fired.

Write a listener

A listener extends Hubleto\Framework\EventListener and has one method named after each event it handles. EventManager::fire() calls $listener->onEventName(...$args) for every listener registered for the event, so the listener must have a method with that exact name for every event it is registered for. PHP raises an error otherwise.

apps/PriceLists/EventListeners/InvalidateScopeCache.php
<?php

namespace Hubleto\App\Community\PriceLists\EventListeners;

use Hubleto\App\Community\Customers\Models\CustomerTag;
use Hubleto\App\Community\Products\Models\Category;
use Hubleto\App\Community\Products\Models\Group;
use Hubleto\App\Community\PriceLists\Services\PriceResolver;
use Hubleto\Framework\Interfaces\ModelInterface;

/**
 * Clears cached categories, groups and customer tags of the price resolver when they change within a request.
 */
class InvalidateScopeCache extends \Hubleto\Framework\EventListener implements \Hubleto\Framework\Interfaces\EventListenerInterface
{
  public function onModelAfterCreate(ModelInterface $model, array $savedRecord): void
  {
    if (!$this->isScopeModel($model)) return;
    $this->clear();
  }

  public function onModelAfterUpdate(ModelInterface $model, array $originalRecord, array $savedRecord): void
  {
    if (!$this->isScopeModel($model)) return;
    $this->clear();
  }

  public function onModelAfterDelete(ModelInterface $model, int $id): void
  {
    if (!$this->isScopeModel($model)) return;
    $this->clear();
  }

  protected function isScopeModel(ModelInterface $model): bool
  {
    return $model instanceof Category || $model instanceof Group || $model instanceof CustomerTag;
  }

  protected function clear(): void
  {
    /** @var PriceResolver $resolver */
    $resolver = $this->getService(PriceResolver::class);
    $resolver->clearCache();
  }
}

Listeners of onModel* events receive the events of all models in the system, so the first thing a listener does is to check what it got and return if it does not care.

Register a listener

Register the listener in Loader::init() of your app, once for each event:

apps/PriceLists/Loader.php (excerpt)
    /** @var EventListeners\InvalidateScopeCache $invalidateCache */
    $invalidateCache = $this->getService(EventListeners\InvalidateScopeCache::class);
    foreach (['onModelAfterCreate', 'onModelAfterUpdate', 'onModelAfterDelete'] as $event) {
      $this->eventManager()->addEventListener($event, $invalidateCache);
    }

Listeners of a project

A listener that does not belong to an app goes to src/event-listeners of the project. The folder is autoloaded (namespace HubletoProject\EventListeners, see composer.json of the project), but nothing registers the classes in it automatically. Register them yourself, e.g. in init() of a custom app of the project:

erp-project: src/event-listeners/Example.php
<?php

/*
  This event listener does not do anything. It is just an example of how the
  event listener should be implemented.
*/

namespace HubletoProject\EventListeners;

use Hubleto\Erp\Controller;

class Example extends \Hubleto\Framework\EventListener implements \Hubleto\Framework\Interfaces\EventListenerInterface
{

  public function onControllerSetView(Controller $controller, string $view): void
  {
    // This method is invoked ater a $eventManager()->fire('onControllerSetView')
    // Do anything you want here.
  }

}
$this->eventManager()->addEventListener(
  'onControllerSetView',
  $this->getService(\HubletoProject\EventListeners\Example::class)
);

Rules

Related pages