From 7c0f21b317221e296b8486e34181d8c43b2e90f0 Mon Sep 17 00:00:00 2001 From: Seamus Lee Date: Mon, 10 May 2021 19:26:39 +1000 Subject: [PATCH] Rework the listeners so that they are called on the basis of each entitiy rather than just once. Also extract weight function out and make improvements following Coleman's review and add tests --- ext/afform/core/CRM/Afform/Utils.php | 52 ++++++++++++++++ .../Civi/Afform/Event/AfformSubmitEvent.php | 49 ++++++--------- .../core/Civi/Api4/Action/Afform/Submit.php | 62 ++++++++----------- .../tests/phpunit/CRM/Afform/UtilTest.php | 61 ++++++++++++++++++ 4 files changed, 158 insertions(+), 66 deletions(-) create mode 100644 ext/afform/core/CRM/Afform/Utils.php diff --git a/ext/afform/core/CRM/Afform/Utils.php b/ext/afform/core/CRM/Afform/Utils.php new file mode 100644 index 0000000000..4b2d3ec659 --- /dev/null +++ b/ext/afform/core/CRM/Afform/Utils.php @@ -0,0 +1,52 @@ + $entity) { + $entityWeights[$entityName] = 1; + $entityMapping[$entityName] = $entity['type']; + foreach ($entityValues[$entity['type']][$entityName] as $index => $vals) { + foreach ($vals as $field => $value) { + if (array_key_exists($value, $entityWeights)) { + $entityWeights[$entityName] = max((int) $entityWeights[$entityName], (int) ($entityWeights[$value] + 1)); + } + else { + if (!array_key_exists($value, $entitiesToBeProcessed)) { + $entitiesToBeProcessed[$value] = [$entityName]; + } + else { + $entitiesToBeProcessed[$value][] = $entityName; + } + } + } + } + // If any other entities have been processed that relied on this entity lets now alter their weights based on this entity's weight. + if (array_key_exists($entityName, $entitiesToBeProcessed)) { + foreach ($entitiesToBeProcessed[$entityName] as $dependentEntity) { + $entityWeights[$dependentEntity] = max((int) $entityWeights[$dependentEntity], (int) ($entityWeights[$entityName] + 1)); + } + } + } + // Numerically sort the weights now that we have them set + asort($entityWeights); + return $entityWeights; + } + +} diff --git a/ext/afform/core/Civi/Afform/Event/AfformSubmitEvent.php b/ext/afform/core/Civi/Afform/Event/AfformSubmitEvent.php index f2bdf951aa..c4f968c1b9 100644 --- a/ext/afform/core/Civi/Afform/Event/AfformSubmitEvent.php +++ b/ext/afform/core/Civi/Afform/Event/AfformSubmitEvent.php @@ -20,17 +20,22 @@ class AfformSubmitEvent extends AfformBaseEvent { /** * @var array - * List of definitions of the entities. - * $entityDefns['spouse'] = ['type' => 'Individual']; + * Values to be saved for this entity + * */ - public $entityDefns; + public $values; /** - * @var array - * List of submitted entities to save. - * $entityValues['Contact']['spouse'] = ['first_name' => 'Optimus Prime']; + * @var string + * entityType + */ + public $entityType; + + /** + * @var string + * entityName e.g. Individual1, Activity1, */ - public $entityValues; + public $entityName; /** * @var array @@ -39,39 +44,23 @@ class AfformSubmitEvent extends AfformBaseEvent { */ public $entityIds; - public $entityWeights; - - public $entityMapping; - /** * AfformSubmitEvent constructor. * * @param array $afform * @param \Civi\Afform\FormDataModel $formDataModel * @param \Civi\Api4\Action\Afform\Submit $apiRequest - * @param array $entityDefns - * @param array $entityValues + * @param array $values + * @param string $entityType + * @param string $entityName * @param array $entityIds - * @param array $entityWeights - * @param array $entityMapping */ - public function __construct(array $afform, FormDataModel $formDataModel, Submit $apiRequest, $entityDefns, array $entityValues, array $entityIds, array $entityWeights, array $entityMapping) { + public function __construct(array $afform, FormDataModel $formDataModel, Submit $apiRequest, $values, string $entityType, string $entityName, array $entityIds) { parent::__construct($afform, $formDataModel, $apiRequest); - $this->entityDefns = $entityDefns; - $this->entityValues = $entityValues; + $this->values = $values; + $this->entityType = $entityType; + $this->entityName = $entityName; $this->entityIds = $entityIds; - $this->entityWeights = $entityWeights; - $this->entityMapping = $entityMapping; - } - - /** - * List of entity types which need processing. - * - * @return array - * Ex: ['Contact', 'Activity'] - */ - public function getTypes() { - return array_keys($this->entityValues); } } diff --git a/ext/afform/core/Civi/Api4/Action/Afform/Submit.php b/ext/afform/core/Civi/Api4/Action/Afform/Submit.php index b79b807150..ddfefa5917 100644 --- a/ext/afform/core/Civi/Api4/Action/Afform/Submit.php +++ b/ext/afform/core/Civi/Api4/Action/Afform/Submit.php @@ -20,11 +20,9 @@ class Submit extends AbstractProcessor { protected $values; protected function processForm() { - $entityValues = $entityIds = $entityWeights = $entityMapping = []; + $entityValues = $entityIds = []; foreach ($this->_formDataModel->getEntities() as $entityName => $entity) { $entityIds[$entityName] = NULL; - $entityWeights[$entityName] = 1; - $entityMapping[$entityName] = $entity['type']; foreach ($this->values[$entityName] ?? [] as $values) { $entityValues[$entity['type']][$entityName][] = $values + ['fields' => []]; // Predetermined values override submitted values @@ -34,18 +32,13 @@ class Submit extends AbstractProcessor { } } } - foreach ($entityValues[$entity['type']][$entityName] as $index => $vals) { - foreach ($vals as $field => $value) { - if (in_array($value, array_keys($entityWeights))) { - $entityWeights[$entityName] = max((int) $entityWeights[$entityName], (int) ($entityWeights[$value] + 1)); - } - } - } } - // Numerically sort the weights smallest to largest. - asort($entityWeights); - $event = new AfformSubmitEvent($this->_afform, $this->_formDataModel, $this, $this->_formDataModel->getEntities(), $entityValues, $entityIds, $entityWeights, $entityMapping); - \Civi::dispatcher()->dispatch(self::EVENT_NAME, $event); + $entityWeights = \CRM_Afform_Utils::getEntityWeights($this->_formDataModel->getEntities(), $entityValues); + foreach ($entityWeights as $entity => $weight) { + $values = $entityValues[$entityMapping[$entity]][$entity]; + $event = new AfformSubmitEvent($this->_afform, $this->_formDataModel, $this, $values, $entityMapping[$entity], $entity, $entityIds); + \Civi::dispatcher()->dispatch(self::EVENT_NAME, $event); + } foreach ($event->entityValues as $entityType => $entities) { if (!empty($entities)) { throw new \API_Exception(sprintf("Failed to process entities (type=%s; name=%s)", $entityType, implode(',', array_keys($entities)))); @@ -62,15 +55,14 @@ class Submit extends AbstractProcessor { * @see afform_civicrm_config */ public static function processContacts(AfformSubmitEvent $event) { - foreach ($event->entityValues['Contact'] ?? [] as $entityName => $contacts) { - $api4 = $event->formDataModel->getSecureApi4($entityName); - foreach ($contacts as $contact) { - $saved = $api4('Contact', 'save', ['records' => [$contact['fields']]])->first(); - $event->entityIds[$entityName] = $saved['id']; - self::saveJoins('Contact', $saved['id'], $contact['joins'] ?? []); - } + if ($event->entityType !== 'Contact') { + return; } - unset($event->entityValues['Contact']); + $entityName = $event->entityName; + $api4 = $event->formDataModel->getSecureApi4($entityName); + $saved = $api4('Contact', 'save', ['records' => [$event->values['fields']]])->first(); + $event->entityIds[$entityName] = $saved['id']; + self::saveJoins('Contact', $saved['id'], $event->values['joins'] ?? []); } /** @@ -79,22 +71,20 @@ class Submit extends AbstractProcessor { * @see afform_civicrm_config */ public static function processGenericEntity(AfformSubmitEvent $event) { - foreach ($event->entityWeights as $entityName => $weight) { - $records = $event->entityValues[$event->entityMapping[$entityName]][$entityName]; - $api4 = $event->formDataModel->getSecureApi4($entityName); - // Replace Entity reference fields that reference other form based entities with their created ids. - foreach ($records as $record) { - foreach ($record['fields'] as $field => $value) { - if (in_array($value, array_keys($event->entityIds)) && !empty($event->entityIds[$value])) { - $record['fields'][$field] = $event->entityIds[$value]; - } - } - $saved = $api4($entityType, 'save', ['records' => [$record['fields']]])->first(); - $event->entityIds[$entityName] = $saved['id']; - self::saveJoins($entityType, $saved['id'], $record['joins'] ?? []); + if ($event->entityType === 'Contact') { + return; + } + $entityName = $event->entityName; + $api4 = $event->formDataModel->getSecureApi4($event->entityName); + // Replace Entity reference fields that reference other form based entities with their created ids. + foreach ($record['fields'] as $field => $value) { + if (array_key_exists($value, $event->entityIds) && !empty($event->entityIds[$value])) { + $record['fields'][$field] = $event->entityIds[$value]; } } - unset($event->entityValues[$entityType]); + $saved = $api4($entityType, 'save', ['records' => [$event->values['fields']]])->first(); + $event->entityIds[$entityName] = $saved['id']; + self::saveJoins($entityType, $saved['id'], $event->values['joins'] ?? []); } protected static function saveJoins($mainEntityName, $entityId, $joins) { diff --git a/ext/afform/core/tests/phpunit/CRM/Afform/UtilTest.php b/ext/afform/core/tests/phpunit/CRM/Afform/UtilTest.php index c759c16ee6..7715d9d0d8 100644 --- a/ext/afform/core/tests/phpunit/CRM/Afform/UtilTest.php +++ b/ext/afform/core/tests/phpunit/CRM/Afform/UtilTest.php @@ -61,4 +61,65 @@ class CRM_Afform_UtilTest extends \PHPUnit\Framework\TestCase implements Headles $this->assertEquals($expected, $actual); } + + public function formEntityWeightExampls() { + $exs = []; + $exs[] = [ + [ + 'Individual1' => ['type' => 'Contact', ['fields' => ['first_name', 'last_name']]], + 'Activity1' => ['type' => 'Activity', ['fields' => ['source_contact_id']]], + ], + [ + 'Contact' => ['Individual1' => ['fields' => ['first_name' => 'Test', 'last_name' => 'Contact']]], + 'Activity' => ['Activity1' => ['fields' => ['source_contact_id' => 'Individual1']]], + ], + [ + 'Individual1' => 1, + 'Activity1' => 2, + ], + ]; + $exs[] = [ + [ + 'Individual1' => ['type' => 'Contact', ['fields' => ['first_name', 'last_name']]], + 'Event1' => ['type' => 'Event', ['fields' => ['created_id']]], + 'LocBlock1' => ['type' => 'LocBlock', ['fields' => ['event_id']]], + ], + [ + 'Contact' => ['Individual1' => ['fields' => ['first_name' => 'Test', 'last_name' => 'Contact']]], + 'Event' => ['Event1' => ['fields' => ['created_id' => 'Individual1']]], + 'LocBlock' => ['LocBlock1' => ['fields' => ['event_id' => 'Event1']]], + ], + [ + 'Individual1' => 1, + 'Event1' => 2, + 'LocBlock1' => 3, + ], + ]; + $exs[] = [ + [ + 'Individual1' => ['type' => 'Contact', ['fields' => ['first_name', 'last_name']]], + 'LocBlock1' => ['type' => 'LocBlock', ['fields' => ['event_id']]], + 'Event1' => ['type' => 'Event', ['fields' => ['created_id']]], + ], + [ + 'Contact' => ['Individual1' => ['fields' => ['first_name' => 'Test', 'last_name' => 'Contact']]], + 'LocBlock' => ['LocBlock1' => ['fields' => ['event_id' => 'Event1']]], + 'Event' => ['Event1' => ['fields' => ['created_id' => 'Individual1']]], + ], + [ + 'Individual1' => 1, + 'Event1' => 2, + 'LocBlock1' => 3, + ], + ]; + return $exs; + } + + /** + * @dataProvider formEntityWeightExampls + */ + public function testEntityWeights($formEntities, $entityValues, $expectedWeights) { + $this->assertEquals($expectedWeights, CRM_Afform_Utils::getEntityWeights($formEntities, $entityValues)); + } + } -- 2.25.1