API Bulk upload and logic hooks

Dear all,

I built a custom Module which should represent better our company structure in CRM. The repository of these data are in an external database. Out from there I built an API interface witch brings the data to CRM. I have experience on this, because we us this for other occasions as well. As I don't know the relation of each record at the moment of the selection in the external database, I decided to relate the records witch resides in CRM at the moment of the import with a logic hook.

This works very well for single POST calls. Because of the data amount I would like to import the data with bulk-calls. Also this works fine until the logic hook should do his job. In that case the hook triggers only once.

Does anybody out there know something about this issue? I tried before_ and after_save hooks. We're on SUGAR 11

This is how my hook looks like

class pskHook {
    
    // before Save Hooks
    public function getAccount ($bean, $event, $arguments) {
        
        $psk = $bean->mg_psk_no_c;
        
        $accounts = BeanFactory::newBean('Accounts');

        $query = new SugarQuery();
        $query->select(array('id', 'mg_psk_c'));
        $query->from($accounts, array('team_security' => true));
        $query->where()->equals('mg_psk_c', $psk);
        $account_list = $query->execute();
                
        if ( $account_list != null ) {
            $rel_name = 'psk_accounts_1';
            if($bean->load_relationship($rel_name)){
                foreach ($account_list as $acct) {
                     $bean->$rel_name->add($acct['id']);
                }
            }
        } 
    }
}

Any inputs are really appreciated. Thank you very much to share your thoughts.

Rene

Parents
  • Hi Rene, 

    Just to confirm if I understand the scenario correctly: 

    - You are doing a bulk POST request to your instance to create records

    - There is an before_save logic hook for the module calling pskHook()

    Expected: The logic hook fires for all records

    Actual: The logic hook only fires for the first

    --

    I think we need more logging to understand better what data is processed. Can you add _ppl() to your logic hook to confirm when the hook is called and two more _ppl() into the two IF statements to make sure those are true for all records? Afterwards check sugarcrm.log to confirm that what you see in the logs. 

    Also is it possible to convert the before_save to an after_save logic hook to see if that makes any difference? 

  • Hi Dennis,

    Thank you for your patience. I opened a ticket at Sugar support with that issue. It looks like they pointed you to this page, for looking into it. Thank you very much for this...

    in the ticket a attached the version which was implemented on my test environment. And that was the hint you gave me.

    Please find this version here:

    <?php
    
    if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
    
    require_once('/www/crm/data/SugarBean.php');
    require_once('/www/crm/include/utils.php');
    
    class pskHook {
    
    static $already_run = false;
    
    // before Save Hooks
    public function getAccount ($bean, $event, $arguments) {
    
    if (self::$already_run == true) return;
    self::$already_run = true;
    
    $psk = $bean->mg_psk_no_c;
    
    //ppl('Hook fires');
    
    $accounts = BeanFactory::newBean('Accounts');
    
    $query = new SugarQuery();
    $query->select(array('id', 'mg_psk_c'));
    $query->from($accounts, array('team_security' => false));
    $query->where()->equals('mg_psk_c', $psk);
    $account_list = $query->execute();
    
    if ($account_list != null ) {
    //_ppl('Account is not empty');
    $rel_name = 'psk_accounts_1';
    if($bean->load_relationship($rel_name)){
    foreach ($account_list as $acct) {
    $bean->$rel_name->add($acct['id']);
    }
    //_ppl('Relation found');
    }
    }
    }
    }

    I did make the experience with other hooks that they sometimes fire several times. That's why I added the "$already_run" variable. In the end, this was the reason why the hook only ran once per bulk.

    I still don't quite understand it, because the hook is called for each record in the bulk call. Otherwise the relation would net set.

    But anyway, in the end it works now.

    Thank you for taking the time to have a look into this

    Best regards
    Rene

Reply
  • Hi Dennis,

    Thank you for your patience. I opened a ticket at Sugar support with that issue. It looks like they pointed you to this page, for looking into it. Thank you very much for this...

    in the ticket a attached the version which was implemented on my test environment. And that was the hint you gave me.

    Please find this version here:

    <?php
    
    if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
    
    require_once('/www/crm/data/SugarBean.php');
    require_once('/www/crm/include/utils.php');
    
    class pskHook {
    
    static $already_run = false;
    
    // before Save Hooks
    public function getAccount ($bean, $event, $arguments) {
    
    if (self::$already_run == true) return;
    self::$already_run = true;
    
    $psk = $bean->mg_psk_no_c;
    
    //ppl('Hook fires');
    
    $accounts = BeanFactory::newBean('Accounts');
    
    $query = new SugarQuery();
    $query->select(array('id', 'mg_psk_c'));
    $query->from($accounts, array('team_security' => false));
    $query->where()->equals('mg_psk_c', $psk);
    $account_list = $query->execute();
    
    if ($account_list != null ) {
    //_ppl('Account is not empty');
    $rel_name = 'psk_accounts_1';
    if($bean->load_relationship($rel_name)){
    foreach ($account_list as $acct) {
    $bean->$rel_name->add($acct['id']);
    }
    //_ppl('Relation found');
    }
    }
    }
    }

    I did make the experience with other hooks that they sometimes fire several times. That's why I added the "$already_run" variable. In the end, this was the reason why the hook only ran once per bulk.

    I still don't quite understand it, because the hook is called for each record in the bulk call. Otherwise the relation would net set.

    But anyway, in the end it works now.

    Thank you for taking the time to have a look into this

    Best regards
    Rene

Children
  • Rene,

    I think you might find that your hook is firing for all records in your bulk but your logic is not firing - mainly because you are telling it not to.

    I think you will find, if you move your debugging line #20 and place it between lines #13 & #15 you get a response. Your debug line is not until after you have put the condition for when not to fire the logic.

    The actual reason it is only firing the logic once is to do with how static variables work in PHP. As they are static they persist and I think what is happening with the bulk load is that the instance of your class pskHook that is created when the first record fires then stays in memory and is always used for the rest as well. Seeing as you set the $already_run static variable (a bit of an oxymoron I know!) to true on the first run it will always be true for all the rest of the records. The logic hook class is not re-instanciated for each record in your bulk call.

    For reference this is exactly the PHP processing rule that is exploited in the way we used to grab the fetched_row values to use in an after_save hook - as can be seen in several forum posts from the past.

    Glad you have your code working now anyway.

    [EDIT] I should have added that the correct way for you to achieve what you are trying to do is to exploit the above PHP rules and to use a static variable to collect the $bean->id value and to make your condition based on whether the statically held value is different to the current $bean->id value. That way, if the hook fires multiple times for the same bean it will not fire your logic but if the instance is used to fire a hook on a new bean then it will run your logic. Something like:

    class pskHook {
    
    static $already_run_bean = '';
    
    // before Save Hooks
    public function getAccount ($bean, $event, $arguments) {
    
    if (self::$already_run_bean == $bean->id) return;
    self::$already_run_bean = $bean->id;
    
    ...

    Thanks,

    JH.