From 1a5485fe5d9edcde5410c0ac4db3e3470c020bb2 Mon Sep 17 00:00:00 2001 From: Kurund Jalmi Date: Mon, 14 Aug 2023 17:34:05 +0100 Subject: [PATCH] add new process api to fetch the data from submissions and save to various tables in Civi --- .../Civi/Afform/Event/AfformSubmitEvent.php | 6 +- .../Api4/Action/Afform/AbstractProcessor.php | 114 ++++++++++++++++++ .../core/Civi/Api4/Action/Afform/Process.php | 53 ++++++++ .../core/Civi/Api4/Action/Afform/Submit.php | 105 +--------------- ext/afform/core/Civi/Api4/Afform.php | 9 ++ 5 files changed, 183 insertions(+), 104 deletions(-) create mode 100644 ext/afform/core/Civi/Api4/Action/Afform/Process.php diff --git a/ext/afform/core/Civi/Afform/Event/AfformSubmitEvent.php b/ext/afform/core/Civi/Afform/Event/AfformSubmitEvent.php index 522cf21900..548126ce71 100644 --- a/ext/afform/core/Civi/Afform/Event/AfformSubmitEvent.php +++ b/ext/afform/core/Civi/Afform/Event/AfformSubmitEvent.php @@ -2,7 +2,7 @@ namespace Civi\Afform\Event; use Civi\Afform\FormDataModel; -use Civi\Api4\Action\Afform\Submit; +use Civi\Api4\Action\Afform\AbstractProcessor; /** * Handle submission of an "" entity (or set of entities in the case of ``). @@ -34,13 +34,13 @@ class AfformSubmitEvent extends AfformBaseEvent { * * @param array $afform * @param \Civi\Afform\FormDataModel $formDataModel - * @param \Civi\Api4\Action\Afform\Submit $apiRequest + * @param \Civi\Api4\Action\Afform\AbstractProcessor $apiRequest * @param array $records * @param string $entityType * @param string $entityName * @param array $entityIds */ - public function __construct(array $afform, FormDataModel $formDataModel, Submit $apiRequest, &$records, string $entityType, string $entityName, array &$entityIds) { + public function __construct(array $afform, FormDataModel $formDataModel, AbstractProcessor $apiRequest, &$records, string $entityType, string $entityName, array &$entityIds) { parent::__construct($afform, $formDataModel, $apiRequest); $this->records =& $records; $this->entityType = $entityType; diff --git a/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php b/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php index 54135b57ce..cc96c5851d 100644 --- a/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php +++ b/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php @@ -4,6 +4,7 @@ namespace Civi\Api4\Action\Afform; use Civi\Afform\Event\AfformEntitySortEvent; use Civi\Afform\Event\AfformPrefillEvent; +use Civi\Afform\Event\AfformSubmitEvent; use Civi\Afform\FormDataModel; use Civi\API\Exception\UnauthorizedException; use Civi\Api4\Generic\Result; @@ -303,4 +304,117 @@ abstract class AbstractProcessor extends \Civi\Api4\Generic\AbstractAction { return $this; } + /** + * Replace Entity reference fields with the id of the referenced entity. + * @param string $entityName + * @param $records + */ + public function replaceReferences($entityName, $records) { + $entityNames = array_diff(array_keys($this->_entityIds), [$entityName]); + $entityType = $this->_formDataModel->getEntity($entityName)['type']; + foreach ($records as $key => $record) { + foreach ($record['fields'] as $field => $value) { + if (array_intersect($entityNames, (array) $value) && $this->getEntityField($entityType, $field)['input_type'] === 'EntityRef') { + if (is_array($value)) { + foreach ($value as $i => $val) { + if (in_array($val, $entityNames, TRUE)) { + $refIds = array_filter(array_column($this->_entityIds[$val], 'id')); + array_splice($records[$key]['fields'][$field], $i, 1, $refIds); + } + } + } + else { + $records[$key]['fields'][$field] = $this->_entityIds[$value][0]['id'] ?? NULL; + } + } + } + } + return $records; + } + + /** + * @param array $records + * @param string $entityName + */ + public function fillIdFields(array &$records, string $entityName): void { + foreach ($records as $index => &$record) { + if (empty($record['fields']['id']) && !empty($this->_entityIds[$entityName][$index]['id'])) { + $record['fields']['id'] = $this->_entityIds[$entityName][$index]['id']; + } + } + } + + /** + * Recursively add entity IDs to the values. + */ + protected function combineValuesAndIds($values, $ids, $isJoin = FALSE) { + $combined = []; + $values += array_fill_keys(array_keys($ids), []); + foreach ($values as $name => $value) { + foreach ($value as $idx => $val) { + $idData = $ids[$name][$idx] ?? []; + if (!$isJoin) { + $idData['_joins'] = $this->combineValuesAndIds($val['joins'] ?? [], $idData['_joins'] ?? [], TRUE); + } + $item = array_merge($isJoin ? $val : ($val['fields'] ?? []), $idData); + $combined[$name][$idx] = $item; + } + } + return $combined; + } + + /** + * Preprocess submitted values + */ + public function preprocessSubmittedValues(array $submittedValues) { + $entityValues = []; + foreach ($this->_formDataModel->getEntities() as $entityName => $entity) { + $entityValues[$entityName] = []; + // Gather submitted field values from $values['fields'] and sub-entities from $values['joins'] + foreach ($submittedValues[$entityName] ?? [] as $values) { + // Only accept values from fields on the form + $values['fields'] = array_intersect_key($values['fields'] ?? [], $entity['fields']); + // Only accept joins set on the form + $values['joins'] = array_intersect_key($values['joins'] ?? [], $entity['joins']); + foreach ($values['joins'] as $joinEntity => &$joinValues) { + // Enforce the limit set by join[max] + $joinValues = array_slice($joinValues, 0, $entity['joins'][$joinEntity]['max'] ?? NULL); + foreach ($joinValues as $index => $vals) { + // Only accept values from join fields on the form + $joinValues[$index] = array_intersect_key($vals, $entity['joins'][$joinEntity]['fields'] ?? []); + // Merge in pre-set data + $joinValues[$index] = array_merge($joinValues[$index], $entity['joins'][$joinEntity]['data'] ?? []); + } + } + $entityValues[$entityName][] = $values; + } + if (!empty($entity['data'])) { + // If no submitted values but data exists, fill the minimum number of records + for ($index = 0; $index < $entity['min']; $index++) { + $entityValues[$entityName][$index] = $entityValues[$entityName][$index] ?? ['fields' => []]; + } + // Predetermined values override submitted values + foreach ($entityValues[$entityName] as $index => $vals) { + $entityValues[$entityName][$index]['fields'] = $entity['data'] + $vals['fields']; + } + } + } + + return $entityValues; + } + + /** + * Process form data + */ + public function processFormData(array $entityValues) { + $entityWeights = \Civi\Afform\Utils::getEntityWeights($this->_formDataModel->getEntities(), $entityValues); + foreach ($entityWeights as $entityName) { + $entityType = $this->_formDataModel->getEntity($entityName)['type']; + $records = $this->replaceReferences($entityName, $entityValues[$entityName]); + $this->fillIdFields($records, $entityName); + $event = new AfformSubmitEvent($this->_afform, $this->_formDataModel, $this, $records, $entityType, $entityName, $this->_entityIds); + \Civi::dispatcher()->dispatch('civi.afform.submit', $event); + } + } + } diff --git a/ext/afform/core/Civi/Api4/Action/Afform/Process.php b/ext/afform/core/Civi/Api4/Action/Afform/Process.php new file mode 100644 index 0000000000..de77b1a5b2 --- /dev/null +++ b/ext/afform/core/Civi/Api4/Action/Afform/Process.php @@ -0,0 +1,53 @@ +addSelect('data') + ->addWhere('id', '=', $this->submissionId) + ->addWhere('status_id:name', '=', 'Pending') + ->execute()->first(); + + // return early if nothing to process + if (empty($afformSubmissionData)) { + return []; + } + + // preprocess submitted values + $entityValues = $this->preprocessSubmittedValues($afformSubmissionData['data']); + + // process and save various enities + $this->processFormData($entityValues); + + // combine ids with existing data + $submissionData = $this->combineValuesAndIds($afformSubmissionData['data'], $this->_entityIds); + + // Update submission record with entity IDs. + AfformSubmission::update(FALSE) + ->addWhere('id', '=', $this->submissionId) + ->addValue('data', $submissionData) + ->addValue('status_id:name', 'Processed') + ->execute(); + + return $this->_entityIds; + } + +} diff --git a/ext/afform/core/Civi/Api4/Action/Afform/Submit.php b/ext/afform/core/Civi/Api4/Action/Afform/Submit.php index b05c2b087b..90ba0f0866 100644 --- a/ext/afform/core/Civi/Api4/Action/Afform/Submit.php +++ b/ext/afform/core/Civi/Api4/Action/Afform/Submit.php @@ -29,39 +29,8 @@ class Submit extends AbstractProcessor { protected $values; protected function processForm() { - // Preprocess submitted values - $entityValues = []; - foreach ($this->_formDataModel->getEntities() as $entityName => $entity) { - $entityValues[$entityName] = []; - // Gather submitted field values from $values['fields'] and sub-entities from $values['joins'] - foreach ($this->values[$entityName] ?? [] as $values) { - // Only accept values from fields on the form - $values['fields'] = array_intersect_key($values['fields'] ?? [], $entity['fields']); - // Only accept joins set on the form - $values['joins'] = array_intersect_key($values['joins'] ?? [], $entity['joins']); - foreach ($values['joins'] as $joinEntity => &$joinValues) { - // Enforce the limit set by join[max] - $joinValues = array_slice($joinValues, 0, $entity['joins'][$joinEntity]['max'] ?? NULL); - foreach ($joinValues as $index => $vals) { - // Only accept values from join fields on the form - $joinValues[$index] = array_intersect_key($vals, $entity['joins'][$joinEntity]['fields'] ?? []); - // Merge in pre-set data - $joinValues[$index] = array_merge($joinValues[$index], $entity['joins'][$joinEntity]['data'] ?? []); - } - } - $entityValues[$entityName][] = $values; - } - if (!empty($entity['data'])) { - // If no submitted values but data exists, fill the minimum number of records - for ($index = 0; $index < $entity['min']; $index++) { - $entityValues[$entityName][$index] = $entityValues[$entityName][$index] ?? ['fields' => []]; - } - // Predetermined values override submitted values - foreach ($entityValues[$entityName] as $index => $vals) { - $entityValues[$entityName][$index]['fields'] = $entity['data'] + $vals['fields']; - } - } - } + // preprocess submitted values + $entityValues = $this->preprocessSubmittedValues($this->values); // Call validation handlers $event = new AfformValidateEvent($this->_afform, $this->_formDataModel, $this, $entityValues); @@ -92,15 +61,8 @@ class Submit extends AbstractProcessor { return []; } - // Call submit handlers - $entityWeights = \Civi\Afform\Utils::getEntityWeights($this->_formDataModel->getEntities(), $entityValues); - foreach ($entityWeights as $entityName) { - $entityType = $this->_formDataModel->getEntity($entityName)['type']; - $records = $this->replaceReferences($entityName, $entityValues[$entityName]); - $this->fillIdFields($records, $entityName); - $event = new AfformSubmitEvent($this->_afform, $this->_formDataModel, $this, $records, $entityType, $entityName, $this->_entityIds); - \Civi::dispatcher()->dispatch('civi.afform.submit', $event); - } + // process and save various enities + $this->processFormData($entityValues); $submissionData = $this->combineValuesAndIds($this->getValues(), $this->_entityIds); // Update submission record with entity IDs. @@ -117,25 +79,6 @@ class Submit extends AbstractProcessor { ]; } - /** - * Recursively add entity IDs to the values. - */ - protected function combineValuesAndIds($values, $ids, $isJoin = FALSE) { - $combined = []; - $values += array_fill_keys(array_keys($ids), []); - foreach ($values as $name => $value) { - foreach ($value as $idx => $val) { - $idData = $ids[$name][$idx] ?? []; - if (!$isJoin) { - $idData['_joins'] = $this->combineValuesAndIds($val['joins'] ?? [], $idData['_joins'] ?? [], TRUE); - } - $item = array_merge($isJoin ? $val : ($val['fields'] ?? []), $idData); - $combined[$name][$idx] = $item; - } - } - return $combined; - } - /** * Validate required field values * @@ -262,34 +205,6 @@ class Submit extends AbstractProcessor { return NULL; } - /** - * Replace Entity reference fields with the id of the referenced entity. - * @param string $entityName - * @param $records - */ - private function replaceReferences($entityName, $records) { - $entityNames = array_diff(array_keys($this->_entityIds), [$entityName]); - $entityType = $this->_formDataModel->getEntity($entityName)['type']; - foreach ($records as $key => $record) { - foreach ($record['fields'] as $field => $value) { - if (array_intersect($entityNames, (array) $value) && $this->getEntityField($entityType, $field)['input_type'] === 'EntityRef') { - if (is_array($value)) { - foreach ($value as $i => $val) { - if (in_array($val, $entityNames, TRUE)) { - $refIds = array_filter(array_column($this->_entityIds[$val], 'id')); - array_splice($records[$key]['fields'][$field], $i, 1, $refIds); - } - } - } - else { - $records[$key]['fields'][$field] = $this->_entityIds[$value][0]['id'] ?? NULL; - } - } - } - } - return $records; - } - /** * Check if contact(s) meet the minimum requirements to be created (name and/or email). * @@ -512,18 +427,6 @@ class Submit extends AbstractProcessor { return $this; } - /** - * @param array $records - * @param string $entityName - */ - private function fillIdFields(array &$records, string $entityName): void { - foreach ($records as $index => &$record) { - if (empty($record['fields']['id']) && !empty($this->_entityIds[$entityName][$index]['id'])) { - $record['fields']['id'] = $this->_entityIds[$entityName][$index]['id']; - } - } - } - /** * Generates token returned from submit action * diff --git a/ext/afform/core/Civi/Api4/Afform.php b/ext/afform/core/Civi/Api4/Afform.php index 667809eb38..c19a81aa52 100644 --- a/ext/afform/core/Civi/Api4/Afform.php +++ b/ext/afform/core/Civi/Api4/Afform.php @@ -98,6 +98,15 @@ class Afform extends Generic\AbstractEntity { ->setCheckPermissions($checkPermissions); } + /** + * @param bool $checkPermissions + * @return Action\Afform\Process + */ + public static function process($checkPermissions = TRUE) { + return (new Action\Afform\Process('Afform', __FUNCTION__)) + ->setCheckPermissions($checkPermissions); + } + /** * @param bool $checkPermissions * @return Action\Afform\SubmitFile -- 2.25.1