after_relationship_add logic hook triggering two related record saves which overwrite updated data

I have two custom modules (“sctrl_tc_branch_members” and “tsp_caseEntry”) which have a many-to-many relationship. When I add or delete a relationship between the two I want to updated a field on tsp_caseEntry (“tccount”, an integer type) with the number of “branch members” that are related to it. I have the same code triggering on after_relationship_add and after_relationship_delete events.

At first glance, when I add a related record the code does not appear to work. When deleting it does work. After adding some debug statements to the tsp_caseEntry before_save and after_save logic hook events it has become apparent that on an after_relationship_add event that the tsp_caseEntry record in question gets saved twice: first with good “tccount” data and the second with the data it had in the field from between the new “tccount” was calculated. On an after_relationship_delete the tsp_caseEntry record is only saved once with the good, updated “tccount” data. How can I get the “add” operation to work?

Below are some pertinent files and suitecrm.log readouts (note that I didn’t post the before_save and after_save logic hook code for the tsp_caseEntry but it only contains the debug logging code shown in the suitecrm.log).

/var/www/suitecrm/suitecrm.log (when adding a connection between modules through a subpanel on a record)


Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***TcBranchMembersAfterRelationshipAdd*** Got caseEntry_bean:e1f84888-da08-a15e-c37f-5af35e7e46f1
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***TcBranchMembersAfterRelationshipAdd*** 1 current tccount: 9  new tccount:10
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***TcBranchMembersAfterRelationshipAdd*** 2 current tccount: 10  new tccount:10
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry beforeSave*** Entered
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry beforeSave*** tccount:10
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry beforeSave*** id:e1f84888-da08-a15e-c37f-5af35e7e46f1
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry beforeSave*** event:before_save
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry afterSave*** Entered
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry afterSave*** tccount:10
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry afterSave*** id:e1f84888-da08-a15e-c37f-5af35e7e46f1
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry afterSave*** event:after_save
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry beforeSave*** Entered
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry beforeSave*** tccount:9
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry beforeSave*** id:e1f84888-da08-a15e-c37f-5af35e7e46f1
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry beforeSave*** event:before_save
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry afterSave*** Entered
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry afterSave*** tccount:9
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry afterSave*** id:e1f84888-da08-a15e-c37f-5af35e7e46f1
Thu May 10 13:48:30 2018 [27217][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry afterSave*** event:after_save

/var/www/suitecrm/suitecrm.log (when deleting a connection between modules through a subpanel on a record)


Thu May 10 13:47:41 2018 [27218][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***TcBranchMembersAfterRelationshipAdd*** Entered
Thu May 10 13:47:41 2018 [27218][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***TcBranchMembersAfterRelationshipAdd*** Got caseEntry_bean:e1f84888-da08-a15e-c37f-5af35e7e46f1
Thu May 10 13:47:41 2018 [27218][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***TcBranchMembersAfterRelationshipAdd*** 1 current tccount: 3  new tccount:9
Thu May 10 13:47:41 2018 [27218][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***TcBranchMembersAfterRelationshipAdd*** 2 current tccount: 9  new tccount:9
Thu May 10 13:47:41 2018 [27218][316cc89c-7590-6a43-308c-57923a9221a7][ERROR] Warning: Multiple links found for relationship opportunities_contacts_1 within module Opportunities
Thu May 10 13:47:41 2018 [27218][316cc89c-7590-6a43-308c-57923a9221a7][ERROR] Warning: Multiple links found for relationship opportunities_contacts_1 within module Contacts
Thu May 10 13:47:41 2018 [27218][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry beforeSave*** Entered
Thu May 10 13:47:41 2018 [27218][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry beforeSave*** tccount:9
Thu May 10 13:47:41 2018 [27218][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry beforeSave*** id:e1f84888-da08-a15e-c37f-5af35e7e46f1
Thu May 10 13:47:41 2018 [27218][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry beforeSave*** event:before_save
Thu May 10 13:47:41 2018 [27218][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry afterSave*** Entered
Thu May 10 13:47:41 2018 [27218][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry afterSave*** tccount:9
Thu May 10 13:47:41 2018 [27218][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry afterSave*** id:e1f84888-da08-a15e-c37f-5af35e7e46f1
Thu May 10 13:47:41 2018 [27218][316cc89c-7590-6a43-308c-57923a9221a7][FATAL] ***caseEntry afterSave*** event:after_save

/var/www/suitecrm/custom/modules/sctrl_tc_branch_members/logic_hooks.php


<?php

    $hook_version = 1;
    if (!isset($hook_array) || !is_array($hook_array)) $hook_array = array();
    $hook_array['after_relationship_add'] = Array();
    $hook_array['after_relationship_add'][] = Array(
        1,
        'Update tccount',
        'custom/modules/sctrl_tc_branch_members/tcBranchMembersHookLogic.php',
        'classTcBranchMembersHookLogic',
        'TcBranchMembersAfterRelationshipAdd'
    );
    $hook_array['after_relationship_delete'] = Array();
    $hook_array['after_relationship_delete'][] = Array(
        1,
        'Update tccount',
        'custom/modules/sctrl_tc_branch_members/tcBranchMembersHookLogic.php',
        'classTcBranchMembersHookLogic',
        'TcBranchMembersAfterRelationshipAdd'
    );

/var/www/suitecrm/custom/modules/sctrl_tc_branch_members/tcBranchMembersHookLogic.php


<?php
    if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

    class classTcBranchMembersHookLogic
    {    
        static $already_ran = false;
        function TcBranchMembersAfterRelationshipAdd ($bean, $event, $arguments)
        {   
            if(self::$already_ran == true) return;
            self::$already_ran = true;
            if ($arguments['related_module'] =='tsp_caseEntry'){
                $GLOBALS['log']->fatal('***TcBranchMembersAfterRelationshipAdd*** Entered');
                $caseEntry_bean  = BeanFactory::getBean('tsp_caseEntry', $arguments['related_id']);
                $GLOBALS['log']->fatal('***TcBranchMembersAfterRelationshipAdd*** Got caseEntry_bean:' . $caseEntry_bean->id);
                if ($caseEntry_bean->load_relationship('tsp_caseentry_sctrl_tc_branch_members'))
                {
                    $tcCount = 0;
                    foreach ($caseEntry_bean->tsp_caseentry_sctrl_tc_branch_members->getBeans() as $members) {
                        $tcCount = $tcCount + 1;
                    }
                }
                $GLOBALS['log']->fatal('***TcBranchMembersAfterRelationshipAdd*** 1 current tccount: ' . $caseEntry_bean->tccount  . '  new tccount:' . $tcCount);
                $caseEntry_bean->tccount = intval($tcCount);
                $GLOBALS['log']->fatal('***TcBranchMembersAfterRelationshipAdd*** 2 current tccount: ' . $caseEntry_bean->tccount  . '  new tccount:' . $tcCount);
                $caseEntry_bean->save();
            }
        }
    }

I don’t have time at the moment to go into this in depth, but…

  • I believe it is normal for a relationship_add hook to be called twice, once for each side of the relationship. You should be able to tell the two apart by looking at the parameters (you’ll have to explore or Google for the specifics)

  • remember that if you save beans inside hooks, you’re causing new hook invocations

Thanks for the response. Through my research of how to implement this logic hook I did see that the after_relationship_add will get called from both sides and that’s why I’m conditionally executing this code based on the value of the related module argument. One thing I didn’t try yet was executing on the opposite direction by changing ‘related module’ to just ‘module’ but I’m not sure that would make a difference.

It’s clear from the logs that my conditional code is only executing once. The code is incrementing the ‘tccount’ field like it should but then another version of the same record is getting saved which looks like it was generated before my conditional code ran.