From ec9e2b0277a070f977aa871123a0be139ee60434 Mon Sep 17 00:00:00 2001 From: Kevin Pfeifer Date: Wed, 27 May 2026 18:03:17 +0200 Subject: [PATCH] add documentation for new event listeneres DI support --- docs/en/appendices/5-4-migration-guide.md | 12 +++- docs/en/core-libraries/events.md | 81 +++++++++++++++++++++-- docs/en/development/application.md | 13 ++-- docs/en/plugins.md | 25 +++++++ 4 files changed, 120 insertions(+), 11 deletions(-) diff --git a/docs/en/appendices/5-4-migration-guide.md b/docs/en/appendices/5-4-migration-guide.md index 1e5fc5e233..6a329505fb 100644 --- a/docs/en/appendices/5-4-migration-guide.md +++ b/docs/en/appendices/5-4-migration-guide.md @@ -43,9 +43,19 @@ version is reported as `unknown`), the header is omitted. Events being registered in either `Application::events()` or `Plugin::events()` now work in both web and CLI contexts. It is therefore highly recommended to move your event listeners from the `config/bootstrap.php` file to the -`events()` method in your `Application` or `Plugin` class. +`eventListeners()` method in your `Application` or `Plugin` class. Use the +`events()` method when you need custom registration logic or anonymous +listeners. See [Application and Plugin Events](../core-libraries/events#registering-event-listeners) for more details. +`Application::eventListeners()` and `Plugin::eventListeners()` were added to +register event listener classes declaratively. These listeners are resolved +through the application's dependency injection container, so they can use +constructor-injected dependencies. + +`EventAwareApplicationInterface::pluginEvents()` has been deprecated. Plugin +events are now registered while each plugin is bootstrapped. + ### I18n - `Number::parseFloat()` now returns `null` instead of `0.0` when parsing diff --git a/docs/en/core-libraries/events.md b/docs/en/core-libraries/events.md index 24885525bc..4c7e917377 100644 --- a/docs/en/core-libraries/events.md +++ b/docs/en/core-libraries/events.md @@ -298,16 +298,86 @@ As you can see in the above code, the `on()` function will accept instances of the `EventListener` interface. Internally, the event manager will use `implementedEvents()` to attach the correct callbacks. -::: info Added in version 5.1.0 -The `events` hook was added to the `BaseApplication` as well as the `BasePlugin` class +::: info Added in version 5.4.0 +The `eventListeners` hook was added to `BaseApplication` and `BasePlugin`. ::: -As of CakePHP 5.1 it is recommended to register event listeners by adding them via the `events` hook in your application or plugin class: +As of CakePHP 5.4, applications and plugins can register listener classes with +the `eventListeners()` hook. Listener classes are resolved through the +application's dependency injection container before they are attached to the +global event manager. This lets listeners declare constructor dependencies: ```php namespace App; use App\Event\UserStatistic; +use Cake\Http\BaseApplication; + +class Application extends BaseApplication +{ + // The rest of your Application class + + /** + * @return list> + */ + public function eventListeners(): array + { + return [ + UserStatistic::class, + ]; + } +} +``` + +Plugins can define event listeners the same way in their plugin class: + +```php +namespace ContactManager; + +use Cake\Core\BasePlugin; +use ContactManager\Event\UserStatistic; + +class ContactManagerPlugin extends BasePlugin +{ + /** + * @return list> + */ + public function eventListeners(): array + { + return [ + UserStatistic::class, + ]; + } +} +``` + +If your listener has constructor dependencies, register the listener and its +dependencies in `Application::services()` or `Plugin::services()`: + +```php +use App\Event\UserStatistic; +use App\Service\StatisticsClient; +use Cake\Core\ContainerInterface; + +public function services(ContainerInterface $container): void +{ + $container->addShared(StatisticsClient::class); + $container->addShared(UserStatistic::class) + ->addArgument(StatisticsClient::class); +} +``` + +::: info Added in version 5.1.0 +The `events` hook was added to the `BaseApplication` as well as the `BasePlugin` class. +::: + +Use the `events()` hook in your application or plugin class when you need +imperative registration logic, or want to register anonymous functions: + +```php +namespace App; + +use Cake\Event\EventInterface; use Cake\Event\EventManagerInterface; use Cake\Http\BaseApplication; @@ -317,8 +387,9 @@ class Application extends BaseApplication public function events(EventManagerInterface $eventManager): EventManagerInterface { - $statistics = new UserStatistic(); - $eventManager->on($statistics); + $eventManager->on('Order.afterPlace', function (EventInterface $event): void { + // Code to update statistics + }); return $eventManager; } diff --git a/docs/en/development/application.md b/docs/en/development/application.md index 1b65cbebd4..97355e8dce 100644 --- a/docs/en/development/application.md +++ b/docs/en/development/application.md @@ -16,13 +16,17 @@ methods: - `bootstrap` Used to load [configuration files](../development/configuration), define constants and other global functions. By default, this will include **config/bootstrap.php**. This is the ideal place - to load [Plugins](../plugins) and global [event listeners](../core-libraries/events). + to load [Plugins](../plugins) and application configuration. - `routes` Used to load [routes](../development/routing). By default, this will include **config/routes.php**. - `middleware` Used to add [middleware](../controllers/middleware) to your application. - `console` Used to add [console commands](../console-commands) to your application. By default, this will automatically discover console commands in your application and all plugins. +- `eventListeners` Used to register global [event listener](../core-libraries/events) + classes with the application's event manager. +- `events` Used to register global [events](../core-libraries/events) that + require custom registration logic. ## Bootstrapping your Application @@ -49,8 +53,7 @@ sections there are better ways you add custom logic to your application. In addition to the **config/bootstrap.php** file which should be used to configure low-level concerns of your application, you can also use the -`Application::bootstrap()` hook method to load/initialize plugins, and attach -global event listeners: +`Application::bootstrap()` hook method to load/initialize plugins: ```php // in src/Application.php @@ -84,6 +87,6 @@ class Application extends BaseApplication } ``` -Loading plugins and events in `Application::bootstrap()` makes -[Integration Testing](../development/testing#integration-testing) easier as events and routes will be re-processed on +Loading plugins in `Application::bootstrap()` makes +[Integration Testing](../development/testing#integration-testing) easier as routes will be re-processed on each test method. diff --git a/docs/en/plugins.md b/docs/en/plugins.md index 6e772ffe1d..5b2f6df62f 100644 --- a/docs/en/plugins.md +++ b/docs/en/plugins.md @@ -115,6 +115,10 @@ appropriate parts of your application. The hooks are: collection. - `services` Used to register application container services. This is a good opportunity to setup additional objects that need access to the container. +- `eventListeners` Used to register global event listener classes with the + application's event manager. +- `events` Used to register global events that require custom registration + logic. By default, all plugins hooks are enabled. You can disable hooks by using the related options of the `plugin load` command: @@ -311,6 +315,7 @@ use Cake\Core\BasePlugin; use Cake\Core\ContainerInterface; use Cake\Core\PluginApplicationInterface; use Cake\Console\CommandCollection; +use Cake\Event\EventManagerInterface; use Cake\Http\MiddlewareQueue; use Cake\Routing\RouteBuilder; @@ -369,6 +374,26 @@ class ContactManagerPlugin extends BasePlugin { // Add your services here } + + /** + * @return list> + */ + public function eventListeners(): array + { + return [ + // Add your event listeners here. + ]; + } + + /** + * @inheritDoc + */ + public function events(EventManagerInterface $eventManager): EventManagerInterface + { + // Add custom event registration logic here. + + return $eventManager; + } } ```