Error message

Deprecated function: implode(): Passing glue string after array is deprecated. Swap the parameters in drupal_get_feeds() (line 394 of /home1/tylerfra/public_html/includes/common.inc).

Drupal - Create a Custom Hook for Other Modules to Use

Category: 

In this example, we'll explore how to make a custom hook with our module so that other modules may use it.

For example, say we were implementing hook_entity_delete() to display a custom message whenever an entity was deleted. But before we display the message, we want to allow other modules to modify the message if they want. We would do that by 'creating' a custom hook by using module_invoke_all(), like so:

In the code example below, we'll 'create' a hook called: hook_change_my_entity_delete_message()

/**
 * Implements hook_entity_delete().
 */
function MY_MODULE_entity_delete($entity, $type) {
  
  // Create our message variables.
  $variables = array('msg' => 'foo');

  // Make sure at least one module implements our hook.
  if (sizeof(module_implements('change_my_entity_delete_message')) > 0) {
    // Call all modules that implement the hook, and let them make changes to $variables.
    $variables = module_invoke_all('change_my_entity_delete_message', $variables);
  }

  // Display the message.
  drupal_set_message($variables['msg']);
}

Now if another module wanted to implement this hook and change the message, they could do that like so:

/**
 * Implements hook_change_my_entity_delete_message().
*/
function SOME_OTHER_MODULE_change_my_entity_delete_message ($variables) {
  $variables['msg'] = 'bar';
  return $variables;
}

Now when an entity is deleted, our implementation of hook_entity_delete will get called and get ready to display the message, then call any module that implements hook_change_my_entity_delete_message(), then return and print out $variables['msg'].

Which in this case the drupal_set_message would display: bar

Comments

you can use drupal_alter() too for altering purposes

tyler's picture

Hi Jason,

Thank you for pointing out drupal_alter(). Now I feel like I should have used that function instead, and will look to that in the future as it seems it could provide a cleaner approach than what is implemented in this post.

Thanks Tyler,

Very helpful. A lot simpler than I thought it was going to be. The documentation for drupal_alter seems to be rather obtuse. If you do a revised blog post using that method let me know. :)

tyler's picture

Thanks Chris. I actually just used drupal_alter() for the first time the other day.

From the code above, this line here would replace the line with the call to module_invoke_all()

drupal_alter('change_my_entity_delete_message', $results);

Then any implementers of the hook would use a function template like this:

/**
 * Implements hook_change_my_entity_delete_message().
*/
function SOME_OTHER_MODULE_change_my_entity_delete_message (&$variables) {
  $variables['msg'] = 'bar';
}

That should work! (didn't test it though)

hook_change_my_entity_delete_message_alter()

You forget the _alter :).

Tyler,

Great blog post and I think drupal_alter() is great, but you also could make it even simpler by using a regular variable without the $variables array and just passing a non-array variable with a reference:

Instead of  $variables = array('msg' => 'foo'); you could simply use $message="foo"

Then, when the hook is implemented, it could use:

/**

 * Implements hook_change_my_entity_delete_message().
*/
function SOME_OTHER_MODULE_change_my_entity_delete_message (&$message) {
  $message = 'bar';
}

What you did works as well. Either way would work.

BC

I wanted to add JS variables from a number of modules and consolidate them as script using drupal_add_html_head(). I was running my function if a static variable wasn't true, to avoid calling the hook time and again.

However, I find modules aren't loaded, so in order to use module_invoke_all, or module_implements, I had to use module_load_all() first.
Seems like this is prolly real clumsy.  I did try using:

if (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL) { return; }

But still, there was only a single module loaded that called the hook as before, so I assume I was in full bootstrap already: Assume it is related to module weights and/or dependencies.

I could set a variable or cache a file to disc or hardcode a config var, and then just load the modules calling the hook. First thought I would mention it in case someone had done it and had some added insight. Thanks in advance!

In the following code, I did not understant oen thing. How module_implements() function will determine how many modules
implemets the 'chnage_my_entity_delete_message' hook, while we are declarining that hook after that code. Hence, how the code
detect something which are created after that code. Thanks, 

 

// Make sure at least one module implements our hook.

if (sizeof(module_implements('change_my_entity_delete_message')) > 0)

{ // Call all modules that implement the hook, and let them make changes to $variables.

$variables = module_invoke_all('change_my_entity_delete_message', $variables); }
tyler's picture

After the module count, we're not declaring the hook, we're invoking it. These days I recommend using this one line approach instead:

drupal_alter('entity_delete_message', $variables);

This would invoke all implementations of hook_entity_delete_message_alter, and is much cleaner and consistent with Drupal.