CRM-11856 - Update custom values when option values are modified
authorColeman Watts <coleman@civicrm.org>
Tue, 28 Jul 2015 15:00:46 +0000 (11:00 -0400)
committerColeman Watts <coleman@civicrm.org>
Wed, 29 Jul 2015 17:31:34 +0000 (13:31 -0400)
CRM/Core/BAO/CustomOption.php
CRM/Core/BAO/OptionValue.php
api/v3/CustomField.php
tests/phpunit/api/v3/CustomValueTest.php

index 48d86991ab86d416afdca75d2cb73d267a3a12ea..559139a1d11cd3049d65de606a4f25fbde865519 100644 (file)
@@ -416,4 +416,47 @@ SET    {$dao->columnName} = REPLACE( {$dao->columnName}, %1, %2 )";
     return $options;
   }
 
+  /**
+   * When changing the value of an option this is called to update all corresponding custom data
+   *
+   * @param int $optionId
+   * @param string $newValue
+   */
+  public static function updateValue($optionId, $newValue) {
+    $optionValue = new CRM_Core_DAO_OptionValue();
+    $optionValue->id = $optionId;
+    $optionValue->find(TRUE);
+    $oldValue = $optionValue->value;
+    if ($oldValue == $newValue) {
+      return;
+    }
+
+    $customField = new CRM_Core_DAO_CustomField();
+    $customField->option_group_id = $optionValue->option_group_id;
+    $customField->find();
+    while ($customField->fetch()) {
+      $customGroup = new CRM_Core_DAO_CustomGroup();
+      $customGroup->id = $customField->custom_group_id;
+      $customGroup->find(TRUE);
+      if (CRM_Core_BAO_CustomField::isSerialized($customField)) {
+        $params = array(
+          1 => array(CRM_Utils_Array::implodePadded($oldValue), 'String'),
+          2 => array(CRM_Utils_Array::implodePadded($newValue), 'String'),
+          3 => array('%' . CRM_Utils_Array::implodePadded($oldValue) . '%', 'String'),
+        );
+      }
+      else {
+        $params = array(
+          1 => array($oldValue, 'String'),
+          2 => array($newValue, 'String'),
+          3 => array($oldValue, 'String'),
+        );
+      }
+      $sql = "UPDATE `{$customGroup->table_name}` SET `{$customField->column_name}` = REPLACE(`{$customField->column_name}`, %1, %2) WHERE `{$customField->column_name}` LIKE %3";
+      $customGroup->free();
+      CRM_Core_DAO::executeQuery($sql, $params);
+    }
+    $customField->free();
+  }
+
 }
index 64ee49689d2ce0f5139c2b6ec3758760a008d67e..56598ab18b03e7efb217bf7c4ab571d41043b7e6 100644 (file)
@@ -182,6 +182,10 @@ class CRM_Core_BAO_OptionValue extends CRM_Core_DAO_OptionValue {
       $params['is_optgroup'] = CRM_Utils_Array::value('is_optgroup', $params, FALSE);
       $params['filter'] = CRM_Utils_Array::value('filter', $params, FALSE);
     }
+    // Update custom field data to reflect the new value
+    elseif (isset($params['value'])) {
+      CRM_Core_BAO_CustomOption::updateValue($ids['optionValue'], $params['value']);
+    }
 
     // action is taken depending upon the mode
     $optionValue = new CRM_Core_DAO_OptionValue();
index 1d8daa570bf7113202b5a515f665bc3ac4199a89..1b46d6f7e270ad6937e7aebf35523151c0b703ae 100644 (file)
@@ -48,7 +48,18 @@ function civicrm_api3_custom_field_create($params) {
 
   // Array created for passing options in params.
   if (isset($params['option_values']) && is_array($params['option_values'])) {
+    $weight = 0;
     foreach ($params['option_values'] as $key => $value) {
+      // Translate simple key/value pairs into full-blown option values
+      if (!is_array($value)) {
+        $value = array(
+          'label' => $value,
+          'value' => $key,
+          'is_active' => 1,
+          'weight' => $weight,
+        );
+        $key = $weight++;
+      }
       $params['option_label'][$key] = $value['label'];
       $params['option_value'][$key] = $value['value'];
       $params['option_status'][$key] = $value['is_active'];
@@ -80,6 +91,11 @@ function _civicrm_api3_custom_field_create_spec(&$params) {
   $params['label']['api.required'] = 1;
   $params['custom_group_id']['api.required'] = 1;
   $params['is_active']['api.default'] = 1;
+  $params['option_values'] = array(
+    'title' => 'Option Values',
+    'description' => "Pass an array of options (value => label) to create this field's option values",
+  );
+  // TODO: Why expose this to the api at all?
   $params['option_type'] = array(
     'title' => 'Option Type',
     'description' => 'This (boolean) field tells the BAO to create an option group for the field if the field type is appropriate',
index bd7b73dcff3f925e1e07594886a2c86f0cb958b7..ba2c2b171900d3f21c4333e2286c3832efe9bc2a 100644 (file)
@@ -174,4 +174,83 @@ class api_v3_CustomValueTest extends CiviUnitTestCase {
     $this->assertEquals('second multi value 3', $result['values'][$firstCustomField]['latest']);
   }
 
+  /**
+   * Ensure custom data is updated when option values are modified
+   *
+   * @link https://issues.civicrm.org/jira/browse/CRM-11856
+   *
+   * @throws \CiviCRM_API3_Exception
+   */
+  public function testAlterOptionValue() {
+    $selectField = $this->customFieldCreate(array(
+      'custom_group_id' => $this->ids['single']['custom_group_id'],
+      'label' => 'Custom Select',
+      'html_type' => 'Select',
+      'option_values' => array(
+        'one' => 'Option1',
+        'two' => 'Option2',
+        'notone' => 'OptionNotOne',
+      ),
+    ));
+    $selectField = civicrm_api3('customField', 'getsingle', array('id' => $selectField['id']));
+    $radioField = $this->customFieldCreate(array(
+      'custom_group_id' => $this->ids['single']['custom_group_id'],
+      'label' => 'Custom Radio',
+      'html_type' => 'Radio',
+      'option_group_id' => $selectField['option_group_id'],
+    ));
+    $multiSelectField = $this->customFieldCreate(array(
+      'custom_group_id' => $this->ids['single']['custom_group_id'],
+      'label' => 'Custom Multi-Select',
+      'html_type' => 'Multi-Select',
+      'option_group_id' => $selectField['option_group_id'],
+    ));
+    $selectName = 'custom_' . $selectField['id'];
+    $radioName = 'custom_' . $radioField['id'];
+    $multiSelectName = 'custom_' . $multiSelectField['id'];
+    $controlFieldName = 'custom_' . $this->ids['single']['custom_field_id'];
+
+    $params = array(
+      'first_name' => 'abc4',
+      'last_name' => 'xyz4',
+      'contact_type' => 'Individual',
+      'email' => 'man4@yahoo.com',
+      $selectName => 'one',
+      $multiSelectName => array('one', 'two', 'notone'),
+      $radioName => 'notone',
+      // The control group in a science experiment should be unaffected
+      $controlFieldName => 'one',
+    );
+
+    $contact = $this->callAPISuccess('Contact', 'create', $params);
+
+    $result = $this->callAPISuccess('Contact', 'getsingle', array(
+      'id' => $contact['id'],
+      'return' => array($selectName, $multiSelectName),
+    ));
+    $this->assertEquals('one', $result[$selectName]);
+    $this->assertEquals(array('one', 'two', 'notone'), $result[$multiSelectName]);
+
+    $this->callAPISuccess('OptionValue', 'create', array(
+      'value' => 'one-modified',
+      'option_group_id' => $selectField['option_group_id'],
+      'name' => 'Option1',
+      'options' => array(
+        'match-mandatory' => array('option_group_id', 'name'),
+      ),
+    ));
+
+    $result = $this->callAPISuccess('Contact', 'getsingle', array(
+      'id' => $contact['id'],
+      'return' => array($selectName, $multiSelectName, $controlFieldName, $radioName),
+    ));
+    // Ensure the relevant fields have been updated
+    $this->assertEquals('one-modified', $result[$selectName]);
+    $this->assertEquals(array('one-modified', 'two', 'notone'), $result[$multiSelectName]);
+    // This field should not have changed because we didn't alter this option
+    $this->assertEquals('notone', $result[$radioName]);
+    // This should not have changed because this field doesn't use the affected option group
+    $this->assertEquals('one', $result[$controlFieldName]);
+  }
+
 }