From 29ab318b684a79f4cdedde2bf0417775d4be5523 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Sun, 4 Jul 2021 16:55:36 -0400 Subject: [PATCH] APIv4 - Deprecate passing 'id' to basic actions and standardize update actions 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. --- Civi/Api4/Action/CustomValue/Update.php | 8 +++ Civi/Api4/CustomValue.php | 2 +- Civi/Api4/Generic/AbstractBatchAction.php | 31 +++------ Civi/Api4/Generic/AbstractSaveAction.php | 34 +++------- Civi/Api4/Generic/AbstractUpdateAction.php | 63 +++++++++++++++++++ Civi/Api4/Generic/BasicBatchAction.php | 14 +++-- Civi/Api4/Generic/BasicEntity.php | 8 +-- Civi/Api4/Generic/BasicSaveAction.php | 14 +++-- Civi/Api4/Generic/BasicUpdateAction.php | 36 +++++------ Civi/Api4/Generic/DAOUpdateAction.php | 63 +++---------------- .../Generic/Traits/CustomValueActionTrait.php | 2 +- ext/afform/core/Civi/Api4/Afform.php | 6 +- .../api/v4/Action/BasicActionsTest.php | 12 +++- .../MockBasicEntity/BatchFrobnicate.php | 38 +++++++++++ .../api/v4/Mock/Api4/MockBasicEntity.php | 8 +-- 15 files changed, 191 insertions(+), 148 deletions(-) create mode 100644 tests/phpunit/api/v4/Mock/Api4/Action/MockBasicEntity/BatchFrobnicate.php diff --git a/Civi/Api4/Action/CustomValue/Update.php b/Civi/Api4/Action/CustomValue/Update.php index 5198ddaf31..205ab77e2a 100644 --- a/Civi/Api4/Action/CustomValue/Update.php +++ b/Civi/Api4/Action/CustomValue/Update.php @@ -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']; + } + } diff --git a/Civi/Api4/CustomValue.php b/Civi/Api4/CustomValue.php index f08ffcb582..7e7496099d 100644 --- a/Civi/Api4/CustomValue.php +++ b/Civi/Api4/CustomValue.php @@ -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); } diff --git a/Civi/Api4/Generic/AbstractBatchAction.php b/Civi/Api4/Generic/AbstractBatchAction.php index b3b789f07d..19040576de 100644 --- a/Civi/Api4/Generic/AbstractBatchAction.php +++ b/Civi/Api4/Generic/AbstractBatchAction.php @@ -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'); } } diff --git a/Civi/Api4/Generic/AbstractSaveAction.php b/Civi/Api4/Generic/AbstractSaveAction.php index 7f0efdf65a..c815645abb 100644 --- a/Civi/Api4/Generic/AbstractSaveAction.php +++ b/Civi/Api4/Generic/AbstractSaveAction.php @@ -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]; } /** diff --git a/Civi/Api4/Generic/AbstractUpdateAction.php b/Civi/Api4/Generic/AbstractUpdateAction.php index 66390d4e1f..ba8e3934a8 100644 --- a/Civi/Api4/Generic/AbstractUpdateAction.php +++ b/Civi/Api4/Generic/AbstractUpdateAction.php @@ -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 * diff --git a/Civi/Api4/Generic/BasicBatchAction.php b/Civi/Api4/Generic/BasicBatchAction.php index afe8458014..bc3d6a51e6 100644 --- a/Civi/Api4/Generic/BasicBatchAction.php +++ b/Civi/Api4/Generic/BasicBatchAction.php @@ -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; + } } /** diff --git a/Civi/Api4/Generic/BasicEntity.php b/Civi/Api4/Generic/BasicEntity.php index dbe4ad1a53..48c649b661 100644 --- a/Civi/Api4/Generic/BasicEntity.php +++ b/Civi/Api4/Generic/BasicEntity.php @@ -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); } diff --git a/Civi/Api4/Generic/BasicSaveAction.php b/Civi/Api4/Generic/BasicSaveAction.php index 3727fe4be3..f0e175e5fa 100644 --- a/Civi/Api4/Generic/BasicSaveAction.php +++ b/Civi/Api4/Generic/BasicSaveAction.php @@ -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; + } } /** diff --git a/Civi/Api4/Generic/BasicUpdateAction.php b/Civi/Api4/Generic/BasicUpdateAction.php index 9b4dc038c9..5fefc107f4 100644 --- a/Civi/Api4/Generic/BasicUpdateAction.php +++ b/Civi/Api4/Generic/BasicUpdateAction.php @@ -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); } /** diff --git a/Civi/Api4/Generic/DAOUpdateAction.php b/Civi/Api4/Generic/DAOUpdateAction.php index 9b3b6401d3..25593822a6 100644 --- a/Civi/Api4/Generic/DAOUpdateAction.php +++ b/Civi/Api4/Generic/DAOUpdateAction.php @@ -12,70 +12,23 @@ 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); } } diff --git a/Civi/Api4/Generic/Traits/CustomValueActionTrait.php b/Civi/Api4/Generic/Traits/CustomValueActionTrait.php index 44d3caa3bf..066167fb2c 100644 --- a/Civi/Api4/Generic/Traits/CustomValueActionTrait.php +++ b/Civi/Api4/Generic/Traits/CustomValueActionTrait.php @@ -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); } /** diff --git a/ext/afform/core/Civi/Api4/Afform.php b/ext/afform/core/Civi/Api4/Afform.php index 1c8e3275e0..5aa33eb1f5 100644 --- a/ext/afform/core/Civi/Api4/Afform.php +++ b/ext/afform/core/Civi/Api4/Afform.php @@ -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, diff --git a/tests/phpunit/api/v4/Action/BasicActionsTest.php b/tests/phpunit/api/v4/Action/BasicActionsTest.php index 78215925cb..32d223ea1d 100644 --- a/tests/phpunit/api/v4/Action/BasicActionsTest.php +++ b/tests/phpunit/api/v4/Action/BasicActionsTest.php @@ -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 index 0000000000..01a3185b29 --- /dev/null +++ b/tests/phpunit/api/v4/Mock/Api4/Action/MockBasicEntity/BatchFrobnicate.php @@ -0,0 +1,38 @@ + $item['identifier'], + 'frobnication' => $item['number'] * $item['number'], + ]; + } + + protected function getSelect() { + return ['identifier', 'number']; + } + +} diff --git a/tests/phpunit/api/v4/Mock/Api4/MockBasicEntity.php b/tests/phpunit/api/v4/Mock/Api4/MockBasicEntity.php index 88adad6f49..b991947cd7 100644 --- a/tests/phpunit/api/v4/Mock/Api4/MockBasicEntity.php +++ b/tests/phpunit/api/v4/Mock/Api4/MockBasicEntity.php @@ -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); } } -- 2.25.1