On occasion I run into a logic hook use case that ultimately exposes a limitation of the framework, or generates peculiar challenges during its implementation. Lets review some of these items in hopes they may help you avoid frustration and save you some time.
before_save vs. after_save
It is not uncommon to stumble upon references to either of these terms as one reads about logic hooks. They refer to the timing of the execution of your logic hook code. A before_save hook triggers your code before the record a SugarCRM user is interacting with is physically written to the database, and an after_save hook triggers it after the data has been written to the database.
While those conditions are easy to understand, the same cannot be said for the technical subtleties each carry. It is important to understand these subtleties because some may require one to change from using before_save instead of after_save or vice-versa. More importantly, being aware of these subtleties helps us reduce the amount of time that would otherwise be wasted on troubleshooting the problems the subtleties bring about.
When it comes to a before_save trigger, timing is indeed everything. And this is the thing you most need to be concerned about. Here are a couple of related examples of some behaviors you should expect to see:
- Related records created via the hook may not immediately appear on the corresponding subpanel.
- The value in the case_number field from the Cases module will not be available.
- Using $object->retrieve() to access the record in question will fail.
Coincidentally, using an after_save hook in place of a before_save hook should resolve all of the above issues. However, there is a very important point about the after_save hook which one must remember: one cannot access custom fields by default.
Normally, one can access fields from a record by means of the $bean object. That allows one to read or change the values of the record that triggered the logic hook. To do this, one would use code similar to the following: $bean->field_name or $bean->my_custom_field_c.
For scenarios where an after_save hook is being used, the custom fields must be reloaded. Without reloading them, a line of code such as $bean->my_custom_field_c would not work, either for read or write purposes. So how do we reload the custom fields? It is rather simple.
To solve this problem, simply add the following code prior to any code intended to access a custom field:
$bean->custom_fields->retrieve();
Are there some other gotchas that you have encountered? If so, please share them and how you solved them.
Can you add a bug for this part...
ReplyDelete" For scenarios where an after_save hook is being used, the custom fields must be reloaded. Without reloading them, a line of code such as $bean->my_custom_field_c would not work, either for read or write purposes. So how do we reload the custom fields? It is rather simple.
To solve this problem, simply add the following code prior to any code intended to access a custom field:
$bean->custom_fields->retrieve();"
Sure thing. I always assumed it was intended behavior (as odd as it is) given its been there forever.
DeleteI was able to access the custom fields without adding
Delete$bean->custom_fields->retrieve();
so, it seems the code is patched, and also it has been almost 4 years now.
The case_number field does does not contain a value when using an after_save() or before_save() hook. See http://forums.sugarcrm.com/f6/cases-logic-hook-80689
ReplyDeleteWhat I've done in the past is use the function mysql_insert_id() within an after_save hook context to get the value. Of course, this only works on MySQL.
DeleteIn this case I've also done a retrieve() on the bean after_save to make sure the case number has been loaded.
DeleteThis is a great topic. I think I'll write up a some of my notes on the subject and post in a few days. Good stuff, as always.
ReplyDeleteCool! Post your link when you've got it done.
DeleteGreat post. How about if I want to integrate sugar in an external application? after saving the record, I need to do a parameter passing (http://localhost/extapp?case_date='2012-07-30'&subject='Test Case') to record case details in a different application
ReplyDeleteYou should be able to do that via cURL, as described here:
Deletehttp://davidwalsh.name/execute-http-post-php-curl
Thanks Angel, I've been working with some hooks recently for the JJW Design Google Maps project on Source Forge. It seems simple, until you start working with the differences in the after_save hook. Your post headed me in the right direction. Thanks, Jeff
ReplyDeleteYou are welcome. Happy to be of help.
DeleteNever save or create a new record in another module in before_save of logic_hook module. If save event fails, you will have corrupted your database. Store a session variable and do it in 'after_save'
ReplyDeleteThanks for the blog.
ReplyDeleteIs before_save hook the right place to add validations, for instance wanted the email-id/SSN entered to be unique in the system (fail the save if it exists already and prompt failure back in the edit screen etc)
No, logic hooks do not interact with the interface. As a result, you would not have a way to stop the save with a prompt on the screen, alerting the user that the value is invalid, etc.
DeleteYou need to do that via custom Javascript. You can also leverage the YUI and jQuery components that are part of Sugar.
See here for an example of inserting custom JS: http://cheleguanaco.blogspot.com/2012/08/sugarcrm-customization-utilizing-custom.html
You can interrupt the save and redirect the user back to the original record and use the SugarCRM error handling code to display an error message in a before_save hook situation
DeleteJohn Mertic wrote a post about it on the SugarCRM blog (http://developers.sugarcrm.com/wordpress/2013/01/18/redirecting-to-another-page-inside-a-php-script-in-sugar/) and I wrote a very quick addition using the appendErrorMessage, so that the problem(s) can be displayed to the user:
http://austgate.co.uk/2013/02/showing-error-messages-and-redirecting-in-sugarcrm/