Logic hooks, as we mentioned last week, allow you to insert business logic at certain points called events. Creating these hooks is fairly straightforward and easy in most cases with SugarCRM. However, there are scenarios that can really leave you scratching your head.
One such scenario is when you notice your hook running multiple times and even into an infinite loop. Take for example the following hook. This is a before_save hook on the Contacts module that reassigns all other contacts with the same first name to the same user. [Note: this is a silly example just for demonstration purposes. There are many ways to prevent a loop in this specific example.]
Warning: this causes an infinite loop so run at your own peril!
<?php | |
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point'); | |
class SO_LoopExample { | |
function reassign($bean, $event, $arguments) | |
{ | |
$GLOBALS['log']->fatal('SO_LoopExample called for contact: '.$bean->name); | |
//get all contacts with the same first name...including this contact | |
//then reassign to the same user. | |
require_once('modules/Contacts/Contact.php'); | |
$contact_bean = new Contact(); | |
$similar_contacts = $contact_bean->get_list('','contacts.first_name="'.$bean->first_name.'"'); | |
foreach($similar_contacts['list'] as $contact_bean) { | |
//only this user deals with contacts with this first name | |
$contact_bean->assigned_user_id = $bean->assigned_user_id; | |
$contact_bean->save(false); //set to true or omit if you want notifications sent | |
} | |
} //end function reassign | |
} //end class SO_LoopExample |
After saving a contact you should see something like this in the logs:
[FATAL] SO_LoopExample called for contact: Joshua Jenkins
[FATAL] SO_LoopExample called for contact: Joshua Ros
[FATAL] SO_LoopExample called for contact: Joshua Ros
[FATAL] SO_LoopExample called for contact: Joshua Ros
...and so on
The problem is that when save() is called it triggers off its own set of logic hooks. This can cause an infinite loop. [Side note: SugarCRM includes a counter in SugarBean.php that tracks and limits how many concurrent logic hook events are running. The default setting for this is 10. However, this does not prevent infinite loops. ] In this case, we want the logic hook to run just the first time. To make sure of that we can utilize a static variable called $already_ran.
<?php | |
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point'); | |
class SO_LoopExample { | |
static $already_ran = false; | |
function reassign($bean, $event, $arguments) | |
{ | |
if(self::$already_ran == true) return; | |
self::$already_ran = true; | |
$GLOBALS['log']->fatal('SO_LoopExample called for contact: '.$bean->name); | |
//get all contacts with the same first name...including this contact | |
//then reassign to the same user. | |
require_once('modules/Contacts/Contact.php'); | |
$contact_bean = new Contact(); | |
$similar_contacts = $contact_bean->get_list('','contacts.first_name="'.$bean->first_name.'"'); | |
foreach($similar_contacts['list'] as $contact_bean) { | |
//only this user deals with contacts with this first name | |
$contact_bean->assigned_user_id = $bean->assigned_user_id; | |
$contact_bean->save(false); //set to true or omit if you want notifications sent | |
} | |
} //end function reassign | |
} //end class SO_LoopExample |
Now only the contact that is being edited ends up being logged:
[FATAL] SO_LoopExample called for contact: Joshua Jenkins
With this change the logic hook runs just once while allowing the other Contact hooks to run as normal, if there are any. For example, there may be logic hooks in place that do something when a contact gets reassigned (besides our own) such as ping a 3rd party notification system. This is why I promote the use of save() in a before_save or after_save hook. There may be critical business logic that needs to be triggered on any Contact save. The challenge is making sure your own hooks can handle that.
There are other mechanisms to ensure proper execution order/frequency such as checking the SESSION or REQUEST. The key to creating hooks that can co-exist in an unknown environment is to make it as specific as possible for the use case at hand.
-
Faye SugarCRM QuickBooks Desktop Integration
FEATUREDFaye SugarCRM QuickBooks Desktop Integration simplifies your accounting and customer relationship management needs by integrating the two software packages. Organizations can use both packages togethe... -
Dropbox Integration for Sugar
FEATUREDSharing files across multiple platforms can be time consuming and confusing. What if you could utilize the sharing power of Dropbox all from within your SugarCRM platform? Dropbox integration combines... -
Business Discovery Session
Ensure your CRM implementation gets off to a flying start with an expert consultation covering everything you need to know, from both a technical and business viewpoint. - Show more addons