Sunday, January 14, 2018

Adding custom, global, upgrade-safe PHP functions to SugarCRM

PHP Functions
This falls into the category of hard to find.  It is clearly outlined in the SugarCRM Developers Guide but I have never seen anyone use it.  There were no postings on the message boards about it and I simply stumbled upon it one day.  I wanted to consolidate some code written by others, there were functions that were repeated over and over again in module after module.  So I wanted to find a way to easily move these functions into someplace that was shareable by all modules and was upgrade safe.



Custom utils is part of the Extensions framework, the file they are compiled into  'custom/application/Ext/Utils/custom_utils.ext.php' is include()d by the AutoLoader in 'include/entrypoint.php', very early in the loading of the app.  Loading this code so early in the scheme of things can be a great asset, it's a great place to stash all kinds of global stuff.  Of course, stashing all kinds of global stuff can be a bad thing, but when its needed it's my go-to repository.  Also, it loads for both REST and standard (index.php) calls to SugarCRM.

The Code


Files with your functions are stored in custom/Extension/application/Ext/Utils/For this it does not matter what you neame the files, but like everything in custom/extensions keep your file names as unique as you can.  I keep my files segregated by function so I have a file with User utils in it and a file with field level functions in it and so on.   These files will be 'compiled' into one file by the infamously misnamed Quick repair and Rebuild.  An example of one of my files might be
<?php
/**
* Checks to see if a value has changed to any value from any value
* @param SugarBean $bean
* @param string $fieldName
* @return bool
*/
function hasFieldChanged(SugarBean $bean, $fieldName) {
if(!isset($bean->$fieldName)) {
//field does not exist
$GLOBALS['log']->fatal(__FUNCTION__ . ": Field {$fieldName} does not exist in {$bean->object_name}.");
return false;
}
if(isset($bean->fetched_row)) {
if(isset($bean->fetched_row[$fieldName])) {
if($bean->fetched_row[$fieldName] == $bean->$fieldName) {
return false;
} else {
return true;
}
} else {
//Field had no value and now it does
return true;
}
} else {
//No way to tell if the field has changed
return false;
}
}
/**
* Checks to see if a value has change to a specific value and
* optionally from a specific value
* @param SugarBean $bean
* @param string $fieldName
* @param string $newValue
* @param string $oldValue
* @return bool
*/
function hasFieldChangedTo(SugarBean $bean, $fieldName, $newValue, $oldValue='') {
if($bean->$fieldName != $newValue ||
$bean->fetched_row[$fieldName] == $bean->$fieldName) {
//It has not changed to the new value or changed at all
return false;
} else {
if(isset($bean->fetched_row)) {
if (!empty($oldValue) && $bean->fetched_row[$fieldName] != $oldValue) {
//it didn't switch from the old value specified
return false;
} else {
//It changed to the new value from some other value
return true;
}
} else {
//There is no way to tell
return false;
}
}
}


This file makes my code more readable by eliminating all the 
if($bean->status == 'shipped' &&
(isset($bean->fetched_row) &&
$bean->fetched_row['status'] != $bean->status)) {
view raw example517c.php hosted with ❤ by GitHub
from my code.  It's much easier to read and understand this
if (hasFieldChangedTo($bean, 'status', 'Shipped')) {
view raw example517b.php hosted with ❤ by GitHub
So other things you might store here might be constants
<?php
//A list of Global Constants
const UNASSIGNED_USER = 'dce140x2-84cd-x0e5-exbf-5511b8548780';
const UNASSIGNED_TEAM = '5bb580x2-07x4-ee0b-c5c8-58xx8179c15f';
const SALES_EXCEPTION_GROUP_USER = '64f824e0-axxd-11e7-ac04-52x504b6x2cb';
const GLOBAL_TEAM = '1';
const ADMIN_USER = '1';
view raw constants.php hosted with ❤ by GitHub
I use this because it is far easier to understand UNASSIGNED_USER than if you are looking at a GUID.  There is no reason I can think of that you couldn't include classes here as well.  I havnt run across a use case for it yet but it's possible.

No comments:

Post a Comment