New Feature in Drupal 9.3: Entity Bundle Classes

https://unsplash.com/

Entity Bundle Classes is a new feature in Drupal 9.3.0. It allows for more control of entity bundles within Drupal and provides a number of benefits over the former way of using hooks.

Drupal makes extensive use of entities to manage content and configuration. User details, pages of content, and taxonomy terms are just a few of the examples that are represented in a Drupal site as content entities. Different types of similar configuration are called configuration entities, which would include text format settings or even date formats.

Content entities are normally fields, and in the case of content types and taxonomy terms, Drupal allows users to generate their own variants. These different variants of content entities are called bundles.

Entities define the types of objects found in a Drupal site, bundles represent the different sub-types of these entities. For example, the node is the core entity type for creating pages of content, which we sub-type into bundles to create pages, articles, events, or whatever you want to represent on the site.

Why Entity Bundles are Useful?

To get the context of why these classes were introduced and what kind of problems it solves, let’s look at the explanation given by Philip Norton.

"Take the following code, which might be found in many modules and themes in the Drupal 8/9 world.


/**
 * Implements hook_preprocess_node().
 */
function mytheme_preprocess_node(&$variables) {
  /* @var $node \Drupal\node\NodeInterface */
  $node = $variables['node'];
  if ($node->getType() == 'article') {
    $variables['colour'] = $node->get('field_article_colour')->getValue()[0];
  }
}

In the code above we are using the hook_preprocess_node() hook to intercept the rendering of a node. We then use the bundle name to find out the type of node being passed to the hook before getting a value of a field from that node and setting it to a variable in the template.

Whilst this code works, it tends to be quite fragile. Writing if statements like this to catch which type of node has been passed is fine, but with fields used by more than one content type (e.g. the body field) this simple check soon becomes a bit tricky to manage. It's easy to add more conditions to the if statement until you end up with a complex function peppered with conditions. It's not uncommon to come across preprocess hooks that are hundreds of lines long (which is bad practice if you can't tell).

Grabbing values from fields to pass to the template like this is ok (there are better ways to do this even without bundle classes) but in practice, this can become a weak point in your code. In recent years I have swapped to using the hasField() method of the entity to check to see if the field exists before using it, rather than any direct comparisons with the bundle type. This allows slightly more robust code that can be adapted to other content types without any code changes.

There is another issue here in the form of a lack of encapsulation around hooks that change the way in which entities operate in a Drupal site. What I mean by this is that there is nothing to stop multiple themes or modules from implementing the same hook and changing the template in some way. The problem is deeper than this as there are lots of hooks that change the way in which entities operate, and they don't have to be collected together in the same place.

As an example of another hook, it is possible to add a hook_entity_access() hook to allow (or disallow) access to entities in certain operations. The following code will return an access denied page if a user who does not have access to the site administration pages attempts to view it as an article content type.



/**
 * Implements hook_entity_access().
 */
function mymodule_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account)
{
  if ($operation == 'view' && $entity->bundle() == 'article') {
    return AccessResult::forbiddenIf(!$account->hasPermission('access administration pages'));
  }
  return AccessResult::neutral();
}

My point here is that this hook can be placed in a different module than the preprocess hook above. This means that although both hooks interact with the same entity, they are in different parts of the codebase. It's common for preprocess hooks to be placed in the theme, but it depends on the style of the developer making the function. Different hooks can be placed in different modules and themes throughout a Drupal site.

This "action at a distance" anti-pattern is quite prevalent in Drupal, just due to the way in which hooks work. You might find that different modules will implement different sets of hooks that can often work against each other. Turning off modules can also have unpredictable results if it disables a certain hook that your entity was reliant upon.

This is why it's really important to employ behavioral testing on a Drupal site as there can be a disconnect between what you test using Drupal's testing frameworks and what actually happens on the site.

Entity bundle classes attempt to solve some of these problems by moving the "hook-able" functionality an entity might need into a single class. Or at the very least, a hierarchy of classes.”

Note: The vision of this web portal is to help promote news and stories around the Drupal community and promote and celebrate the people and organizations in the community. We strive to create and distribute our content based on these content policy. If you see any omission/variation on this please let us know in the comments below and we will try to address the issue as best we can.

Advertisement Here

Upcoming Events

Latest Opportunities

Advertisement Here

Call for Support