I'd like a isDirty($bean) function in my code

I find it very useful in my Laravel code and now I kind of need it in my sugar code.  I have many external sources putting data in my system. We have many custom APIs that save data or update data. But sometimes these external systems send Data it is exactly the same as the data that's already in the database, my system does the save anyway which kicks off workflows BPM's logic hooks yada yada yada.  All of this is unnecessary since nothing has really changed. What I'd like to do is before I save anything I'd like to check to see if there are any differences. But there's just no good way to do this, and I'm kind of worried about slowing down the system too much. 

My first attempt is here (I updated it after  answered)

<?php
/**
 * Checks to see if any field in a beam has changed in relation to what's in
 * the database right now
 *
 * It does not check many to many relationships
 *
 * @param SugarBean $bean
 * @return bool
 */
function isDirty(SugarBean $bean): bool
{
	$tableName = $bean->table_name;
	$beanID = $bean->id;
	if (empty($beanID) || empty($tableName)) return false;
	$persistedState = is_array($bean->fetched_row)
		? array_merge($bean->fetched_row, $bean->fetched_rel_row)
		: [];
	foreach ($persistedState as $fieldName => $fieldValue) {
		if ($fieldName !== 'id_c' && $bean->$fieldName != $fieldValue) {
			return true;
		}
	}
	return false;
}
?>

This works well enough for what I need, I'm not sending saves from the UI through it, just calls to my custom APIs so it doesnt affect users at all but still, its not terribly elegant.

Anyone got a better way to do this?

  • in the Save function of the SugarBean already checks if it's an update. This information is passed along to the logic_hooks in the arguments parameter. I believe it also tells you which fields are changed. 

  • Sure it checks to see if it's an update, but it doesn't check to see if any of the fields are change does it?  I just wanna stop the save if I'm not actually changing data in the database.

  • Check this function in the saveData

    $this->dataChanges = $this->db->getDataChanges($this);
        public function getDataChanges(SugarBean &$bean, array $options = [])
        {
            $persistedState = is_array($bean->fetched_row)
                ? array_merge($bean->fetched_row, $bean->fetched_rel_row)
                : [];
    
            return $this->getStateChanges($bean, $persistedState, $options);
        }
    

  • It passes the changed data to the logic_hooks in the arguments 

            $this->call_custom_logic('after_save', array(
                'isUpdate' => $isUpdate,
                /* The usage of the dataChanges element is discouraged in favor of stateChanges */
                'dataChanges' => $this->dataChanges,
                'stateChanges' => $this->stateChanges,
            ));
    

  • Are you using the UPSERT API?

  • I can't believe I forgot about this, this is the code that feeds the array to an after save logic hook.  It's good to know, but that code is far more complex than I need it to be. It has to do a lot of translations that I don't really care about. I just need to know if the value changed.

    Although it does show me that I could use the fetched_row of the bean instead of making another SQL call so that would make my code smaller and faster.  No need for that function the checks for a custom table anymore. So that's a lot better actually.

  • I am not, I am doing a straight bean saves because I have to kick off workflows and BPM's when applicable.

  • The UPSERT API is an OOTB API in Sugar 11; it will determine if it's an insert or and update so you don't have to figure that out in your code

  • Yeah we won't be on version 11+ until mid next year at best. We're stuck on 10.0.5 till then.

  • Hi

    just a thought, have you considered using the array_diff_assoc function in PHP to compare the two arrays instead of looping though each field?

    I'm not sure how much of a performance gain you will get, but I suspect it may be more efficient than the foreach.

    Also, do you have checks to make sure you are not inserting duplicates as new records?

    Like you I have APIs that insert data in Sugar, and not all are under my control.

    As I mentioned to Matt at the "Uncon" Roundtable after the conference on Wednesday, I'm tired of seeing duplicate records that would have been caught by the duplicate check had they been entered in the interface slip through the cracks via APIs, and I'm tired of people asking about resulting blank dropdowns that are not really empty but were populated with values that are not defined in the corresponding Dropdown list..

    I guess I'm asking what else do you do to keep API data clean(er)... any tips would be more than welcome.

    thanks,

    FrancescaS