APIv4 - Deprecate and stop using PreSaveSubscriber
authorColeman Watts <coleman@civicrm.org>
Thu, 27 Jan 2022 21:09:08 +0000 (16:09 -0500)
committerColeman Watts <coleman@civicrm.org>
Fri, 28 Jan 2022 00:32:10 +0000 (19:32 -0500)
The PreSaveSubscriber allowed APIs to tweak inputs before saving a record,
but it was unreliable because:
- It would run for Create and Update but not Save actions
- It would run before pseudoconstant suffixes had been resolved
- It would run before any formatting or internal processing had taken place

Meanwhile, a new pattern of overriding the create/save/update actions and sharing a trait
has become more common and while a bit more cumbersome to implement, it does not share
those limitations.

This removes all uses of PreSaveSubscriber, refactors those bits into Save traits, and
adds a deprecation warning in case any extension in the Universe happens to have implemented it.

37 files changed:
Civi/Api4/Action/Address/AddressSaveTrait.php
Civi/Api4/Action/Contact/ContactSaveTrait.php [new file with mode: 0644]
Civi/Api4/Action/Contact/Create.php [new file with mode: 0644]
Civi/Api4/Action/Contact/Save.php
Civi/Api4/Action/Contact/Update.php
Civi/Api4/Action/Contribution/ContributionSaveTrait.php [moved from Civi/Api4/Event/Subscriber/ContributionPreSaveSubscriber.php with 56% similarity]
Civi/Api4/Action/Contribution/Create.php [new file with mode: 0644]
Civi/Api4/Action/Contribution/Save.php [new file with mode: 0644]
Civi/Api4/Action/Contribution/Update.php [new file with mode: 0644]
Civi/Api4/Action/CustomField/Create.php [new file with mode: 0644]
Civi/Api4/Action/CustomField/CustomFieldSaveTrait.php [moved from Civi/Api4/Event/Subscriber/CustomFieldPreSaveSubscriber.php with 69% similarity]
Civi/Api4/Action/CustomField/Save.php [new file with mode: 0644]
Civi/Api4/Action/CustomField/Update.php [new file with mode: 0644]
Civi/Api4/Contact.php
Civi/Api4/Contribution.php
Civi/Api4/CustomField.php
Civi/Api4/Event/Subscriber/ContactPreSaveSubscriber.php [deleted file]
Civi/Api4/Event/Subscriber/CustomGroupPreCreationSubscriber.php [deleted file]
Civi/Api4/Event/Subscriber/Generic/PreSaveSubscriber.php
Civi/Api4/Event/Subscriber/OptionValuePreCreationSubscriber.php [deleted file]
Civi/Api4/Service/Spec/Provider/ContactCreationSpecProvider.php
ext/search_kit/tests/phpunit/api/v4/SearchDisplay/SearchRunWithCustomFieldTest.php
tests/phpunit/api/v3/ACLPermissionTest.php
tests/phpunit/api/v4/Action/BasicCustomFieldTest.php
tests/phpunit/api/v4/Action/ChainTest.php
tests/phpunit/api/v4/Action/CreateCustomValueTest.php
tests/phpunit/api/v4/Action/CreateWithOptionGroupTest.php
tests/phpunit/api/v4/Action/CustomContactRefTest.php
tests/phpunit/api/v4/Action/CustomGroupACLTest.php
tests/phpunit/api/v4/Action/CustomValueTest.php
tests/phpunit/api/v4/Action/ExtendFromIndividualTest.php
tests/phpunit/api/v4/Action/PseudoconstantTest.php
tests/phpunit/api/v4/Action/ReplaceTest.php
tests/phpunit/api/v4/Action/UpdateCustomValueTest.php
tests/phpunit/api/v4/DataSets/ConformanceTest.json
tests/phpunit/api/v4/Entity/ContactTypeTest.php
tests/phpunit/api/v4/Spec/SpecGathererTest.php

index de055b08f54b759016158cf5b4723974af04432b..d14fca6a1d9957025554db4812a8cc2ed7724a08 100644 (file)
@@ -13,7 +13,8 @@
 namespace Civi\Api4\Action\Address;
 
 /**
- * @inheritDoc
+ * Code shared by Address create/update/save actions
+ *
  * @method bool getStreetParsing()
  * @method $this setStreetParsing(bool $streetParsing)
  * @method bool getSkipGeocode()
diff --git a/Civi/Api4/Action/Contact/ContactSaveTrait.php b/Civi/Api4/Action/Contact/ContactSaveTrait.php
new file mode 100644 (file)
index 0000000..67a9cca
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Action\Contact;
+
+/**
+ * Code shared by Contact create/update/save actions
+ */
+trait ContactSaveTrait {
+
+  /**
+   * @param array $items
+   * @return array
+   */
+  protected function write(array $items) {
+    foreach ($items as &$contact) {
+      // For some reason the contact BAO requires this for updates
+      if (!empty($contact['id'])) {
+        $contact['contact_id'] = $contact['id'];
+      }
+      elseif (empty($contact['contact_type'])) {
+        // Guess which type of contact is being created
+        if (!empty($contact['organization_name'])) {
+          $contact['contact_type'] = 'Organization';
+        }
+        elseif (!empty($contact['household_name'])) {
+          $contact['contact_type'] = 'Household';
+        }
+        else {
+          $contact['contact_type'] = 'Individual';
+        }
+      }
+    }
+    return parent::write($items);
+  }
+
+}
diff --git a/Civi/Api4/Action/Contact/Create.php b/Civi/Api4/Action/Contact/Create.php
new file mode 100644 (file)
index 0000000..8da7c20
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Action\Contact;
+
+/**
+ * @inheritDoc
+ */
+class Create extends \Civi\Api4\Generic\DAOCreateAction {
+  use ContactSaveTrait;
+
+}
index 5c089093955d895bd36546b0a41aa44b03413153..322e4d51e003b6c7e7731d25d318708d656c734a 100644 (file)
@@ -16,21 +16,6 @@ namespace Civi\Api4\Action\Contact;
  * @inheritDoc
  */
 class Save extends \Civi\Api4\Generic\DAOSaveAction {
-
-  /**
-   * @param array $items
-   * @return array
-   */
-  protected function write(array $items) {
-    $saved = [];
-    foreach ($items as $item) {
-      // For some reason the contact BAO requires this for updates
-      if (!empty($item['id']) && !\CRM_Utils_System::isNull($item['id'])) {
-        $item['contact_id'] = $item['id'];
-      }
-      $saved[] = \CRM_Contact_BAO_Contact::create($item);
-    }
-    return $saved;
-  }
+  use ContactSaveTrait;
 
 }
index 0743d5752c120df6e76a64ae62911a664c103564..d5798dc4d09d4e321fa4d76c0da52fb0e117ee6f 100644 (file)
@@ -16,19 +16,6 @@ namespace Civi\Api4\Action\Contact;
  * @inheritDoc
  */
 class Update extends \Civi\Api4\Generic\DAOUpdateAction {
-
-  /**
-   * @param array $items
-   * @return array
-   */
-  protected function write(array $items) {
-    $saved = [];
-    foreach ($items as $item) {
-      // For some reason the contact BAO requires this for updates
-      $item['contact_id'] = $item['id'];
-      $saved[] = \CRM_Contact_BAO_Contact::create($item);
-    }
-    return $saved;
-  }
+  use ContactSaveTrait;
 
 }
similarity index 56%
rename from Civi/Api4/Event/Subscriber/ContributionPreSaveSubscriber.php
rename to Civi/Api4/Action/Contribution/ContributionSaveTrait.php
index 20f623f817bb8d244a969cfdebcea9abf1a13da2..70ed6fbe1922384fcf3918e0b9909a71626b922f 100644 (file)
  +--------------------------------------------------------------------+
  */
 
-namespace Civi\Api4\Event\Subscriber;
+namespace Civi\Api4\Action\Contribution;
 
-use Civi\Api4\Generic\AbstractAction;
-
-class ContributionPreSaveSubscriber extends Generic\PreSaveSubscriber {
-
-  public function modify(&$record, AbstractAction $request) {
-    // Required by Contribution BAO
-    $record['skipCleanMoney'] = TRUE;
-  }
+/**
+ * Code shared by Contribution create/update/save actions
+ */
+trait ContributionSaveTrait {
 
-  public function applies(AbstractAction $request) {
-    return $request->getEntityName() === 'Contribution';
+  /**
+   * @inheritDoc
+   */
+  protected function write(array $items) {
+    foreach ($items as &$item) {
+      // Required by Contribution BAO
+      $item['skipCleanMoney'] = TRUE;
+    }
+    return parent::write($items);
   }
 
 }
diff --git a/Civi/Api4/Action/Contribution/Create.php b/Civi/Api4/Action/Contribution/Create.php
new file mode 100644 (file)
index 0000000..80a8b7e
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Action\Contribution;
+
+/**
+ * @inheritDoc
+ */
+class Create extends \Civi\Api4\Generic\DAOCreateAction {
+  use ContributionSaveTrait;
+
+}
diff --git a/Civi/Api4/Action/Contribution/Save.php b/Civi/Api4/Action/Contribution/Save.php
new file mode 100644 (file)
index 0000000..d19b3b1
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Action\Contribution;
+
+/**
+ * @inheritDoc
+ */
+class Save extends \Civi\Api4\Generic\DAOSaveAction {
+  use ContributionSaveTrait;
+
+}
diff --git a/Civi/Api4/Action/Contribution/Update.php b/Civi/Api4/Action/Contribution/Update.php
new file mode 100644 (file)
index 0000000..e61df15
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Action\Contribution;
+
+/**
+ * @inheritDoc
+ */
+class Update extends \Civi\Api4\Generic\DAOUpdateAction {
+  use ContributionSaveTrait;
+
+}
diff --git a/Civi/Api4/Action/CustomField/Create.php b/Civi/Api4/Action/CustomField/Create.php
new file mode 100644 (file)
index 0000000..edb1afa
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Action\CustomField;
+
+/**
+ * @inheritDoc
+ */
+class Create extends \Civi\Api4\Generic\DAOCreateAction {
+  use CustomFieldSaveTrait;
+
+}
similarity index 69%
rename from Civi/Api4/Event/Subscriber/CustomFieldPreSaveSubscriber.php
rename to Civi/Api4/Action/CustomField/CustomFieldSaveTrait.php
index 8d8be12420c3e5b1bc282668767b1df115bff0ce..87803fbe2b59993e44875d3a52aef4b59ea2552c 100644 (file)
  +--------------------------------------------------------------------+
  */
 
-namespace Civi\Api4\Event\Subscriber;
+namespace Civi\Api4\Action\CustomField;
 
-use Civi\Api4\Generic\AbstractAction;
-
-class CustomFieldPreSaveSubscriber extends Generic\PreSaveSubscriber {
+/**
+ * Code shared by CustomField create/update/save actions
+ */
+trait CustomFieldSaveTrait {
 
   /**
-   * @var string
+   * @inheritDoc
    */
-  public $supportedOperation = 'create';
+  protected function write(array $items) {
+    foreach ($items as &$field) {
+      if (empty($field['id'])) {
+        self::formatOptionValues($field);
+      }
+    }
+    return parent::write($items);
+  }
 
-  public function modify(&$field, AbstractAction $request) {
+  /**
+   * If 'option_values' have been supplied, reformat it according to the expectations of the BAO
+   *
+   * @param array $field
+   */
+  private static function formatOptionValues(array &$field): void {
+    $field['option_type'] = !empty($field['option_values']);
     if (!empty($field['option_values'])) {
-      $weight = $key = 0;
-      $field['option_label'] = $field['option_value'] = $field['option_status'] = $field['option_weight'] = [];
+      $weight = 0;
+      $field['option_label'] = $field['option_value'] = $field['option_status'] = $field['option_weight'] =
       $field['option_name'] = $field['option_color'] = $field['option_description'] = $field['option_icon'] = [];
       foreach ($field['option_values'] as $key => $value) {
         // Translate simple key/value pairs into full-blown option values
@@ -34,22 +48,16 @@ class CustomFieldPreSaveSubscriber extends Generic\PreSaveSubscriber {
             'id' => $key,
           ];
         }
-        $weight++;
         $field['option_label'][] = $value['label'] ?? $value['name'];
         $field['option_name'][] = $value['name'] ?? NULL;
         $field['option_value'][] = $value['id'];
         $field['option_status'][] = $value['is_active'] ?? 1;
-        $field['option_weight'][] = $value['weight'] ?? $weight;
+        $field['option_weight'][] = $value['weight'] ?? ++$weight;
         $field['option_color'][] = $value['color'] ?? NULL;
         $field['option_description'][] = $value['description'] ?? NULL;
         $field['option_icon'][] = $value['icon'] ?? NULL;
       }
     }
-    $field['option_type'] = !empty($field['option_values']);
-  }
-
-  public function applies(AbstractAction $request) {
-    return $request->getEntityName() === 'CustomField';
   }
 
 }
diff --git a/Civi/Api4/Action/CustomField/Save.php b/Civi/Api4/Action/CustomField/Save.php
new file mode 100644 (file)
index 0000000..d6d3a35
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Action\CustomField;
+
+/**
+ * @inheritDoc
+ */
+class Save extends \Civi\Api4\Generic\DAOSaveAction {
+  use CustomFieldSaveTrait;
+
+}
diff --git a/Civi/Api4/Action/CustomField/Update.php b/Civi/Api4/Action/CustomField/Update.php
new file mode 100644 (file)
index 0000000..443e686
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Action\CustomField;
+
+/**
+ * @inheritDoc
+ */
+class Update extends \Civi\Api4\Generic\DAOUpdateAction {
+  use CustomFieldSaveTrait;
+
+}
index 800d8f6aec9b37f3686f280d594bdbe73e2679d8..cd717f128c3544886168ff202494ef1a865c1973 100644 (file)
@@ -26,6 +26,15 @@ namespace Civi\Api4;
  */
 class Contact extends Generic\DAOEntity {
 
+  /**
+   * @param bool $checkPermissions
+   * @return Action\Contact\Create
+   */
+  public static function create($checkPermissions = TRUE) {
+    return (new Action\Contact\Create(__CLASS__, __FUNCTION__))
+      ->setCheckPermissions($checkPermissions);
+  }
+
   /**
    * @param bool $checkPermissions
    * @return Action\Contact\Update
index 2be2264b61725d7c006a06d14a96baaca89bac71..67d61be1f7b6af624e6451ffe8d37b8d09bbc0a7 100644 (file)
@@ -19,4 +19,31 @@ namespace Civi\Api4;
  */
 class Contribution extends Generic\DAOEntity {
 
+  /**
+   * @param bool $checkPermissions
+   * @return Action\Contribution\Create
+   */
+  public static function create($checkPermissions = TRUE) {
+    return (new Action\Contribution\Create(__CLASS__, __FUNCTION__))
+      ->setCheckPermissions($checkPermissions);
+  }
+
+  /**
+   * @param bool $checkPermissions
+   * @return Action\Contribution\Save
+   */
+  public static function save($checkPermissions = TRUE) {
+    return (new Action\Contribution\Save(__CLASS__, __FUNCTION__))
+      ->setCheckPermissions($checkPermissions);
+  }
+
+  /**
+   * @param bool $checkPermissions
+   * @return Action\Contribution\Update
+   */
+  public static function update($checkPermissions = TRUE) {
+    return (new Action\Contribution\Update(__CLASS__, __FUNCTION__))
+      ->setCheckPermissions($checkPermissions);
+  }
+
 }
index 37be6eb2b45f37b12722bbdac56a6e18ad4d57b8..5751e6492e8e902bd9991216b3e781f13e687065 100644 (file)
@@ -23,4 +23,31 @@ class CustomField extends Generic\DAOEntity {
   use Generic\Traits\ManagedEntity;
   use Generic\Traits\SortableEntity;
 
+  /**
+   * @param bool $checkPermissions
+   * @return Action\CustomField\Create
+   */
+  public static function create($checkPermissions = TRUE) {
+    return (new Action\CustomField\Create(__CLASS__, __FUNCTION__))
+      ->setCheckPermissions($checkPermissions);
+  }
+
+  /**
+   * @param bool $checkPermissions
+   * @return Action\CustomField\Save
+   */
+  public static function save($checkPermissions = TRUE) {
+    return (new Action\CustomField\Save(__CLASS__, __FUNCTION__))
+      ->setCheckPermissions($checkPermissions);
+  }
+
+  /**
+   * @param bool $checkPermissions
+   * @return Action\CustomField\Update
+   */
+  public static function update($checkPermissions = TRUE) {
+    return (new Action\CustomField\Update(__CLASS__, __FUNCTION__))
+      ->setCheckPermissions($checkPermissions);
+  }
+
 }
diff --git a/Civi/Api4/Event/Subscriber/ContactPreSaveSubscriber.php b/Civi/Api4/Event/Subscriber/ContactPreSaveSubscriber.php
deleted file mode 100644 (file)
index 3af614f..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-/*
- +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC. All rights reserved.                        |
- |                                                                    |
- | This work is published under the GNU AGPLv3 license with some      |
- | permitted exceptions and without any warranty. For full license    |
- | and copyright information, see https://civicrm.org/licensing       |
- +--------------------------------------------------------------------+
- */
-
-namespace Civi\Api4\Event\Subscriber;
-
-use Civi\Api4\Generic\AbstractAction;
-
-class ContactPreSaveSubscriber extends Generic\PreSaveSubscriber {
-
-  /**
-   * @var string
-   */
-  public $supportedOperation = 'create';
-
-  public function modify(&$contact, AbstractAction $request) {
-    // Guess which type of contact is being created
-    if (empty($contact['contact_type']) && !empty($contact['organization_name'])) {
-      $contact['contact_type'] = 'Organization';
-    }
-    if (empty($contact['contact_type']) && !empty($contact['household_name'])) {
-      $contact['contact_type'] = 'Household';
-    }
-  }
-
-  public function applies(AbstractAction $request) {
-    return $request->getEntityName() === 'Contact';
-  }
-
-}
diff --git a/Civi/Api4/Event/Subscriber/CustomGroupPreCreationSubscriber.php b/Civi/Api4/Event/Subscriber/CustomGroupPreCreationSubscriber.php
deleted file mode 100644 (file)
index 132c789..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-/*
- +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC. All rights reserved.                        |
- |                                                                    |
- | This work is published under the GNU AGPLv3 license with some      |
- | permitted exceptions and without any warranty. For full license    |
- | and copyright information, see https://civicrm.org/licensing       |
- +--------------------------------------------------------------------+
- */
-
-namespace Civi\Api4\Event\Subscriber;
-
-use Civi\Api4\Generic\DAOCreateAction;
-
-class CustomGroupPreCreationSubscriber extends Generic\PreCreationSubscriber {
-
-  /**
-   * @param \Civi\Api4\Generic\DAOCreateAction $request
-   */
-  protected function modify(DAOCreateAction $request) {
-    $title = $request->getValue('title');
-    $name = $request->getValue('name');
-
-    if (NULL === $title && $name) {
-      $request->addValue('title', $name);
-    }
-  }
-
-  protected function applies(DAOCreateAction $request) {
-    return $request->getEntityName() === 'CustomGroup';
-  }
-
-}
index 8741f842d8b4705d5ff0462c457260cf55ccccf4..68fc4f672f12576874123b4415f5e3ac49fe22b0 100644 (file)
@@ -17,6 +17,9 @@ use Civi\Api4\Generic\AbstractAction;
 use Civi\Api4\Generic\AbstractCreateAction;
 use Civi\Api4\Generic\AbstractUpdateAction;
 
+/**
+ * @deprecated
+ */
 abstract class PreSaveSubscriber extends AbstractPrepareSubscriber {
 
   /**
@@ -32,6 +35,7 @@ abstract class PreSaveSubscriber extends AbstractPrepareSubscriber {
     $apiRequest = $event->getApiRequest();
 
     if ($apiRequest instanceof AbstractAction && $this->applies($apiRequest)) {
+      \CRM_Core_Error::deprecatedWarning("Use of APIv4 'PreSaveSubscriber' is deprecated. '" . get_class($this) . "' should be removed ({$apiRequest->getEntityName()}::{$apiRequest->getActionName()}).");
       if (
         ($apiRequest instanceof AbstractCreateAction && $this->supportedOperation !== 'update') ||
         ($apiRequest instanceof AbstractUpdateAction && $this->supportedOperation !== 'create')
diff --git a/Civi/Api4/Event/Subscriber/OptionValuePreCreationSubscriber.php b/Civi/Api4/Event/Subscriber/OptionValuePreCreationSubscriber.php
deleted file mode 100644 (file)
index 3b37c70..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-
-/*
- +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC. All rights reserved.                        |
- |                                                                    |
- | This work is published under the GNU AGPLv3 license with some      |
- | permitted exceptions and without any warranty. For full license    |
- | and copyright information, see https://civicrm.org/licensing       |
- +--------------------------------------------------------------------+
- */
-
-namespace Civi\Api4\Event\Subscriber;
-
-use Civi\Api4\Generic\DAOCreateAction;
-use Civi\Api4\OptionGroup;
-
-class OptionValuePreCreationSubscriber extends Generic\PreCreationSubscriber {
-
-  /**
-   * @param \Civi\Api4\Generic\DAOCreateAction $request
-   */
-  protected function modify(DAOCreateAction $request) {
-    $this->setOptionGroupId($request);
-  }
-
-  /**
-   * @param \Civi\Api4\Generic\DAOCreateAction $request
-   *
-   * @return bool
-   */
-  protected function applies(DAOCreateAction $request) {
-    return $request->getEntityName() === 'OptionValue';
-  }
-
-  /**
-   * @param \Civi\Api4\Generic\DAOCreateAction $request
-   * @throws \API_Exception
-   * @throws \Exception
-   */
-  private function setOptionGroupId(DAOCreateAction $request) {
-    $optionGroupName = $request->getValue('option_group');
-    if (!$optionGroupName || $request->getValue('option_group_id')) {
-      return;
-    }
-    \CRM_Core_Error::deprecatedFunctionWarning('Use option_group_id:name instead of option_group in APIv4');
-    $optionGroup = OptionGroup::get(FALSE)
-      ->addSelect('id')
-      ->addWhere('name', '=', $optionGroupName)
-      ->execute();
-
-    if ($optionGroup->count() !== 1) {
-      throw new \Exception('Option group name must match only a single group');
-    }
-
-    $request->addValue('option_group_id', $optionGroup->first()['id']);
-  }
-
-}
index ac13bcba8186c904b2951d3b0d68ca75e4b26861..c3d799dd0505cbf5ae404852c23bca202d4b7a1d 100644 (file)
@@ -20,14 +20,8 @@ class ContactCreationSpecProvider implements Generic\SpecProviderInterface {
    * @param \Civi\Api4\Service\Spec\RequestSpec $spec
    */
   public function modifySpec(RequestSpec $spec) {
-    $contactTypeField = $spec->getFieldByName('contact_type');
-    if ($contactTypeField) {
-      $contactTypeField->setDefaultValue('Individual');
-    }
-
     $spec->getFieldByName('is_opt_out')->setRequired(FALSE);
     $spec->getFieldByName('is_deleted')->setRequired(FALSE);
-
   }
 
   /**
index b62494aa35706249570141dde248261803dfee04..9900a679268c0d22f3f53ce2682f22d448ee6d37 100644 (file)
@@ -33,7 +33,7 @@ class SearchRunWithCustomFieldTest extends \PHPUnit\Framework\TestCase implement
    */
   public function testRunWithImageField() {
     CustomGroup::create(FALSE)
-      ->addValue('name', 'TestSearchFields')
+      ->addValue('title', 'TestSearchFields')
       ->addValue('extends', 'Individual')
       ->execute()
       ->first();
index d218d37929f93fdcfd72d161cb91ab8cf614d6f7..0e800e484296cf04909d850041e21989af690de7 100644 (file)
@@ -1107,7 +1107,7 @@ class api_v3_ACLPermissionTest extends CiviUnitTestCase {
     $textField = 'text_field';
 
     CustomGroup::create(FALSE)
-      ->addValue('name', $group)
+      ->addValue('title', $group)
       ->addValue('extends', 'Contact')
       ->addValue('is_multiple', TRUE)
       ->addChain('field', CustomField::create()
index 4cc32bbcf010bbbce27909a2dcd2647f4066d7d1..d0a18ff04d308083df5ece7a137bc927774a4456 100644 (file)
@@ -36,7 +36,7 @@ class BasicCustomFieldTest extends BaseCustomValueTest {
    */
   public function testWithSingleField(): void {
     $customGroup = CustomGroup::create(FALSE)
-      ->addValue('name', 'MyIndividualFields')
+      ->addValue('title', 'MyIndividualFields')
       ->addValue('extends', 'Individual')
       ->execute()
       ->first();
@@ -103,7 +103,7 @@ class BasicCustomFieldTest extends BaseCustomValueTest {
 
     // First custom set
     CustomGroup::create(FALSE)
-      ->addValue('name', 'MyContactFields')
+      ->addValue('title', 'MyContactFields')
       ->addValue('extends', 'Contact')
       ->addChain('field1', CustomField::create()
         ->addValue('label', 'FavColor')
@@ -119,7 +119,7 @@ class BasicCustomFieldTest extends BaseCustomValueTest {
 
     // Second custom set
     CustomGroup::create(FALSE)
-      ->addValue('name', 'MyContactFields2')
+      ->addValue('title', 'MyContactFields2')
       ->addValue('extends', 'Contact')
       ->addChain('field1', CustomField::create()
         ->addValue('label', 'FavColor')
@@ -247,7 +247,7 @@ class BasicCustomFieldTest extends BaseCustomValueTest {
     $cgName = uniqid('RelFields');
 
     $customGroup = CustomGroup::create(FALSE)
-      ->addValue('name', $cgName)
+      ->addValue('title', $cgName)
       ->addValue('extends', 'Relationship')
       ->execute()
       ->first();
@@ -320,7 +320,7 @@ class BasicCustomFieldTest extends BaseCustomValueTest {
     $cgName = uniqid('My');
 
     CustomGroup::create(FALSE)
-      ->addValue('name', $cgName)
+      ->addValue('title', $cgName)
       ->addValue('extends', 'Contact')
       ->addChain('field1', CustomField::create()
         ->addValue('label', 'FavColor')
@@ -371,7 +371,7 @@ class BasicCustomFieldTest extends BaseCustomValueTest {
     $optionGroupCount = OptionGroup::get(FALSE)->selectRowCount()->execute()->count();
 
     $customGroup = CustomGroup::create(FALSE)
-      ->addValue('name', 'MyIndividualFields')
+      ->addValue('title', 'MyIndividualFields')
       ->addValue('extends', 'Individual')
       ->execute()
       ->first();
@@ -408,6 +408,7 @@ class BasicCustomFieldTest extends BaseCustomValueTest {
     // Create 2 custom groups. Control group is to ensure updating one doesn't affect the other
     foreach (['controlGroup', 'experimentalGroup'] as $groupName) {
       $customGroups[$groupName] = CustomGroup::create(FALSE)
+        ->addValue('title', $groupName)
         ->addValue('name', $groupName)
         ->addValue('extends', 'Individual')
         ->execute()->first();
@@ -455,6 +456,16 @@ class BasicCustomFieldTest extends BaseCustomValueTest {
     // Experimental group should be updated, control group should not
     $this->assertEquals(['One' => 1, 'Three' => 2, 'Two' => 3, 'Four' => 4], $getValues('experimentalGroup'));
     $this->assertEquals(['One' => 1, 'Two' => 2, 'Three' => 3, 'Four' => 4], $getValues('controlGroup'));
+
+    // Move first option to last position
+    CustomField::update(FALSE)
+      ->addWhere('custom_group_id.name', '=', 'experimentalGroup')
+      ->addWhere('name', '=', 'One')
+      ->addValue('weight', 4)
+      ->execute();
+    // Experimental group should be updated, control group should not
+    $this->assertEquals(['Three' => 1, 'Two' => 2, 'Four' => 3, 'One' => 4], $getValues('experimentalGroup'));
+    $this->assertEquals(['One' => 1, 'Two' => 2, 'Three' => 3, 'Four' => 4], $getValues('controlGroup'));
   }
 
 }
index 16274b2ca1c6511d261b1a69f34c28a70e2f43e3..47a1cbe86d07650cf9504d7d50b3a87489ccbd8c 100644 (file)
@@ -83,7 +83,7 @@ class ChainTest extends UnitTestCase {
   public function testWithContactRef() {
     CustomGroup::create()
       ->setCheckPermissions(FALSE)
-      ->addValue('name', 'TestActCus')
+      ->addValue('title', 'TestActCus')
       ->addValue('extends', 'Activity')
       ->addChain('field1', CustomField::create()
         ->addValue('label', 'FavPerson')
index 10278cc84abc47b91c04dfe5a9f5129bb9f08555..7fed9b787c6f610399e1833279d472750732962d 100644 (file)
@@ -33,7 +33,7 @@ class CreateCustomValueTest extends BaseCustomValueTest {
     $optionValues = ['r' => 'Red', 'g' => 'Green', 'b' => 'Blue'];
 
     $customGroup = CustomGroup::create(FALSE)
-      ->addValue('name', 'MyContactFields')
+      ->addValue('title', 'MyContactFields')
       ->addValue('extends', 'Contact')
       ->execute()
       ->first();
index dc778fe31d264edc048d62d9242ad3744901aa5f..2f6d8f78e087680c5c99906023b18c856118a921 100644 (file)
@@ -34,7 +34,7 @@ class CreateWithOptionGroupTest extends BaseCustomValueTest {
     $foodField = uniqid('fooda');
 
     $customGroupId = CustomGroup::create(FALSE)
-      ->addValue('name', $group)
+      ->addValue('title', $group)
       ->addValue('extends', 'Contact')
       ->execute()
       ->first()['id'];
@@ -58,7 +58,7 @@ class CreateWithOptionGroupTest extends BaseCustomValueTest {
       ->execute();
 
     $customGroupId = CustomGroup::create(FALSE)
-      ->addValue('name', 'FinancialStuff')
+      ->addValue('title', 'FinancialStuff')
       ->addValue('extends', 'Contact')
       ->execute()
       ->first()['id'];
@@ -100,7 +100,7 @@ class CreateWithOptionGroupTest extends BaseCustomValueTest {
     $foodField = uniqid('foodb');
 
     $customGroupId = CustomGroup::create(FALSE)
-      ->addValue('name', $group)
+      ->addValue('title', $group)
       ->addValue('extends', 'Contact')
       ->execute()
       ->first()['id'];
@@ -124,7 +124,7 @@ class CreateWithOptionGroupTest extends BaseCustomValueTest {
       ->execute();
 
     $customGroupId = CustomGroup::create(FALSE)
-      ->addValue('name', 'FinancialStuff')
+      ->addValue('title', 'FinancialStuff')
       ->addValue('extends', 'Contact')
       ->execute()
       ->first()['id'];
index 22db0897d85c18e0c8a9db3988ba567ae0c392c2..5ca38ff0c6bfc9d7f0641363adb491aedde62ad3 100644 (file)
@@ -32,7 +32,7 @@ class CustomContactRefTest extends BaseCustomValueTest {
     $firstName = uniqid('fav');
 
     $customGroup = CustomGroup::create(FALSE)
-      ->addValue('name', 'MyContactRef')
+      ->addValue('title', 'MyContactRef')
       ->addValue('extends', 'Individual')
       ->execute()
       ->first();
@@ -127,7 +127,7 @@ class CustomContactRefTest extends BaseCustomValueTest {
     $currentUser = $this->createLoggedInUser();
 
     $customGroup = CustomGroup::create(FALSE)
-      ->addValue('name', 'MyContactRef')
+      ->addValue('title', 'MyContactRef')
       ->addValue('extends', 'Individual')
       ->execute()
       ->first();
index ec75d2affad274cf35c20acdff050c98a33be1fb..6dec3a742bdce1ee1221abfdc6ff0fd779466f1f 100644 (file)
@@ -44,7 +44,7 @@ class CustomGroupACLTest extends BaseCustomValueTest {
 
     foreach ($groups as $name => $access) {
       $singleGroup = CustomGroup::create(FALSE)
-        ->addValue('name', 'My' . ucfirst($name) . 'Single')
+        ->addValue('title', 'My' . ucfirst($name) . 'Single')
         ->addValue('extends', 'Individual')
         ->addChain('field', CustomField::create()
           ->addValue('label', 'MyField')
@@ -53,7 +53,7 @@ class CustomGroupACLTest extends BaseCustomValueTest {
         ->execute()->single();
       $v3['single'][$name] = 'custom_' . $singleGroup['field']['id'];
       $multiGroup = CustomGroup::create(FALSE)
-        ->addValue('name', 'My' . ucfirst($name) . 'Multi')
+        ->addValue('title', 'My' . ucfirst($name) . 'Multi')
         ->addValue('extends', 'Individual')
         ->addValue('is_multiple', TRUE)
         ->addChain('field', CustomField::create()
index de6df7c9bcf049c71f8796a10306ffa0cfbb4d6e..68e510d06d15eed6790fb16d1155a3082bfea0db 100644 (file)
@@ -43,7 +43,7 @@ class CustomValueTest extends BaseCustomValueTest {
     $textFieldName = uniqid('txt');
 
     $customGroup = CustomGroup::create(FALSE)
-      ->addValue('name', $group)
+      ->addValue('title', $group)
       ->addValue('extends', 'Contact')
       ->addValue('is_multiple', TRUE)
       ->execute()
index df1c0c16ae02fad03b57f5c1da09acb2e1dd9c66..9b573b13b5170e808feb3ef05955c55a52fb029f 100644 (file)
@@ -31,7 +31,7 @@ class ExtendFromIndividualTest extends BaseCustomValueTest {
   public function testGetWithNonStandardExtends() {
 
     $customGroup = CustomGroup::create(FALSE)
-      ->addValue('name', 'MyContactFields')
+      ->addValue('title', 'MyContactFields')
       // not Contact
       ->addValue('extends', 'Individual')
       ->execute()
index d8f7621731e761f4fdbe2742dc76d77c497955f7..a6c7960534100775293a479ca1fadb9437c7e6a5 100644 (file)
@@ -156,7 +156,7 @@ class PseudoconstantTest extends BaseCustomValueTest {
     ];
 
     CustomGroup::create(FALSE)
-      ->addValue('name', 'myPseudoconstantTest')
+      ->addValue('title', 'myPseudoconstantTest')
       ->addValue('extends', 'Individual')
       ->addChain('field1', CustomField::create()
         ->addValue('custom_group_id', '$id')
index 1846cc5a748df90b81ff20d367fc9f1349e7ee2c..87821656ea9d0013bb2e35685ce5aec0302c43a6 100644 (file)
@@ -105,7 +105,7 @@ class ReplaceTest extends UnitTestCase {
 
   public function testCustomValueReplace() {
     $customGroup = CustomGroup::create(FALSE)
-      ->addValue('name', 'replaceTest')
+      ->addValue('title', 'replaceTest')
       ->addValue('extends', 'Contact')
       ->addValue('is_multiple', TRUE)
       ->execute()
index 2177dd4252f1f94c670282a003c29d464bcb7001..81fd5a7a7c24ccf8f005c940502905d8ff3fbccf 100644 (file)
@@ -32,7 +32,7 @@ class UpdateCustomValueTest extends BaseCustomValueTest {
   public function testGetWithCustomData() {
 
     $customGroup = CustomGroup::create(FALSE)
-      ->addValue('name', 'MyContactFields')
+      ->addValue('title', 'MyContactFields')
       ->addValue('extends', 'Contact')
       ->execute()
       ->first();
index 82ce19278cff7f7dc330b9f5af093bfe6c8d7435..e1a32c3bc2753422f2feb7b7dc40b3a9c267303a 100644 (file)
@@ -24,6 +24,7 @@
   "CustomGroup": [
     {
       "name": "MyFavoriteThings",
+      "title": "MyFavoriteThings",
       "extends": "Contact"
     }
   ],
index cdb4f492aec6ca5977ab25b1c56c0d828a826615..0abe38daba81c9839ec1896a62affac50888e787 100644 (file)
@@ -143,4 +143,18 @@ class ContactTypeTest extends UnitTestCase implements TransactionalInterface {
 
   }
 
+  public function testSaveContactWithImpliedType(): void {
+    // Ensure pseudoconstant suffix works
+    $result = Contact::create(FALSE)
+      ->addValue('contact_type:name', 'Household')
+      ->execute()->first();
+    $this->assertEquals('Household', $result['contact_type']);
+
+    // Contact type should be inferred by the type of name given
+    $result = Contact::save(FALSE)
+      ->addRecord(['organization_name' => 'Foo'])
+      ->execute()->first();
+    $this->assertEquals('Organization', $result['contact_type']);
+  }
+
 }
index 9233ecc02cc10454a7bbb3786486b4b94d5f9db0..5fa6a7e71ccb1edd1b3fce402309c002d4f55891 100644 (file)
@@ -79,19 +79,21 @@ class SpecGathererTest extends UnitTestCase {
 
   public function testPseudoConstantOptionsWillBeAdded() {
     $customGroupId = CustomGroup::create(FALSE)
-      ->addValue('name', 'FavoriteThings')
+      ->addValue('title', 'FavoriteThings')
       ->addValue('extends', 'Contact')
       ->execute()
       ->first()['id'];
 
     $options = ['r' => 'Red', 'g' => 'Green', 'p' => 'Pink'];
 
-    CustomField::create(FALSE)
-      ->addValue('label', 'FavColor')
-      ->addValue('custom_group_id', $customGroupId)
-      ->addValue('option_values', $options)
-      ->addValue('html_type', 'Select')
-      ->addValue('data_type', 'String')
+    CustomField::save(FALSE)
+      ->addRecord([
+        'label' => 'FavColor',
+        'custom_group_id' => $customGroupId,
+        'option_values' => $options,
+        'html_type' => 'Select',
+        'data_type' => 'String',
+      ])
       ->execute();
 
     $gatherer = new SpecGatherer();