APIv4 - Support pseudoconstant lookups
[civicrm-core.git] / Civi / Api4 / Generic / AbstractAction.php
index 5138c1c87ad2d07f85c9eff2de5307e9fefba72b..905c9a1911d69acd5199726267ae8e221c82f0d1 100644 (file)
@@ -20,8 +20,8 @@
 
 namespace Civi\Api4\Generic;
 
+use Civi\Api4\Utils\FormattingUtil;
 use Civi\Api4\Utils\ReflectionUtils;
-use Civi\Api4\Utils\ActionUtil;
 
 /**
  * Base class for all api actions.
@@ -271,13 +271,13 @@ abstract class AbstractAction implements \ArrayAccess {
     if (!isset($this->_paramInfo)) {
       $defaults = $this->getParamDefaults();
       $vars = [
-        '$ENTITY' => $this->getEntityName(),
-        '$ACTION' => $this->getActionName(),
+        'entity' => $this->getEntityName(),
+        'action' => $this->getActionName(),
       ];
       // For actions like "getFields" and "getActions" they are not getting the entity itself.
       // So generic docs will make more sense like this:
-      if (substr($vars['$ACTION'], 0, 3) === 'get' && substr($vars['$ACTION'], -1) === 's') {
-        $vars['$ENTITY'] = lcfirst(substr($vars['$ACTION'], 3, -1));
+      if (substr($vars['action'], 0, 3) === 'get' && substr($vars['action'], -1) === 's') {
+        $vars['entity'] = lcfirst(substr($vars['action'], 3, -1));
       }
       foreach ($this->reflect()->getProperties(\ReflectionProperty::IS_PROTECTED) as $property) {
         $name = $property->getName();
@@ -424,15 +424,14 @@ abstract class AbstractAction implements \ArrayAccess {
    */
   public function entityFields() {
     if (!$this->_entityFields) {
-      $getFields = ActionUtil::getAction($this->getEntityName(), 'getFields');
+      $getFields = \Civi\API\Request::create($this->getEntityName(), 'getFields', [
+        'version' => 4,
+        'checkPermissions' => $this->checkPermissions,
+        'action' => $this->getActionName(),
+        'includeCustom' => FALSE,
+      ]);
       $result = new Result();
-      if (method_exists($this, 'getBaoName')) {
-        $getFields->setIncludeCustom(FALSE);
-      }
-      $getFields
-        ->setCheckPermissions($this->checkPermissions)
-        ->setAction($this->getActionName())
-        ->_run($result);
+      $getFields->_run($result);
       $this->_entityFields = (array) $result->indexBy('name');
     }
     return $this->_entityFields;
@@ -472,6 +471,42 @@ abstract class AbstractAction implements \ArrayAccess {
     return $unmatched;
   }
 
+  /**
+   * Replaces pseudoconstants in input values
+   *
+   * @param array $record
+   * @throws \API_Exception
+   */
+  protected function formatWriteValues(&$record) {
+    $optionFields = [];
+    // Collect fieldnames with a :pseudoconstant suffix & remove them from $record array
+    foreach (array_keys($record) as $expr) {
+      $suffix = strrpos($expr, ':');
+      if ($suffix) {
+        $fieldName = substr($expr, 0, $suffix);
+        $field = $this->entityFields()[$fieldName] ?? NULL;
+        if ($field) {
+          $optionFields[$fieldName] = [
+            'val' => $record[$expr],
+            'name' => empty($field['custom_field_id']) ? $field['name'] : 'custom_' . $field['custom_field_id'],
+            'suffix' => substr($expr, $suffix + 1),
+            'depends' => $field['input_attrs']['controlField'] ?? NULL,
+          ];
+          unset($record[$expr]);
+        }
+      }
+    }
+    // Sort option lookups by dependency, so e.g. country_id is processed first, then state_province_id, then county_id
+    uasort($optionFields, function ($a, $b) {
+      return $a['name'] === $b['depends'] ? -1 : 1;
+    });
+    // Replace pseudoconstants. Note this is a reverse lookup as we are evaluating input not output.
+    foreach ($optionFields as $fieldName => $info) {
+      $options = FormattingUtil::getPseudoconstantList($this->_entityName, $info['name'], $info['suffix'], $record, 'create');
+      $record[$fieldName] = FormattingUtil::replacePseudoconstant($options, $info['val'], TRUE);
+    }
+  }
+
   /**
    * This function is used internally for evaluating field annotations.
    *