Implement Entity hook attribute

Created on 27 December 2024, 25 days ago

Problem/Motivation

πŸ“Œ [PP-1] Determine how to implement Form Alters with attributes. Active introduces the basic concept of semi-dynamic attribute classes with a suffix/prefix concept, so it can be discovered and documented.

Entity hooks are some of the most frequently implemented hooks, so it makes sense to have a good DX for them, this issue is about figuring out if and which

They are also trickier and more dynamic than other hooks, because they basically have two variable components.

1. They are either for all entity types or a specific one. hook_entity_insert() vs hook_ENTITY_TYPE_insert(). That means there is no shared prefix between the two. there are also exceptions to this, for no specific reason, such as hook_entity_view_mode_alter(), which has no ENTITY_TYPE
2. Then there is the suffix, the actual thing. CRUD hooks like insert/update/create/delete. Access hooks like access, create_access, field_access, view/render hooks like view, view_alter, prepare_view_alter and many. They are invoked in many different places, contrib might add more, the only thing they have in common is that they are related to the entity system.

Given that, I think it's fine to say that 2. isn't actually a dynamic part. It's just many different hooks.

Steps to reproduce

Proposed resolution

One proposal was to deprecate hook_ENTITY_TYPE_something() in favor of hook_entity_ENTITY_TYPE_something(). This would result in all such hooks having a shared entity prefix, both generic and for a specific entity type.

The problem is mentioned in point 2 above. We don't really control invocation of all those hooks. We'd need to make sure that every hook that has the ENTITY_TYPE is prefixed with entity, but it's just a string, invoked in many different places. We can say it's only done for specific hooks, but I'm not 100% sure yet that's worth it. The alternative is we have two classes, e.g. EntityInsert and AnyEntityInsert.

As mentioned, we can say that the second part isn't actually a dynamic part of the hook, just the hook. So it also makes sense to add an attribute class for every hook we think should have one. The question is how we decide that. entity.api.php documents around 70 hooks. I'm not sure all of those really need a specific class, many aren't actually that often used. CRUD and access I think are the most common ones, there's also operations, entity type alter being fairly common. Core in total has around 370 results for "Implements hook_entity_..." (case insensitive).

To summarize, the decision is if we want to rename and deprecate non-entity prefixed hooks, likely in groups so we can have:

// all entity types:
#[EntityInsert]

// node entities only:
#[EntityInsert('node')]

Or if we define two classes and go with:

// all entity types:
#[AnyEntityInsert]

// node entities only:
#[EntityInsert('node')]

Remaining tasks

We likely want to use this as a meta issue and create specific sub-tasks.

User interface changes

N/A

Introduced terminology

N/A

API changes

TBD

Data model changes

N/A

Release notes snippet

πŸ“Œ Task
Status

Active

Version

11.0 πŸ”₯

Component

entity system

Created by

πŸ‡¨πŸ‡­Switzerland berdir Switzerland

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @berdir
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Just coming in to say great summary, I think long term if DX is the goal one attribute is better which means we would need to deprecate hook_ENTITY_TYPE_something()

  • πŸ‡¨πŸ‡­Switzerland berdir Switzerland

    One thing that I wasn't sure about, do we have a plan how we will handle documentation of hooks in the future? I can imagine that we'll deprecate the .api.php files and function hook_... stuff and just put those things on the attribute classes? Which also means that we'll want to do attribute classes for all hooks anyway, not just common ones.

    Going one step further, I wonder if we want to also start to somehow use those hook classes when invoking hooks? Something like:

    \Drupal::moduleHandler()->invokeAll(EntityInsert::hook(), $entity);
    \Drupal::moduleHandler()->invokeAll(EntityInsert::hook($entity->getEntityTypeId()), $entity);
    

    That would make the place where this hook is invoked explicitly referenced through a class usage. That's often useful and while for example PHPStorm can do that, it requires magic string matching. Especially entity crud hooks are tricky because they are built dynamically in the storage handler.

Production build 0.71.5 2024