Merge pull request #16475 from JMAConsulting/core_1577
[civicrm-core.git] / api / api.php
index 04b0421bcbb213757ab02e81d11882094e97e891..1a70bc3921cb83182598e84da4c5e23a4d0702b8 100644 (file)
  *
  * @return array|int
  */
-function civicrm_api($entity, $action, $params, $extra = NULL) {
+function civicrm_api(string $entity = NULL, string $action, array $params, $extra = NULL) {
   return \Civi::service('civi_api_kernel')->runSafe($entity, $action, $params, $extra);
 }
 
+/**
+ * CiviCRM API version 4.
+ *
+ * This API (Application Programming Interface) is used to access and manage data in CiviCRM.
+ *
+ * APIv4 is the latest stable version.
+ *
+ * @see https://docs.civicrm.org/dev/en/latest/api/v4/usage/
+ *
+ * @param string $entity Name of the CiviCRM entity to access.
+ *   All entity names are capitalized CamelCase, e.g. `ContributionPage`.
+ *   Most entities correspond to a database table (e.g. `Contact` is the table `civicrm_contact`).
+ *   For a complete list of available entities, call `civicrm_api4('Entity', 'get');`
+ *
+ * @param string $action The "verb" of the api call.
+ *   For a complete list of actions for a given entity (e.g. `Contact`), call `civicrm_api4('Contact', 'getActions');`
+ *
+ * @param array $params An array of API input keyed by parameter name.
+ *   The easiest way to discover all available parameters is to visit the API Explorer on your CiviCRM site.
+ *   The API Explorer is listed in the CiviCRM menu under Support -> Developer.
+ *
+ * @param string|int|array $index Controls the Result array format.
+ *   By default the api Result contains a non-associative array of data. Passing an $index tells the api to
+ *   automatically reformat the array, depending on the variable type passed:
+ *   - **Integer:** return a single result array;
+ *     e.g. `$index = 0` will return the first result, 1 will return the second, and -1 will return the last.
+ *   - **String:** index the results by a field value;
+ *     e.g. `$index = "name"` will return an associative array with the field 'name' as keys.
+ *   - **Non-associative array:** return a single value from each result;
+ *     e.g. `$index = ['title']` will return a non-associative array of strings - the 'title' field from each result.
+ *   - **Associative array:** a combination of the previous two modes;
+ *     e.g. `$index = ['name' => 'title']` will return an array of strings - the 'title' field keyed by the 'name' field.
+ *
+ * @return \Civi\Api4\Generic\Result
+ * @throws \API_Exception
+ * @throws \Civi\API\Exception\NotImplementedException
+ */
+function civicrm_api4(string $entity, string $action, array $params = [], $index = NULL) {
+  $apiCall = \Civi\Api4\Utils\ActionUtil::getAction($entity, $action);
+  $indexField = $index && is_string($index) && !CRM_Utils_Rule::integer($index) ? $index : NULL;
+  $removeIndexField = FALSE;
+
+  // If index field is not part of the select query, we add it here and remove it below
+  if ($indexField && !empty($params['select']) && is_array($params['select']) && !\Civi\Api4\Utils\SelectUtil::isFieldSelected($indexField, $params['select'])) {
+    $params['select'][] = $indexField;
+    $removeIndexField = TRUE;
+  }
+  foreach ($params as $name => $param) {
+    $setter = 'set' . ucfirst($name);
+    $apiCall->$setter($param);
+  }
+
+  if ($index && is_array($index)) {
+    $indexCol = reset($index);
+    $indexField = key($index);
+    if (property_exists($apiCall, 'select')) {
+      $apiCall->setSelect([$indexCol]);
+      if ($indexField && $indexField != $indexCol) {
+        $apiCall->addSelect($indexField);
+      }
+    }
+  }
+
+  $result = $apiCall->execute();
+
+  // Index results by key
+  if ($indexField) {
+    $result->indexBy($indexField);
+    if ($removeIndexField) {
+      foreach ($result as $key => $value) {
+        unset($result[$key][$indexField]);
+      }
+    }
+  }
+  // Return result at index
+  elseif (CRM_Utils_Rule::integer($index)) {
+    $item = $result->itemAt($index);
+    if (is_null($item)) {
+      throw new \API_Exception("Index $index not found in api results");
+    }
+    // Attempt to return a Result object if item is array, otherwise just return the item
+    if (!is_array($item)) {
+      return $item;
+    }
+    $result->exchangeArray($item);
+  }
+  if (!empty($indexCol)) {
+    $result->exchangeArray($result->column($indexCol));
+  }
+  return $result;
+}
+
 /**
  * Version 3 wrapper for civicrm_api.
  *
@@ -36,9 +128,10 @@ function civicrm_api($entity, $action, $params, $extra = NULL) {
  *   Array to be passed to function.
  *
  * @throws CiviCRM_API3_Exception
+ *
  * @return array
  */
-function civicrm_api3($entity, $action, $params = array()) {
+function civicrm_api3(string $entity, string $action, array $params = []) {
   $params['version'] = 3;
   $result = \Civi::service('civi_api_kernel')->runSafe($entity, $action, $params);
   if (is_array($result) && !empty($result['is_error'])) {
@@ -73,9 +166,9 @@ function _civicrm_api3_api_getfields(&$apiRequest) {
       //  $apiRequest['params']['action'] = $apiRequest['params']['api_action'];
       // unset($apiRequest['params']['api_action']);
     }
-    return array('action' => array('api.aliases' => array('api_action')));
+    return ['action' => ['api.aliases' => ['api_action']]];
   }
-  $getFieldsParams = array('action' => $apiRequest['action']);
+  $getFieldsParams = ['action' => $apiRequest['action']];
   $entity = $apiRequest['entity'];
   if ($entity == 'Profile' && array_key_exists('profile_id', $apiRequest['params'])) {
     $getFieldsParams['profile_id'] = $apiRequest['params']['profile_id'];