CiviCRM Community Forums (archive)

*

News:

Have a question about CiviCRM?
Get it answered quickly at the new
CiviCRM Stack Exchange Q+A site

This forum was archived on 25 November 2017. Learn more.
How to get involved.
What to do if you think you've found a bug.



  • CiviCRM Community Forums (archive) »
  • Old sections (read-only, deprecated) »
  • Developer Discussion »
  • APIs and Hooks (Moderator: Donald Lobo) »
  • On custom field edit, need before and after value.
Pages: [1]

Author Topic: On custom field edit, need before and after value.  (Read 737 times)

konadave

  • I’m new here
  • *
  • Posts: 19
  • Karma: 0
  • CiviCRM version: 4.2.6
  • CMS version: Drupal 7.17
  • MySQL version: 5.1.66
  • PHP version: 5.3.19
On custom field edit, need before and after value.
December 08, 2012, 05:48:23 pm
This question is in relation to my IMBA/CAMBr integration extension, posted under Developer Discussion.

I need to be notified when a custom field is edited, and I need to know both the previous and current value for the field. The hook_civicrm_custom hook is called after the DB write, the $params passed to it do not include the previous value, and there is no "pre" counterpart for it. After looking at the other available hooks, I decided that I might be able to do something with the form hooks. After logging calls to hook_civicrm_postProcess and the data passed to it, I've come up with the following.


function cambr_civicrm_postProcess($formName, &$form) {
	
if (!isset(
$form->_entityId))
	
	
return;
	
$before = $after = array();
	
foreach(
$form->_submitValues as $name => $value) {
	
	
if (
strpos($name, 'custom_') === 0) {
	
	
	
list(
$custom, $field_id, $custom_id) = explode('_', $name);
	
	
	
$after['custom_' . $field_id] = $value;
	
	
	
foreach(
$form->_groupTree as $group_id => $group) {
	
	
	
	
foreach(
$group['fields'] as $field) {
	
	
	
	
	
if (
$field['element_name'] == $name) {
	
	
	
	
	
	
$before['custom_' . $field_id] = $field['element_value'];
	
	
	
	
	
	
break;
	
	
	
	
	
}
	
	
	
	
}
	
	
	
}
	
	
}
	
}
	
if (isset(
$after['custom_12']) && ($after['custom_12'] != $before['custom_12'])) {
	
	
// do something with $before
	
	
// do something with $after
	
}
}

This works.

I don't know specifically about CiviCRM, but I know of multiple other systems where instance variables prefaced with an underscore are considered for internal use. As such, I'm slightly concerned about whether this function will continue to work as expected in the long term. Having looked at the docs for HTML_QuickForm and the source for CRM_Core_Form, I do see that I can get to the submitValues with a proper getter, but I cannot find a way to determine the default/previous values that I found in _groupTree.

I'm hoping that somebody that knows better can tell me if there is a proper/better way to do this, and what the chances of this breaking in the future are.

Thank you for your time.

Dave

Erik Hommel

  • Forum Godess / God
  • I live on this forum
  • *****
  • Posts: 1773
  • Karma: 59
    • EE-atWork
  • CiviCRM version: all sorts
  • CMS version: Drupal
  • MySQL version: Ubuntu's latest LTS version
  • PHP version: Ubuntu's latest LTS version
Re: On custom field edit, need before and after value.
December 09, 2012, 04:26:58 am
Hi Dave,

first of all glad it works. I have been struggling with before and after images for a synchronization process in the past too, and solved in mainly the same way. The downside of your current solutions is that you are using the form based postProcess hook. I would recommend using the post hook, which is much more DB related and deals with many objects. If you use it in combination with the pre hook you should be able to get a before and after image. The downside is that these are relatively new hooks so might have to deal with the occasional bug using it. I have succesfully done one synchronization project with the post and pre hooks, but did encounter a core bug that was quite easy to fix.
Good luck!
Erik
Consultant/project manager at EEatWork and CiviCooP (http://www.civicoop.org/)

konadave

  • I’m new here
  • *
  • Posts: 19
  • Karma: 0
  • CiviCRM version: 4.2.6
  • CMS version: Drupal 7.17
  • MySQL version: 5.1.66
  • PHP version: 5.3.19
Re: On custom field edit, need before and after value.
December 09, 2012, 06:34:31 am
Thanks for the reply Erik.

I'm sorry I failed to mention here that pre and post are not called for custom fields. I do use the post hook to capture the standard data that I need.

xavier

  • Forum Godess / God
  • I’m (like) Lobo ;)
  • *****
  • Posts: 4453
  • Karma: 161
    • Tech To The People
  • CiviCRM version: yes probably
  • CMS version: drupal
Re: On custom field edit, need before and after value.
December 09, 2012, 09:38:03 am
Hi,

The custom fields are a bit different than the normal entities. First they can be stored into different custom set, but even if we were to consider each of these set as different it wouldn't work 100%.

Don't want to dig into the detail of the long discussion we've had when we implemented this api, but as some custom sets are unique and some can have multiple value, it became quite complex (and we probably didn't succeed completely making a coherent and logical API ;(

@Dave, your solution seems ok, but beware that you will only get the pre if the modification is via the normal form. You might want to check what happen if you modify the fields using profile or alternate ui. For instance if you use the api to modify it, you will not get the postProcess called.

X+
-Hackathon and data journalism about the European parliament 24-26 jan. Watch out the result

konadave

  • I’m new here
  • *
  • Posts: 19
  • Karma: 0
  • CiviCRM version: 4.2.6
  • CMS version: Drupal 7.17
  • MySQL version: 5.1.66
  • PHP version: 5.3.19
Re: On custom field edit, need before and after value.
December 09, 2012, 03:52:38 pm
I believe I have come up with what would be the best solution. Looking at CRM_Utils_Hook I found that the civicrm_custom hook is called via it's static method custom. Doing a search for "::custom(", I found that the hook is ultimately called from CRM_Core_BAO_CustomValueTable::create. Below is my updated version of this function, minus a bunch of stuff in the middle, that utilizes CRM_Core_BAO_CustomValueTable::getEntityValues to get the previous values before the query is executed, and adds a previous_value key to each field that gets passed to the civicrm_custom hook.

This all takes place on edit only, and works for single or multiple values. Being very new to CiviCRM, I can't say that some other situation may need to be addressed. But, it works for my current needs.

I placed the file at CRM/Core/BAO/CustomValueTable.php of the extension and it works. Of course, if a change is made to this class in the future then this could all break. Ideally, it would be nice if these changes could be made to core.


  
static function create(&$customParams) {
    if (empty(
$customParams) ||
      !
is_array($customParams)
    ) {
      return;
    }

    foreach (
$customParams as $tableName => $tables) {
      foreach (
$tables as $index => $fields) {
        
$sqlOP      = NULL;
        
$hookID     = NULL;
        
$hookOP     = NULL;
        
$entityID   = NULL;
        
$isMultiple = FALSE;
        
$set        = array();
        
$params     = array();
        
$count      = 1;
        
$fieldIDs   = NULL; // add by konadave
        
foreach ($fields as $field) {
          if (!
$sqlOP) {
            
$entityID   = $field['entity_id'];
            
$hookID     = $field['custom_group_id'];
            
$isMultiple = $field['is_multiple'];
            if (
array_key_exists('id', $field)) {
              
$sqlOP          = "UPDATE $tableName ";
              
$where          = " WHERE  id = %{$count}";
              
$params[$count] = array($field['id'], 'Integer');
              
$count++;
              
$hookOP = 'edit';
              
$fieldIDs = array(); // add by konadave
            
}
            else {
              
$sqlOP  = "INSERT INTO $tableName ";
              
$where  = NULL;
              
$hookOP = 'create';
            }
          }
          if (
is_array($fieldIDs)) { // add by konadave
          
	
$fieldIDs[] = $field['custom_field_id'];
          }

          [ 
snip ]

          
// add by konadave
          
if (is_array($fieldIDs)) {
	
	
	
	
$new_fields = array();
	
	
	
	
$pre_values = self::getEntityValues($entityID, NULL, $fieldIDs);
	
	
	
	
foreach(
$fields as $field) {
	
	
	
	
	
$fid = $field['custom_field_id'] . ($field['is_multiple'] ? '_' . $field['id'] : '');
	
	
	
	
	
$field['previous_value'] = isset($pre_values[$fid]) ? $pre_values[$fid] : NULL;
	
	
	
	
	
$new_fields['custom_' . $field['custom_field_id']] = $field;
	
	
	
	
}
	
	
	
	
$fields = $new_fields;
          }
          
// end add by konadave

          
$dao = CRM_Core_DAO::executeQuery($query, $params);

          
CRM_Utils_Hook::custom($hookOP,
            
$hookID,
            
$entityID,
            
$fields
          
);
        }
      }
    }
  }
« Last Edit: December 10, 2012, 05:20:55 am by konadave »

Erik Hommel

  • Forum Godess / God
  • I live on this forum
  • *****
  • Posts: 1773
  • Karma: 59
    • EE-atWork
  • CiviCRM version: all sorts
  • CMS version: Drupal
  • MySQL version: Ubuntu's latest LTS version
  • PHP version: Ubuntu's latest LTS version
Re: On custom field edit, need before and after value.
December 10, 2012, 08:25:20 am
Dave,
did you take a look at what is required to implement the pre and post hooks for the custom fields?
Consultant/project manager at EEatWork and CiviCooP (http://www.civicoop.org/)

Pages: [1]
  • CiviCRM Community Forums (archive) »
  • Old sections (read-only, deprecated) »
  • Developer Discussion »
  • APIs and Hooks (Moderator: Donald Lobo) »
  • On custom field edit, need before and after value.

This forum was archived on 2017-11-26.