Skip to content

Commit

Permalink
refactor(event): use a builder to build an admin scoped event dispatcher
Browse files Browse the repository at this point in the history
  • Loading branch information
JoelAlphonso committed Sep 22, 2022
1 parent ea79e37 commit 78b8f03
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,7 @@ public function setDependencies(Container $container)
$this->filesystemConfig = $container['filesystem/config'];
$this->filesystems = $container['filesystems'];

$this->setEventDispatcher($container['event/dispatcher']);
$this->setEventDispatcher($container['admin/event/dispatcher']);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,6 @@ protected function setDependencies(Container $container)
{
parent::setDependencies($container);

$this->setEventDispatcher($container['event/dispatcher']);
$this->setEventDispatcher($container['admin/event/dispatcher']);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,6 @@ protected function setDependencies(Container $container)
{
parent::setDependencies($container);

$this->setEventDispatcher($container['event/dispatcher']);
$this->setEventDispatcher($container['admin/event/dispatcher']);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

// From Pimple
use Charcoal\Admin\AssetsConfig;
use Charcoal\Event\EventDispatcher;
use Charcoal\Event\EventDispatcherBuilder;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Assetic\Asset\AssetReference;
Expand Down Expand Up @@ -83,6 +85,7 @@ public function register(Container $container)
$this->registerAuthExtensions($container);
$this->registerViewExtensions($container);
$this->registerAssetsManager($container);
$this->registerAdminEventDispatcher($container);

// Register Access-Control-List (acl)
$container->register(new AclServiceProvider());
Expand Down Expand Up @@ -548,4 +551,45 @@ protected function registerFactoryServices(Container $container)
]);
};
}

/**
* @param Container $container Pimple DI container.
* @return void
*/
protected function registerAdminEventDispatcher(Container $container)
{
/**
* @param Container $container
* @return array
*/
$container['admin/event/listeners'] = function (Container $container): array {
return ($container['admin/config']->get('events.listeners') ?? []);
};

/**
* Subscribers are classes that implements `\League\Event\ListenerSubscriber`
* It allows to subscribe many grouped listeners at once.
*
* @param Container $container
* @return array
*/
$container['admin/event/subscribers'] = function (Container $container): array {
return ($container['admin/config'] ->get('events.subscribers') ?? []);
};

/**
* Build an event dispatcher using admin config.
*
* @param Container $container
* @return EventDispatcher
*/
$container['admin/event/dispatcher'] = function (Container $container): EventDispatcher {
/** @var EventDispatcherBuilder $eventDispatcherBuilder */
$eventDispatcherBuilder = $container['event/dispatcher/builder'];
return $eventDispatcherBuilder->build(
$container['admin/event/listeners'],
$container['admin/event/subscribers']
);
};
}
}
104 changes: 104 additions & 0 deletions packages/event/src/Charcoal/Event/EventDispatcherBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

namespace Charcoal\Event;

use InvalidArgumentException;
use League\Event\ListenerSubscriber;
use Pimple\Container;
use Psr\EventDispatcher\EventDispatcherInterface;

/**
* Builder
*
* Helps in the process of building an Event Dispatcher.
*/
class EventDispatcherBuilder
{
/**
* A Pimple dependency-injection container to fulfill the required services.
* @var Container $container
*/
protected Container $container;

/**
* @param Container $container The DI container.
*/
public function __construct(Container $container)
{
$this->container = $container;
}

/**
* @param array $listeners
* @param array $subscribers
* @return EventDispatcher
*/
public function build(array $listeners = [], array $subscribers = []): EventDispatcher
{
$dispatcher = new EventDispatcher();
$dispatcher->setLogger($this->container['logger']);

$this->registerEventListeners($dispatcher, $listeners);
$this->registerListenerSubscribers($dispatcher, $subscribers);

return $dispatcher;
}

/**
* @param EventDispatcherInterface $dispatcher Psr-14 Event Dispatcher Interface
* @param array<string, array> $listenersByEvent Array of EventListenerInterface attached to event.
* @return void
*/
private function registerEventListeners(EventDispatcherInterface $dispatcher, array $listenersByEvent)
{
foreach ($listenersByEvent as $event => $listeners) {
if (!is_iterable($listeners)) {
throw new InvalidArgumentException(sprintf(
'Expected iterable map of event listeners for [%s]',
$event
));
}

foreach ($listeners as $listener => $options) {
if (!is_string($listener)) {
throw new InvalidArgumentException(sprintf(
'Expected event listener class string as map key for [%s]',
$event
));
}

$listener = $this->container['event/listener/factory']->create($listener);

$priority = ($options['priority'] ?? 0);
$once = ($options['once'] ?? false);

if ($once) {
$dispatcher->subscribeOnceTo($event, $listener, $priority);
} else {
$dispatcher->subscribeTo($event, $listener, $priority);
}
}
}
}

/**
* @param EventDispatcherInterface $dispatcher Psr-14 Event Dispatcher Interface
* @param array<class-string<ListenerSubscriber>> $subscribers Pimple DI container
* @return void
*/
private function registerListenerSubscribers(EventDispatcherInterface $dispatcher, array $subscribers)
{
foreach ($subscribers as $subscriber) {
if (!is_string($subscriber) || !class_exists($subscriber)) {
throw new InvalidArgumentException(sprintf(
'Expected event subscriber as class string, received %s',
(is_string($subscriber) ? $subscriber : gettype($subscriber))
));
}

$subscriber = $this->container['event/listener-subscriber/factory']->create($subscriber);

$dispatcher->subscribeListenersFrom($subscriber);
}
}
}
6 changes: 3 additions & 3 deletions packages/event/src/Charcoal/Event/EventDispatcherTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ protected function dispatchEvent(object $event): object

/**
* @param array $events
* @return void
* @return array
*/
protected function dispatchEvents(array $events)
protected function dispatchEvents(array $events): array
{
array_map([$this, 'dispatchEvent'], $events);
return array_map([$this, 'dispatchEvent'], $events);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

namespace Charcoal\Event\ServiceProvider;

use Charcoal\Event\EventDispatcher;
use Charcoal\Event\EventDispatcherBuilder;
use Charcoal\Event\EventListenerInterface;
use Charcoal\Factory\FactoryInterface;
use Charcoal\Factory\GenericFactory;
use InvalidArgumentException;
use League\Event\ListenerSubscriber;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Psr\EventDispatcher\EventDispatcherInterface;

/**
* Event Service Provider. Configures and provides a PDO service to a container.
Expand All @@ -24,17 +22,11 @@ class EventServiceProvider implements ServiceProviderInterface
public function register(Container $container)
{
/**
* @param Container $container Pimple DI container.
* @return EventDispatcherInterface
* @param Container $container A service container.
* @return EventDispatcherBuilder
*/
$container['event/dispatcher'] = function (Container $container): EventDispatcherInterface {
$dispatcher = new EventDispatcher();
$dispatcher->setLogger($container['logger']);

$this->registerEventListeners($dispatcher, $container);
$this->registerListenerSubscribers($dispatcher, $container);

return $dispatcher;
$container['event/dispatcher/builder'] = function (Container $container) {
return new EventDispatcherBuilder($container);
};

/**
Expand Down Expand Up @@ -92,65 +84,4 @@ public function register(Container $container)
]);
};
}

/**
* @param EventDispatcherInterface $dispatcher Psr-14 Event Dispatcher Interface
* @param Container $container Pimple DI container
* @return void
*/
private function registerEventListeners(EventDispatcherInterface $dispatcher, Container $container)
{
/**
* @var array<string, array<class-string<EventListenerInterface, mixed>> $container['event/listeners']
*/
foreach ($container['event/listeners'] as $event => $listeners) {
if (!is_iterable($listeners)) {
throw new InvalidArgumentException(sprintf(
'Expected iterable map of event listeners for [%s]',
$event
));
}

foreach ($listeners as $listener => $options) {
if (!is_string($listener)) {
throw new InvalidArgumentException(sprintf(
'Expected event listener class string as map key for [%s]',
$event
));
}

$listener = $container['event/listener/factory']->create($listener);

$priority = ($options['priority'] ?? 0);
$once = ($options['once'] ?? false);

if ($once) {
$dispatcher->subscribeOnceTo($event, $listener, $priority);
} else {
$dispatcher->subscribeTo($event, $listener, $priority);
}
}
}
}

/**
* @param EventDispatcherInterface $dispatcher Psr-14 Event Dispatcher Interface
* @param Container $container Pimple DI container
* @return void
*/
private function registerListenerSubscribers(EventDispatcherInterface $dispatcher, Container $container)
{
foreach ($container['event/subscribers'] as $subscriber) {
if (!is_string($subscriber) || !class_exists($subscriber)) {
throw new InvalidArgumentException(sprintf(
'Expected event subscriber as class string, received %s',
(is_string($subscriber) ? $subscriber : gettype($subscriber))
));
}

$subscriber = $container['event/listener-subscriber/factory']->create($subscriber);

$dispatcher->subscribeListenersFrom($subscriber);
}
}
}

0 comments on commit 78b8f03

Please sign in to comment.