From 236f858eb4689d71b748252cc94e47a3850dc472 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Tue, 11 May 2021 12:22:48 -0400 Subject: [PATCH] APIv4 - Default to saving and deleting DAO records in bulk This adds new writeRecords() and deleteRecords() methods to CRM_Core_DAO which can be overridden by DAOs who wish to optimize. --- CRM/Core/DAO.php | 34 +++++++++++++++++++-- Civi/Api4/Generic/DAODeleteAction.php | 5 ++- Civi/Api4/Generic/Traits/DAOActionTrait.php | 23 +++++++++++--- 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/CRM/Core/DAO.php b/CRM/Core/DAO.php index 506a971ba7..0e901cdc42 100644 --- a/CRM/Core/DAO.php +++ b/CRM/Core/DAO.php @@ -888,7 +888,7 @@ class CRM_Core_DAO extends DB_DataObject { * * @param array $record * - * @return $this + * @return static * @throws \CRM_Core_Exception */ public static function writeRecord(array $record): CRM_Core_DAO { @@ -908,12 +908,27 @@ class CRM_Core_DAO extends DB_DataObject { return $instance; } + /** + * Bulk save multiple records + * + * @param array[] $records + * @return static[] + * @throws CRM_Core_Exception + */ + public static function writeRecords(array $records) { + $results = []; + foreach ($records as $record) { + $results[] = static::writeRecord($record); + } + return $results; + } + /** * Delete a record from supplied params. * * @param array $record * 'id' is required. - * @return CRM_Core_DAO + * @return static * @throws CRM_Core_Exception */ public static function deleteRecord(array $record) { @@ -937,6 +952,21 @@ class CRM_Core_DAO extends DB_DataObject { return $instance; } + /** + * Bulk delete multiple records. + * + * @param array[] $records + * @return static[] + * @throws CRM_Core_Exception + */ + public static function deleteRecords(array $records) { + $results = []; + foreach ($records as $record) { + $results[] = static::deleteRecord($record); + } + return $results; + } + /** * Check if there is a record with the same name in the db. * diff --git a/Civi/Api4/Generic/DAODeleteAction.php b/Civi/Api4/Generic/DAODeleteAction.php index e9f4c36d90..ecd9c91c13 100644 --- a/Civi/Api4/Generic/DAODeleteAction.php +++ b/Civi/Api4/Generic/DAODeleteAction.php @@ -71,9 +71,8 @@ class DAODeleteAction extends AbstractBatchAction { } } else { - foreach ($items as $item) { - $baoName::deleteRecord($item); - $ids[] = ['id' => $item['id']]; + foreach ($baoName::deleteRecords($items) as $instance) { + $ids[] = ['id' => $instance->id]; } } return $ids; diff --git a/Civi/Api4/Generic/Traits/DAOActionTrait.php b/Civi/Api4/Generic/Traits/DAOActionTrait.php index 197e8936ac..c06475ce8e 100644 --- a/Civi/Api4/Generic/Traits/DAOActionTrait.php +++ b/Civi/Api4/Generic/Traits/DAOActionTrait.php @@ -108,12 +108,13 @@ trait DAOActionTrait { // Some BAOs are weird and don't support a straightforward "create" method. $oddballs = [ + 'Address' => 'add', 'EntityTag' => 'add', 'GroupContact' => 'add', ]; $method = $oddballs[$this->getEntityName()] ?? 'create'; if (!method_exists($baoName, $method)) { - $method = 'add'; + $method = method_exists($baoName, 'add') ? 'add' : FALSE; } $result = []; @@ -122,6 +123,11 @@ trait DAOActionTrait { $entityId = $item['id'] ?? NULL; FormattingUtil::formatWriteParams($item, $this->entityFields()); $this->formatCustomParams($item, $entityId); + + // Skip to writeRecords if not using legacy method + if (!$method) { + continue; + } $item['check_permissions'] = $this->getCheckPermissions(); // For some reason the contact bao requires this @@ -136,11 +142,8 @@ trait DAOActionTrait { if ($this->getEntityName() === 'Address') { $createResult = $baoName::$method($item, $this->fixAddress); } - elseif (method_exists($baoName, $method)) { - $createResult = $baoName::$method($item); - } else { - $createResult = $baoName::writeRecord($item); + $createResult = $baoName::$method($item); } if (!$createResult) { @@ -150,6 +153,16 @@ trait DAOActionTrait { $result[] = $this->baoToArray($createResult, $item); } + + // Use bulk `writeRecords` method if the BAO doesn't have a create or add method + // TODO: reverse this from opt-in to opt-out and default to using `writeRecords` for all BAOs + if (!$method) { + $items = array_values($items); + foreach ($baoName::writeRecords($items) as $i => $createResult) { + $result[] = $this->baoToArray($createResult, $items[$i]); + } + } + FormattingUtil::formatOutputValues($result, $this->entityFields(), $this->getEntityName()); return $result; } -- 2.25.1