diff --git a/framework/core/src/Extend/Event.php b/framework/core/src/Extend/Event.php index 152ed71ec..be23b27f1 100644 --- a/framework/core/src/Extend/Event.php +++ b/framework/core/src/Extend/Event.php @@ -16,13 +16,16 @@ use Illuminate\Contracts\Events\Dispatcher; class Event implements ExtenderInterface { private $listeners = []; + private $subscribers = []; /** * Add a listener to a domain event dispatched by flarum or a flarum extension. * * The listener can either be: - * - a callback function or + * - a callback function * - the class attribute of a class with a public `handle` method, which accepts an instance of the event as a parameter + * - an array, where the first argument is an object or class name, and the second argument is the method on the + * first argument that should be executed as the listener * * @param string $event * @param callable|string $listener @@ -34,6 +37,22 @@ class Event implements ExtenderInterface return $this; } + /** + * Add a subscriber for a set of domain events dispatched by flarum or a flarum extension. + * Event subscribers are classes that may subscribe to multiple events from within the subscriber class itself, + * allowing you to define several event handlers within a single class. + * + * @see https://laravel.com/docs/6.x/events#writing-event-subscribers + * + * @param string $subscriber: The class attribute of the subscriber class + */ + public function subscribe(string $subscriber) + { + $this->subscribers[] = $subscriber; + + return $this; + } + public function extend(Container $container, Extension $extension = null) { $events = $container->make(Dispatcher::class); @@ -41,5 +60,13 @@ class Event implements ExtenderInterface foreach ($this->listeners as $listener) { $events->listen($listener[0], $listener[1]); } + + $app = $container->make('flarum'); + + $app->booted(function () use ($events) { + foreach ($this->subscribers as $subscriber) { + $events->subscribe($subscriber); + } + }); } } diff --git a/framework/core/tests/integration/extenders/EventTest.php b/framework/core/tests/integration/extenders/EventTest.php index 0aab2ce06..657e915fa 100644 --- a/framework/core/tests/integration/extenders/EventTest.php +++ b/framework/core/tests/integration/extenders/EventTest.php @@ -10,12 +10,14 @@ namespace Flarum\Tests\integration\extenders; use Flarum\Extend; +use Flarum\Foundation\Application; use Flarum\Group\Command\CreateGroup; use Flarum\Group\Event\Created; use Flarum\Tests\integration\RetrievesAuthorizedUsers; use Flarum\Tests\integration\TestCase; use Flarum\User\User; -use Illuminate\Contracts\Bus\Dispatcher; +use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher; +use Illuminate\Contracts\Events\Dispatcher; use Symfony\Component\Translation\TranslatorInterface; class EventTest extends TestCase @@ -24,7 +26,7 @@ class EventTest extends TestCase protected function buildGroup() { - $bus = $this->app()->getContainer()->make(Dispatcher::class); + $bus = $this->app()->getContainer()->make(BusDispatcher::class); return $bus->dispatch( new CreateGroup(User::find(1), ['attributes' => [ @@ -72,6 +74,32 @@ class EventTest extends TestCase $this->assertEquals('core.group.admin', $group->name_singular); } + + /** + * @test + */ + public function custom_subscriber_works() + { + // Because it injects a translator, this also tests that stuff can be injected into this callback. + $this->extend((new Extend\Event)->subscribe(CustomSubscriber::class)); + + $group = $this->buildGroup(); + + $this->assertEquals('core.group.admin', $group->name_singular); + } + + /** + * @test + */ + public function custom_subscriber_applied_after_app_booted() + { + // Because it injects a translator, this also tests that stuff can be injected into this callback. + $this->extend((new Extend\Event)->subscribe(CustomSubscriber::class)); + + $group = $this->buildGroup(); + + $this->assertEquals('booted', $group->name_plural); + } } class CustomListener @@ -88,3 +116,26 @@ class CustomListener $event->group->name_singular = $this->translator->trans('core.group.admin'); } } + +class CustomSubscriber +{ + protected $bootedAtConstruct; + protected $translator; + + public function __construct(Application $app, TranslatorInterface $translator) + { + $this->bootedAtConstruct = $app->isBooted(); + $this->translator = $translator; + } + + public function subscribe(Dispatcher $events) + { + $events->listen(Created::class, [$this, 'whenGroupCreated']); + } + + public function whenGroupCreated(Created $event) + { + $event->group->name_singular = $this->translator->trans('core.group.admin'); + $event->group->name_plural = $this->bootedAtConstruct ? 'booted' : 'not booted'; + } +}