In the new version announcement something has caught my eye. From the title you know it is the Hook attribute.
For the people who are not familiar with php attributes, I wrote a post about it a while ago.
The way you needed to add hooks was an eyesore for me since Drupal 8 moved to a object-oriented way of structuring the code.
Using the module name to prefix the functions and using the .module file to add all the functions had a very spaghetti code feel for me.
And now they almost fixed it. Almost because there are a bunch of hooks that are still procedural. The plan is to remove the procedural hooks in Drupal 12, so in the next minor Drupal versions we will see those hooks disappearing.
What are the benefits?
Instead of adding functions to the .module file, the hooks are in the src directory of the module.
I suggest to use a Hooks subdirectory for easier identification. Or add the Hooks suffix to the class name.
Because it is an attribute you can bind multiple hooks to the same method.
// module.module
function module_comment_insert(CommentInterface $comment) {
module_comment_manipulation($comment);
}
function module_comment_update(CommentInterface $comment) {
module_comment_manipulation($comment);
}
function module_comment_manipulation(CommentInterface $comment) {
// do something
}
// with Hook attribute
class CommentHooks {
#[Hook('comment_insert')]
#[Hook('comment_update')]
public function commentInsertOrUpdate(CommentInterface $comment) {
// do something
}
}
For the people that maintain modules for Drupal versions before 11.1 there is an extra attribute, LegacyHook. This allows you to move the code of the hook to the class with hook attribute. And the old versions of Drupal will execute the function in the .module file, but the newer versions will only execute the class method.
// module.module
#[LegacyHook]
function module_comment_insert(CommentInterface $comment) {
new CommentHooks()->commentInsertOrUpdate($comment);
}
#[LegacyHook]
function module_comment_update(CommentInterface $comment) {
new CommentHooks()->commentInsertOrUpdate($comment);
}
How to use the Hook attribute
As you can see from the previous code examples the attribute is added to the method.
But you can also add the method to the class.
#[Hook('comment_insert')]
#[Hook('comment_update')]
class CommentManipulationHook {
public function __invoke(CommentInterface $comment) {
// do something
}
}
As I show in the example I suggest to make the class name more descriptive. And use the suffix Hook instead of Hooks.
You can add the Hook attributes to the class and add the method as the second parameter. I don't recommend this, In that case it is cleaner to add the attribute to the method.
There is a third Hook parameter, module. And this allows you to execute a hook class from another module. For example #hook('comment_insert', 'commentInsert', 'my_comment_module')
.
I have been thinking about a use case for this, but i couldn't find any.
If you know one, let me know.
Conclusion
I love to see Drupal code moving in the right direction.
The one thing that bothered me is that the hooks are magic constants. But the plan is to all the hooks attributes with as base class the Hook attribute. So instead of #[Hook('comment_insert')]
it will be #[CommentInsert]
.
Another way they could do it is by using enums, grouped by module.
enum CommentHooks {
case Insert;
case Update;
}
The information in this post is based on the documentation and the quick look i had of the implementation. When I have tested the feature there will be updates to post or an additional post.
Top comments (0)