Extra Block Types (EBT) - New Layout Builder experience❗

Extra Block Types (EBT) - styled, customizable block types: Slideshows, Tabs, Cards, Accordions and many others. Built-in settings for background, DOM Box, javascript plugins. Experience the future of layout building today.

Demo EBT modules Download EBT modules

❗Extra Paragraph Types (EPT) - New Paragraphs experience

Extra Paragraph Types (EPT) - analogical paragraph based set of modules.

Demo EPT modules Download EPT modules

Scroll

9.11.3. Entity hooks

03/10/2019, by Ivan

In past articles, we have already encountered hooks. In this article, we’ll take a closer look at hooks that help you work with entities.

In this article, you can read in general what hooks are and why they are needed:

http://drupalbook.org/drupal/92-what-hook-drupal-8

We will use hooks to add our custom code that will fire on certain events related to entities: add, delete, update.

You can see all the Drupal hooks on this page:

https://api.drupal.org/api/drupal/core!core.api.php/group/hooks/8.2.x

We will touch only a part of them, which are most often found in custom modules for working with content.

I added all the code on github to the drupalbook_examples module, you can download the module and add it to my website:

https://github.com/levmyshkin/drupalbook8

hook_entity_presave()

https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook_entity_presave/8.6.x

/**
 * Implements hook_entity_presave().
 */
function drupalbook_examples_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
  if ($entity->getEntityTypeId() == 'node' && $entity->getType() == 'article') {
    $entity->title->value = $entity->title->value . 'by ' . date('d-m-Y');
  }
}

Hook_entity_presave () is fired every time an entity is saved.

You don’t need to call $entity->save() inside the hook because the entity object will be modified before saving. In this example, we add to the title of the article the current date the node was saved. If we update the article the next day, the new date will be added again. If you do not delete the date from the title before saving, then the title will become more and more new after re-saving the article. Most often, we check the presence of certain values for fields in the entity when saving, or you can send an email with a notification about the change in the article.

Note that we check the desired entity type first, because the code in hook_entity_presave () works for all entities: content, blocks, comments, taxonomy terms. And also check the bundle of nodes we need.

hook_entity_insert()

https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook_entity_insert/8.6.x

/**
 * Implements hook_entity_insert().
 */
function drupalbook_examples_entity_insert(Drupal\Core\Entity\EntityInterface $entity) {
  if ($entity->getEntityTypeId() == 'node' && $entity->getType() == 'page') {
    $node = Node::create([
      'type'        => 'article',
      'title'       => 'New page created: ' . $entity->title->value,
    ]);
    $node->save();
  }
}

Hook_entity_insert() is called when a new entity is added. So for example, when creating a new page on a site, an article will be created. And if you have previous hooks, then the date will be added to the title of the created article. 

It should be noted that there is a difference between hook_entity_insert () and hook_entity_presave () hooks. Hook_entity_insert () fires only once when an entity is added and does not change the values of the entity fields. That is, if you paste the code from the first hook into the second hook, then this code will not work either:

/**
 * Implements hook_entity_insert().
 */
function drupalbook_examples_entity_insert(Drupal\Core\Entity\EntityInterface $entity) {
  if ($entity->getEntityTypeId() == 'node' && $entity->getType() == 'article') {
    $entity->title->value = $entity->title->value . 'by ' . date('d-m-Y');
  }
}

Of course, you can achieve saving the value of the node:

function your_module_entity_insert(Drupal\Core\Entity\EntityInterface $entity){
  if ($entity->getType() == 'article') {
    drupal_register_shutdown_function('_your_module_post_insert', $entity);
  }
}
 
function _your_module_entity_insert(Drupal\Core\Entity\EntityInterface $entity) {
  if ($entity) {
      $entity->save();
  }
}

But this is not worth doing, it is best to use hook_entity_presave() to change the entity itself when saving, and hook_entity_insert() to change other entities or actions.

hook_entity_update()

https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook_entity_update/8.2.x

/**
 * Implements hook_entity_update().
 */
function drupalbook_examples_entity_update(Drupal\Core\Entity\EntityInterface $entity) {
  if ($entity->getEntityTypeId() == 'node' && $entity->getType() == 'page') {
    \Drupal::messenger()->addMessage('Page has been changed: ' . $entity->title->value);
  }
}

Hook_entity_update () is fired every time an entity is updated. Here it’s worth mentioning right away that it’s not worth updating the fields of the $entity object either, this hook, like hook_entity_insert(), serves to call certain actions that are not related to the data of the fields of the updated entity. This hook should be used again for logging, sending messages or other actions. 

Also, do not execute $entity->save() so as not to call hook_entity_update() again, but if you did, you should make sure that you do not call $entity->save() and don’t looping will occur.

hook_entity_delete()

https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook_entity_delete/8.6.x

Another hook for logging and performing actions after deleting entities.

hook_entity_access()

https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook_entity_access/8.2.x

<?php

use Drupal\Core\Access\AccessResult;
 
...
 
/**
 * Implements hook_entity_access().
 */
function drupalbook_examples_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account) {
  if ($entity->getEntityTypeId() == 'node' && $entity->getType() == 'article' && $operation == 'view' && in_array('administrator', $account->getRoles())) {
    AccessResult::forbidden();
  }
}

?>

In this example, we block access to articles for all users who do not have the administrator role. Of course, if standard role permissions for roles are enough for you to differentiate access to content, then it is better to use permission settings. Hook_entity_access () is needed to flexibly configure access to content, for example, according to a schedule, fulfilling certain conditions by userpoint, karma, user levels, etc. Moreover, if you do the usual restriction of access to certain content through this hook, rewriting the standard access rights, this may cause confusion for another programmer who will continue to engage in the project. From here we come to the main minus of hooks, this is the non-obviousness of the execution of third-party code. If we suppose we don’t know about the existence of a module with implemented hooks, then it will be a mystery to us why our article title changes when saved. Of course, we know that hooks exist and therefore every time you encounter similar behavior, for example, when updating a node, you have to search for the whole entity_presave or entity_update project. Often, programmers follow the code guide and leave comments about the hooks "Implements hook_entity_presave ()", so you can search by the name of the hook, but keep in mind that not all programmers follow the code guide, especially if you got the project from the last command that failed the project. 

We examined only a few hooks, but I think that you should have a certain understanding of where to use the hooks. The more you do Drupal tasks, the more often you will encounter the need to write custom code. If one of the hooks looks like a good place to add code, then you can safely implement it in your custom module.