Back to top

Use of Triggers & Actions before the node is created to get the Drupal URL Alias

We recently came across a small issue on one of our live site's blog module. Since everything had been working fine, the main challenge in resolving this issue was finding a solution quickly, while keeping intact the code that was already implemented.
 

  • What was the issue?

Whenever an admin or blog editor publishes a blog, an email is to be sent containing a link to view that blog to the approver(s) depending on workflow.

  • Resolution

Implementation

In the implementation we will be focusing on three main functions which are:

  • hook_node_presave

A hook acting on node insert or update. This hook will be called just before a node is created / edited i.e before the entry for the node is created in database.

  • pathauto_create_alias

It is a function of the pathauto module. This function creates and returns an alias.

  • drupal_get_pathalias

It returns an alias of an internal path (e.g. node/1)

So in the implementation of hook_node_presave all data related to the blog (such as title, body, categories, taxonomy, etc.) was available in the $node object - but the "node id" as well as the path alias created by Drupal are not available at this point because the entry for this node has not been made in the database as of yet. Due to unavailability of the "node id", we could not acquire the URL alias. Hence to get the link to this blog post we used pathauto_create_alias() function, which creates the alias without having the "node id".  Here is a snippet from whole code:

MODULENAME_node_presave($node) {

$node_alias = custom_get_node_alias($node);

}

function custom_get_node_alias($node) {

if ($node->is_new) {

 module_load_include('inc', 'pathauto');

 $uri = entity_uri('node', $node);

 $alias = pathauto_create_alias('node', 'insert', $uri['path'], array('node' =>     $node), $node->type, LANGUAGE_NONE);

 $node_alias = $alias['alias'];

}

else if ($node->nid) {

 $node_alias = drupal_get_path_alias('node/' . $node->nid);

}

return $node_alias;

}

The value of $node_alias is the link to the blog. This in turn is included in the automated e-mail.

Something to note about pathauto_create_alias function; this function first checks if the alias is already created, and if so, it will add -0, -1.. and so on at the end of the alias till the alias is unique.

The bug :

The link included in the email notification was resulting in 'page not found'. When observed, it was found that the path created was “../blog/link-to-post-0” but on the site the actual path of the blog was “../blog/link-to-post-1”. The above was happening only in the case of emails on a new post, the path of the link sent along in updated posts was correct.

  • Understanding of the issue and solution:

Understanding:

Now we see with respect to implementation what caused the issue. So when debugged we found we were explicitly calling pathauto_create_alias from hook_node_presave  and it was getting called again from pathauto_node_update_alias, which itself is called from pathauto_node_insert (note that this is a hook on node_insert). When went through the steps that take place during node operations it was realised that hook_node_presave is called before hook_node_insert. And hence the functions pathauto_create_alias  was getting called two times and hence two paths were created.

Solution:

We needed the function to be such that it gets called just after the event of node creation or node update. Hence the solution was 'triggers & actions'.

  • Solution : Triggers & Actions

Actions define what action is to be performed and when to trigger that action. Triggers provide UI for setting these actions. Hence we did following modifications in the code to keep all the code the same but not call 'presave'.

  1. Removed implementation of hook_node_presave and moved that code to a custom function
  2. We defined an action which is done by using hook_action_info and in this:
    1. We called our function (i.e the one renamed in step 1)
    2. We define the action to be of ‘node’ type which triggers on node_insert and node_update

The code looks like this:

/**

*Implementation of hook_action_info().

*/

function MODULENAME_action_info() {

 return array(

   'MODULENAME_action' => array(

     'type' => 'node',

     'label' => t('Send email when Blog is created or updated'),

     'configurable' => FALSE,

     'triggers' => array('node_insert', 'node_update'),

     ),

 );

}

/**

 * @param  $object, the object that the action will act upon here it's $node

* @param  $context, array containing information about action

*/

function MODULENAME_action($object, $context) {

 MODULENAME_process_email($object);

}

/**

*previously  this function was hook_node_presave

*/

MODULENAME_process_email($node) {

$node_alias = custom_get_node_alias($node);

}

The above solution worked perfectly. To summarize, the issue arose because the function that creates the alias was getting called two times in two different hooks and the solution was to find a hook which will get executed after all default hooks have been called. This in turn, will trigger an action that is the most appropriate choice.

on 21 Jan 2014 by
Qasima Chogale
Software Engineer

I know for sure that what we dwell on is who we become.