A Plugin-based approach to Drupal Hooks.
Introduction
Welcome to the new Drupal hooks! This new approach might feel a bit intimidating at first, but in time you'll feel its advantages. Promised.
Purpose
This module is dedicated to implementing the Drupal Event Subscriber system instead of the default hook system. It also provides base Hook Plugins and Annotations for you to extend, so there's even less code to be written to get the job done.
Structure
We're using on a combination of two methodologies:
- Annotation-based Plugins (5 min read), covered by the Core's Plugin API (20 min read)
- Event Dispatchers and Subscribers (15 min read), covered by the Hook Event Dispatcher contrib module (1 min read)
That's right, we're going to do a lot less hooks implementations in a .module file from now on.
How To Use
This module primarily does two things:
- It registers Event Subscribers that match Events dispatched by the Hook Event Dispatcher contrib module
- It defines Services, Plugin Managers, Plugins and Annotations required for Plugin discovery in other modules
In the end, what you need to do is the following:
- In your custom module, add a new class in the
src/Plugin/crocheteer/Hookdirectory (directory names must match, including capital letters) -
Give a meaningful name to your class following the usual hook declaration, e.g.:
HookEntityPresave(hook_entity_presave)HookNodePresave(hook_ENTITY_TYPE_presave as hook_node_presave)
- Make use of the desired Annotation in your class docblock comment, e.g.
@HookEntityPresave - Fill in necessary annotation properties, e.g.
id,titleandentityTypes - Extend the appropriate Plugin class, e.g.
HookEntityPresavePlugin - Implement the
public function hook()method in your class
Here is a HookEntityPresave example implementation:
namespace Drupal\mymodule\Plugin\crocheteer\Hook;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\crocheteer\Plugin\Hook\Entity\HookEntityPresavePlugin;
/**
* Example class for Hook Entity Presave.
*
* Note that the entityTypes annotation definition property acts as a
* restriction on affected entities. In this case, this Hook will only affect
* node and user entities. Omitting entityTypes or leaving it empty will have
* the Hook act on all entity types, without restrictions.
*
* @HookEntityPresave(
* id = "mymodule_hook_entity_presave_example",
* title = @Translation("My Module: Hook Entity Presave Example"),
* entityTypes = {
* "node",
* "user",
* },
* )
*/
final class HookEntityPresaveExample extends HookEntityPresavePlugin {
/**
* {@inheritdoc}
*
* Do your hook stuff here!
*
* The example below has been adapted from the official documentation (see
* link below). In this case, $entity has been retrieved from the
* $this->event->getEntity() event method.
*
* @link https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook_entity_presave/8.9.x
*/
public function hook() : void {
$entity = $this->event->getEntity();
if ($entity instanceof ContentEntityInterface && $entity->isTranslatable()) {
$route_match = \Drupal::routeMatch();
\Drupal::service('content_translation.synchronizer')->synchronizeFields(
$entity,
$entity->language()->getId(),
$route_match->getParameter('source_langcode')
);
}
}
}
This piece of code does four things:
- Registers a new class
HookEntityPresaveExample -
Uses the
@HookEntityPresaveannotation statement, and assign values to its public properties- The
idandtitleproperties are common to all Hook Annotations - The
entityTypesproperty is used to restrict the affected objects, and these properties will vary per Hook Annotation
- The
- Extends the
HookEntityPresavePluginclass, transforming the class into a Plugin - Implements the
hook()method, where all its hook manipulations reside
Some things to consider:
- Annotation properties are declared in the corresponding Annotation class, e.g.
\Drupal\crocheteer\Annotation\HookEntityPresave - Plugin methods and utility properties are declared in the extended Plugin class, e.g.
\Drupal\crocheteer\Plugin\Hook\Entity\HookEntityPresavePlugin - Try to split your
hook()method logic into private methods as much as possible, for better code readability and quality - Hook-related properties and methods can be retrieved from the referenced
$this->eventEvent object (which belongs to the Hook Event Dispatcher module) -
If you need dependency injection mechanisms in your Hook Plugin, you must implement the
ContainerFactoryPluginInterfaceinterface, which requires the following adjustments to your class:- Implement the
public static function create()method while injecting your new dependencies - Override the
public function __construct()method to add new dependencies (parameters)
- Implement the
Examples
See the example submodule Crocheteer - Example for concrete examples.
Requirements
PHP minimal version required: 7.4
Image credit
Breaking Changes
Along with the 8.x-1.0 release comes a lot of breaking changes, since the module had to be revised and modified multiple times.
If you need to do update the module from the previous 8.x-1.0-beta5 version, it is strongly recommended that you first uninstall the module and then install the 8.x-1.0 release.
For more information on said changes, check out the 8.x-1.0 release notes.
Project information
Minimally maintained
Maintainers monitor issues, but fast responses are not guaranteed.- Project categories: Developer tools
- Ecosystem: Hook Event Dispatcher
4 sites report using this module
- Created by nbeaucage on , updated
Stable releases for this project are covered by the security advisory policy.
Look for the shield icon below.
Releases
Drupal 10 compatibility.
Development version: 8.x-1.x-dev updated 11 Apr 2024 at 18:41 UTC

