APIv4 - Deprecate passing 'id' to basic actions and standardize update actions
authorColeman Watts <coleman@civicrm.org>
Sun, 4 Jul 2021 20:55:36 +0000 (16:55 -0400)
committerColeman Watts <coleman@civicrm.org>
Mon, 5 Jul 2021 22:01:49 +0000 (18:01 -0400)
The primary_key is now known for every entity so it doesn't need to be passed
into the action classes, and both BasicUpdate and DAOUpdate can function off this key.

This accepts the old constructor arguments for backward-compatablity, for now.

15 files changed:
Civi/Api4/Action/CustomValue/Update.php
Civi/Api4/CustomValue.php
Civi/Api4/Generic/AbstractBatchAction.php
Civi/Api4/Generic/AbstractSaveAction.php
Civi/Api4/Generic/AbstractUpdateAction.php
Civi/Api4/Generic/BasicBatchAction.php
Civi/Api4/Generic/BasicEntity.php
Civi/Api4/Generic/BasicSaveAction.php
Civi/Api4/Generic/BasicUpdateAction.php
Civi/Api4/Generic/DAOUpdateAction.php
Civi/Api4/Generic/Traits/CustomValueActionTrait.php
ext/afform/core/Civi/Api4/Afform.php
tests/phpunit/api/v4/Action/BasicActionsTest.php
tests/phpunit/api/v4/Mock/Api4/Action/MockBasicEntity/BatchFrobnicate.php [new file with mode: 0644]
tests/phpunit/api/v4/Mock/Api4/MockBasicEntity.php

index 5198ddaf31ecc6a0d44941e8ff0f7fcbc4b7344c..205ab77e2a9bc08054aab37db64f6d16477e818a 100644 (file)
@@ -18,4 +18,12 @@ namespace Civi\Api4\Action\CustomValue;
 class Update extends \Civi\Api4\Generic\DAOUpdateAction {
   use \Civi\Api4\Generic\Traits\CustomValueActionTrait;
 
+  /**
+   * Ensure entity_id is returned by getBatchRecords()
+   * @return string[]
+   */
+  protected function getSelect() {
+    return ['id', 'entity_id'];
+  }
+
 }
index f08ffcb582ea742a45618bc460a1ef57c3d64982..7e7496099d2be0fe0346addad5a9c463101bd4ec 100644 (file)
@@ -99,7 +99,7 @@ class CustomValue {
    * @throws \API_Exception
    */
   public static function replace($customGroup, $checkPermissions = TRUE) {
-    return (new Generic\BasicReplaceAction("Custom_$customGroup", __FUNCTION__, ['id', 'entity_id']))
+    return (new Generic\BasicReplaceAction("Custom_$customGroup", __FUNCTION__))
       ->setCheckPermissions($checkPermissions);
   }
 
index b3b789f07db2b94c15ec47d6581afef404bc8341..19040576de168309afd4477f978bb5d3113bf7e5 100644 (file)
@@ -12,6 +12,8 @@
 
 namespace Civi\Api4\Generic;
 
+use Civi\Api4\Utils\CoreUtil;
+
 /**
  * Base class for all batch actions (Update, Delete, Replace).
  *
@@ -29,23 +31,6 @@ abstract class AbstractBatchAction extends AbstractQueryAction {
    */
   protected $where = [];
 
-  /**
-   * @var array
-   */
-  private $select;
-
-  /**
-   * BatchAction constructor.
-   * @param string $entityName
-   * @param string $actionName
-   * @param string|array $select
-   *   One or more fields to load for each item.
-   */
-  public function __construct($entityName, $actionName, $select = 'id') {
-    $this->select = (array) $select;
-    parent::__construct($entityName, $actionName);
-  }
-
   /**
    * Get a list of records for this batch.
    *
@@ -56,7 +41,7 @@ abstract class AbstractBatchAction extends AbstractQueryAction {
   }
 
   /**
-   * Get a query which resolves the list of records for this batch.
+   * Get an API action object which resolves the list of records for this batch.
    *
    * This is similar to `getBatchRecords()`, but you may further refine the
    * API call (e.g. selecting different fields or data-pages) before executing.
@@ -72,16 +57,20 @@ abstract class AbstractBatchAction extends AbstractQueryAction {
       'offset' => $this->offset,
     ];
     if (empty($this->reload)) {
-      $params['select'] = $this->select;
+      $params['select'] = $this->getSelect();
     }
     return \Civi\API\Request::create($this->getEntityName(), 'get', ['version' => 4] + $params);
   }
 
   /**
-   * @return array
+   * Determines what fields will be returned by getBatchRecords
+   *
+   * Defaults to an entity's primary key(s), typically ['id']
+   *
+   * @return string[]
    */
   protected function getSelect() {
-    return $this->select;
+    return CoreUtil::getInfoItem($this->getEntityName(), 'primary_key');
   }
 
 }
index 7f0efdf65ae3fcb381e7a68de46ac4a6ff5aed8b..c815645abbce17243185f4021b363814653a81fa 100644 (file)
@@ -69,32 +69,16 @@ abstract class AbstractSaveAction extends AbstractAction {
    */
   protected $reload = FALSE;
 
-  /**
-   * @var string
-   */
-  private $idField;
-
-  /**
-   * BatchAction constructor.
-   * @param string $entityName
-   * @param string $actionName
-   * @param string $idField
-   */
-  public function __construct($entityName, $actionName, $idField = 'id') {
-    // $idField should be a string but some apis (e.g. CustomValue) give us an array
-    $this->idField = array_values((array) $idField)[0];
-    parent::__construct($entityName, $actionName);
-  }
-
   /**
    * @throws \API_Exception
    * @throws \Civi\API\Exception\UnauthorizedException
    */
   protected function validateValues() {
+    $idField = $this->getIdField();
     // FIXME: There should be a protocol to report a full list of errors... Perhaps a subclass of API_Exception?
     $unmatched = [];
     foreach ($this->records as $record) {
-      if (empty($record[$this->idField])) {
+      if (empty($record[$idField])) {
         $unmatched = array_unique(array_merge($unmatched, $this->checkRequiredFields($record)));
       }
     }
@@ -104,23 +88,23 @@ abstract class AbstractSaveAction extends AbstractAction {
 
     if ($this->checkPermissions) {
       foreach ($this->records as $record) {
-        $action = empty($record[$this->idField]) ? 'create' : 'update';
+        $action = empty($record[$idField]) ? 'create' : 'update';
         if (!CoreUtil::checkAccessDelegated($this->getEntityName(), $action, $record, \CRM_Core_Session::getLoggedInContactID() ?: 0)) {
           throw new UnauthorizedException("ACL check failed");
         }
       }
     }
 
-    $e = new ValidateValuesEvent($this, $this->records, new \CRM_Utils_LazyArray(function() {
-      $existingIds = array_column($this->records, $this->idField);
+    $e = new ValidateValuesEvent($this, $this->records, new \CRM_Utils_LazyArray(function() use ($idField) {
+      $existingIds = array_column($this->records, $idField);
       $existing = civicrm_api4($this->getEntityName(), 'get', [
         'checkPermissions' => $this->checkPermissions,
-        'where' => [[$this->idField, 'IN', $existingIds]],
-      ], $this->idField);
+        'where' => [[$idField, 'IN', $existingIds]],
+      ], $idField);
 
       $result = [];
       foreach ($this->records as $k => $new) {
-        $old = isset($new[$this->idField]) ? $existing[$new[$this->idField]] : NULL;
+        $old = isset($new[$idField]) ? $existing[$new[$idField]] : NULL;
         $result[$k] = ['old' => $old, 'new' => $new];
       }
       return $result;
@@ -135,7 +119,7 @@ abstract class AbstractSaveAction extends AbstractAction {
    * @return string
    */
   protected function getIdField() {
-    return $this->idField;
+    return CoreUtil::getInfoItem($this->getEntityName(), 'primary_key')[0];
   }
 
   /**
index 66390d4e1f9285c3cf817f5b9b787c1b53ef77ef..ba8e3934a81d0ea369681d87b948bbc2b533acee 100644 (file)
@@ -12,7 +12,9 @@
 
 namespace Civi\Api4\Generic;
 
+use Civi\API\Exception\UnauthorizedException;
 use Civi\Api4\Event\ValidateValuesEvent;
+use Civi\Api4\Utils\CoreUtil;
 
 /**
  * Base class for all `Update` api actions
@@ -44,6 +46,67 @@ abstract class AbstractUpdateAction extends AbstractBatchAction {
    */
   protected $reload = FALSE;
 
+  /**
+   * Criteria for selecting items to update.
+   *
+   * Required if no id is supplied in values.
+   *
+   * @var array
+   */
+  protected $where = [];
+
+  abstract protected function updateRecords(array $items): array;
+
+  /**
+   * @inheritDoc
+   */
+  public function _run(Result $result) {
+    $primaryKeys = CoreUtil::getInfoItem($this->getEntityName(), 'primary_key');
+    $this->formatWriteValues($this->values);
+
+    // Add primary keys from values to WHERE clause and check for mismatch
+    foreach ($primaryKeys as $id) {
+      if (!empty($this->values[$id])) {
+        $wheres = array_column($this->where, NULL, 0);
+        if (!isset($wheres[$id])) {
+          $this->addWhere($id, '=', $this->values[$id]);
+        }
+        elseif (!($wheres[$id][1] === '=' && $wheres[$id][2] == $this->values[$id])) {
+          throw new \Exception("Cannot update the $id of an existing " . $this->getEntityName() . '.');
+        }
+      }
+    }
+
+    // Require WHERE if we didn't get primary keys from values
+    if (!$this->where) {
+      throw new \API_Exception('Parameter "where" is required unless primary keys are supplied in values.');
+    }
+
+    // Update a single record by primary key (if this entity has a single primary key)
+    if (count($this->where) === 1 && count($primaryKeys) === 1 && $primaryKeys === $this->getSelect() && $this->where[0][0] === $id && $this->where[0][1] === '=' && !empty($this->where[0][2])) {
+      $this->values[$id] = $this->where[0][2];
+      if ($this->checkPermissions && !CoreUtil::checkAccessRecord($this, $this->values, \CRM_Core_Session::getLoggedInContactID() ?: 0)) {
+        throw new UnauthorizedException("ACL check failed");
+      }
+      $items = [$this->values];
+      $this->validateValues();
+      $result->exchangeArray($this->updateRecords($items));
+      return;
+    }
+
+    // Batch update 1 or more records based on WHERE clause
+    $items = $this->getBatchRecords();
+    foreach ($items as &$item) {
+      $item = $this->values + $item;
+      if ($this->checkPermissions && !CoreUtil::checkAccessRecord($this, $item, \CRM_Core_Session::getLoggedInContactID() ?: 0)) {
+        throw new UnauthorizedException("ACL check failed");
+      }
+    }
+
+    $this->validateValues();
+    $result->exchangeArray($this->updateRecords($items));
+  }
+
   /**
    * @param string $fieldName
    *
index afe8458014911f6ff971e75769d51a965995f5ef..bc3d6a51e6a3f91c5be4f3d8461fad4daf84dc8d 100644 (file)
@@ -43,13 +43,19 @@ class BasicBatchAction extends AbstractBatchAction {
    *
    * @param string $entityName
    * @param string $actionName
-   * @param string|array $select
-   *   One or more fields to select from each matching item.
    * @param callable $doer
    */
-  public function __construct($entityName, $actionName, $select = 'id', $doer = NULL) {
-    parent::__construct($entityName, $actionName, $select);
+  public function __construct($entityName, $actionName, $doer = NULL) {
+    parent::__construct($entityName, $actionName);
     $this->doer = $doer;
+    // Accept doer as 4th param for now, but emit deprecated warning
+    $this->doer = func_get_args()[3] ?? NULL;
+    if ($this->doer) {
+      \CRM_Core_Error::deprecatedWarning(__CLASS__ . ' constructor received $doer as 4th param; it should be the 3rd as the $select param has been removed');
+    }
+    else {
+      $this->doer = $doer;
+    }
   }
 
   /**
index dbe4ad1a534665c004d1dcdc6ce720cc20c95cc4..48c649b66150fc0589b4aba1e18eee685a17bb39 100644 (file)
@@ -105,7 +105,7 @@ abstract class BasicEntity extends AbstractEntity {
    * @return BasicSaveAction
    */
   public static function save($checkPermissions = TRUE) {
-    return (new BasicSaveAction(static::getEntityName(), __FUNCTION__, static::$idField, static::$setter))
+    return (new BasicSaveAction(static::getEntityName(), __FUNCTION__, static::$setter))
       ->setCheckPermissions($checkPermissions);
   }
 
@@ -114,7 +114,7 @@ abstract class BasicEntity extends AbstractEntity {
    * @return BasicUpdateAction
    */
   public static function update($checkPermissions = TRUE) {
-    return (new BasicUpdateAction(static::getEntityName(), __FUNCTION__, static::$idField, static::$setter))
+    return (new BasicUpdateAction(static::getEntityName(), __FUNCTION__, static::$setter))
       ->setCheckPermissions($checkPermissions);
   }
 
@@ -123,7 +123,7 @@ abstract class BasicEntity extends AbstractEntity {
    * @return BasicBatchAction
    */
   public static function delete($checkPermissions = TRUE) {
-    return (new BasicBatchAction(static::getEntityName(), __FUNCTION__, static::$idField, static::$deleter))
+    return (new BasicBatchAction(static::getEntityName(), __FUNCTION__, static::$deleter))
       ->setCheckPermissions($checkPermissions);
   }
 
@@ -132,7 +132,7 @@ abstract class BasicEntity extends AbstractEntity {
    * @return BasicReplaceAction
    */
   public static function replace($checkPermissions = TRUE) {
-    return (new BasicReplaceAction(static::getEntityName(), __FUNCTION__, static::$idField))
+    return (new BasicReplaceAction(static::getEntityName(), __FUNCTION__))
       ->setCheckPermissions($checkPermissions);
   }
 
index 3727fe4be3379d243e9a2a90428ffbaed8dda9d0..f0e175e5faf7d39644685230c17a7a3da190af75 100644 (file)
@@ -30,12 +30,18 @@ class BasicSaveAction extends AbstractSaveAction {
    *
    * @param string $entityName
    * @param string $actionName
-   * @param string $idField
    * @param callable $setter
    */
-  public function __construct($entityName, $actionName, $idField = 'id', $setter = NULL) {
-    parent::__construct($entityName, $actionName, $idField);
-    $this->setter = $setter;
+  public function __construct($entityName, $actionName, $setter = NULL) {
+    parent::__construct($entityName, $actionName);
+    // Accept setter as 4th param for now, but emit deprecated warning
+    $this->setter = func_get_args()[3] ?? NULL;
+    if ($this->setter) {
+      \CRM_Core_Error::deprecatedWarning(__CLASS__ . ' constructor received $setter as 4th param; it should be the 3rd as the $select param has been removed');
+    }
+    else {
+      $this->setter = $setter;
+    }
   }
 
   /**
index 9b4dc038c9f85309ed9b385609f962ade02281f4..5fefc107f47e804437d80beb2766d1baaa5a747e 100644 (file)
@@ -13,8 +13,6 @@
 namespace Civi\Api4\Generic;
 
 use Civi\API\Exception\NotImplementedException;
-use Civi\API\Exception\UnauthorizedException;
-use Civi\Api4\Utils\CoreUtil;
 
 /**
  * Update one or more $ENTITY with new values.
@@ -34,33 +32,27 @@ class BasicUpdateAction extends AbstractUpdateAction {
    *
    * @param string $entityName
    * @param string $actionName
-   * @param string|array $select
-   *   One or more fields to select from each matching item.
    * @param callable $setter
    */
-  public function __construct($entityName, $actionName, $select = 'id', $setter = NULL) {
-    parent::__construct($entityName, $actionName, $select);
-    $this->setter = $setter;
+  public function __construct($entityName, $actionName, $setter = NULL) {
+    parent::__construct($entityName, $actionName);
+    // Accept setter as 4th param for now, but emit deprecated warning
+    $this->setter = func_get_args()[3] ?? NULL;
+    if ($this->setter) {
+      \CRM_Core_Error::deprecatedWarning(__CLASS__ . ' constructor received $setter as 4th param; it should be the 3rd as the $select param has been removed');
+    }
+    else {
+      $this->setter = $setter;
+    }
   }
 
   /**
-   * We pass the writeRecord function an array representing one item to update.
-   * We expect to get the same format back.
-   *
-   * @param \Civi\Api4\Generic\Result $result
+   * @param array $items
+   * @return array
    * @throws \API_Exception
-   * @throws \Civi\API\Exception\NotImplementedException
    */
-  public function _run(Result $result) {
-    $this->formatWriteValues($this->values);
-    $this->validateValues();
-    foreach ($this->getBatchRecords() as $item) {
-      $record = $this->values + $item;
-      if ($this->checkPermissions && !CoreUtil::checkAccessRecord($this, $record, \CRM_Core_Session::getLoggedInContactID() ?: 0)) {
-        throw new UnauthorizedException("ACL check failed");
-      }
-      $result[] = $this->writeRecord($record);
-    }
+  protected function updateRecords(array $items): array {
+    return array_map([$this, 'writeRecord'], $items);
   }
 
   /**
index 9b3b6401d3d8bee78ae5acfcb7c26c4f392bdb60..25593822a6b70be6a9e645bacf3e481bbf4f7608 100644 (file)
 
 namespace Civi\Api4\Generic;
 
-use Civi\API\Exception\UnauthorizedException;
-use Civi\Api4\Utils\CoreUtil;
-
 /**
  * Update one or more $ENTITY with new values.
  *
- * Use the `where` clause (required) to select them.
+ * Use the `where` clause to bulk update multiple records,
+ * or supply 'id' as a value to update a single record.
  */
 class DAOUpdateAction extends AbstractUpdateAction {
   use Traits\DAOActionTrait;
 
   /**
-   * Criteria for selecting items to update.
-   *
-   * Required if no id is supplied in values.
-   *
-   * @var array
-   */
-  protected $where = [];
-
-  /**
-   * @inheritDoc
+   * @param array $items
+   * @return array
+   * @throws \API_Exception
+   * @throws \CRM_Core_Exception
    */
-  public function _run(Result $result) {
-    $this->formatWriteValues($this->values);
-    // Add ID from values to WHERE clause and check for mismatch
-    if (!empty($this->values['id'])) {
-      $wheres = array_column($this->where, NULL, 0);
-      if (!isset($wheres['id'])) {
-        $this->addWhere('id', '=', $this->values['id']);
-      }
-      elseif (!($wheres['id'][1] === '=' && $wheres['id'][2] == $this->values['id'])) {
-        throw new \Exception("Cannot update the id of an existing " . $this->getEntityName() . '.');
-      }
-    }
-
-    // Require WHERE if we didn't get an ID from values
-    if (!$this->where) {
-      throw new \API_Exception('Parameter "where" is required unless an id is supplied in values.');
-    }
-
-    // Update a single record by ID unless select requires more than id
-    if ($this->getSelect() === ['id'] && count($this->where) === 1 && $this->where[0][0] === 'id' && $this->where[0][1] === '=' && !empty($this->where[0][2])) {
-      $this->values['id'] = $this->where[0][2];
-      if ($this->checkPermissions && !CoreUtil::checkAccessRecord($this, $this->values, \CRM_Core_Session::getLoggedInContactID() ?: 0)) {
-        throw new UnauthorizedException("ACL check failed");
-      }
-      $items = [$this->values];
-      $this->validateValues();
-      $result->exchangeArray($this->writeObjects($items));
-      return;
-    }
-
-    // Batch update 1 or more records based on WHERE clause
-    $items = $this->getBatchRecords();
-    foreach ($items as &$item) {
-      $item = $this->values + $item;
-      if ($this->checkPermissions && !CoreUtil::checkAccessRecord($this, $item, \CRM_Core_Session::getLoggedInContactID() ?: 0)) {
-        throw new UnauthorizedException("ACL check failed");
-      }
-    }
-
-    $this->validateValues();
-    $result->exchangeArray($this->writeObjects($items));
+  protected function updateRecords(array $items): array {
+    return $this->writeObjects($items);
   }
 
 }
index 44d3caa3bfadfa3554bcd9f0980fbab52941915c..066167fb2c1f859b85d9b41769ba1ce66743d0dc 100644 (file)
@@ -24,7 +24,7 @@ trait CustomValueActionTrait {
 
   public function __construct($customGroup, $actionName) {
     $this->customGroup = $customGroup;
-    parent::__construct('CustomValue', $actionName, ['id', 'entity_id']);
+    parent::__construct('CustomValue', $actionName);
   }
 
   /**
index 1c8e3275e013f8d1b20fcec34b1e7777c40dbb7a..5aa33eb1f50192dddba79fced3ae3260355e234e 100644 (file)
@@ -44,7 +44,7 @@ class Afform extends Generic\AbstractEntity {
    * @return Action\Afform\Update
    */
   public static function update($checkPermissions = TRUE) {
-    return (new Action\Afform\Update('Afform', __FUNCTION__, 'name'))
+    return (new Action\Afform\Update('Afform', __FUNCTION__))
       ->setCheckPermissions($checkPermissions);
   }
 
@@ -53,7 +53,7 @@ class Afform extends Generic\AbstractEntity {
    * @return Action\Afform\Save
    */
   public static function save($checkPermissions = TRUE) {
-    return (new Action\Afform\Save('Afform', __FUNCTION__, 'name'))
+    return (new Action\Afform\Save('Afform', __FUNCTION__))
       ->setCheckPermissions($checkPermissions);
   }
 
@@ -89,7 +89,7 @@ class Afform extends Generic\AbstractEntity {
    * @return Generic\BasicBatchAction
    */
   public static function revert($checkPermissions = TRUE) {
-    return (new BasicBatchAction('Afform', __FUNCTION__, ['name'], function($item, BasicBatchAction $action) {
+    return (new BasicBatchAction('Afform', __FUNCTION__, function($item, BasicBatchAction $action) {
       $scanner = \Civi::service('afform_scanner');
       $files = [
         \CRM_Afform_AfformScanner::METADATA_FILE,
index 78215925cb55a48c06ccb2d935e2e5085655bc6b..32d223ea1dc156153d4cd25d69b616e5a8a7d469 100644 (file)
@@ -48,18 +48,26 @@ class BasicActionsTest extends UnitTestCase {
     $result = MockBasicEntity::get()->execute();
     $this->assertCount(1, $result);
 
-    $id2 = MockBasicEntity::create()->addValue('foo', 'two')->execute()->first()['identifier'];
+    $id2 = MockBasicEntity::create()
+      ->addValue('foo', 'two')
+      ->addValue('group:label', 'First')
+      ->execute()->first()['identifier'];
 
     $result = MockBasicEntity::get()->selectRowCount()->execute();
     $this->assertEquals(2, $result->count());
 
+    // Updating a single record should support identifier either in the values or the where clause
+    // Test both styles of update
     MockBasicEntity::update()->addWhere('identifier', '=', $id2)->addValue('foo', 'new')->execute();
+    MockBasicEntity::update()->addValue('identifier', $id2)->addValue('color', 'red')->execute();
 
     $result = MockBasicEntity::get()->addOrderBy('identifier', 'DESC')->setLimit(1)->execute();
     // The object's count() method will account for all results, ignoring limit, while the array results are limited
     $this->assertCount(2, $result);
     $this->assertCount(1, (array) $result);
     $this->assertEquals('new', $result->first()['foo']);
+    $this->assertEquals('red', $result->first()['color']);
+    $this->assertEquals('one', $result->first()['group']);
 
     $result = MockBasicEntity::save()
       ->addRecord(['identifier' => $id1, 'foo' => 'one updated', 'weight' => '5'])
@@ -252,7 +260,7 @@ class BasicActionsTest extends UnitTestCase {
     }
 
     $result = MockBasicEntity::get()
-      ->addSelect('*e', 'weig*ht')
+      ->addSelect('s*e', 'weig*ht')
       ->execute()
       ->first();
     $this->assertEquals(['shape', 'size', 'weight'], array_keys($result));
diff --git a/tests/phpunit/api/v4/Mock/Api4/Action/MockBasicEntity/BatchFrobnicate.php b/tests/phpunit/api/v4/Mock/Api4/Action/MockBasicEntity/BatchFrobnicate.php
new file mode 100644 (file)
index 0000000..01a3185
--- /dev/null
@@ -0,0 +1,38 @@
+<?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       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+
+namespace Civi\Api4\Action\MockBasicEntity;
+
+/**
+ * This class demonstrates how the getRecords method of Basic\Get can be overridden.
+ */
+class BatchFrobnicate extends \Civi\Api4\Generic\BasicBatchAction {
+
+  protected function doTask($item) {
+    return [
+      'identifier' => $item['identifier'],
+      'frobnication' => $item['number'] * $item['number'],
+    ];
+  }
+
+  protected function getSelect() {
+    return ['identifier', 'number'];
+  }
+
+}
index 88adad6f49c295b3e76a7567dc6e0b8be0989096..b991947cd73993041f6beaec25e2fe7bfa4c5229 100644 (file)
@@ -96,12 +96,8 @@ class MockBasicEntity extends Generic\BasicEntity {
    * @return Generic\BasicBatchAction
    */
   public static function batchFrobnicate($checkPermissions = TRUE) {
-    return (new Generic\BasicBatchAction(__CLASS__, __FUNCTION__, ['identifier', 'number'], function($item) {
-      return [
-        'identifier' => $item['identifier'],
-        'frobnication' => $item['number'] * $item['number'],
-      ];
-    }))->setCheckPermissions($checkPermissions);
+    return (new Action\MockBasicEntity\BatchFrobnicate(__CLASS__, __FUNCTION__))
+      ->setCheckPermissions($checkPermissions);
   }
 
 }