From 6cdcdc2dd900053defe6d2ee4888558d41b2dd27 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Wed, 14 Dec 2022 17:27:15 -0500 Subject: [PATCH] Afform - Support repeatable relationships --- .../core/Civi/Api4/Action/Afform/Submit.php | 89 ++++++++++--------- .../api/v4/AfformRelationshipUsageTest.php | 81 ++++++++++++++++- 2 files changed, 125 insertions(+), 45 deletions(-) diff --git a/ext/afform/core/Civi/Api4/Action/Afform/Submit.php b/ext/afform/core/Civi/Api4/Action/Afform/Submit.php index 1e886b126a..44dc9a5d9d 100644 --- a/ext/afform/core/Civi/Api4/Action/Afform/Submit.php +++ b/ext/afform/core/Civi/Api4/Action/Afform/Submit.php @@ -344,52 +344,55 @@ class Submit extends AbstractProcessor { // Prevent processGenericEntity $event->stopPropagation(); $api4 = $event->getSecureApi4(); - $relationship = $event->records[0]['fields'] ?? []; - if (empty($relationship['contact_id_a']) || empty($relationship['contact_id_b']) || empty($relationship['relationship_type_id'])) { - return; - } - $relationshipType = RelationshipType::get(FALSE) - ->addWhere('id', '=', $relationship['relationship_type_id']) - ->execute()->single(); - $isReciprocal = $relationshipType['label_a_b'] == $relationshipType['label_b_a']; - $isActive = !isset($relationship['is_active']) || !empty($relationship['is_active']); - // Each contact id could be multivalued (e.g. using `af-repeat`) - foreach ((array) $relationship['contact_id_a'] as $contact_id_a) { - foreach ((array) $relationship['contact_id_b'] as $contact_id_b) { - $params = $relationship; - $params['contact_id_a'] = $contact_id_a; - $params['contact_id_b'] = $contact_id_b; - // Check for existing relationships (if allowed) - if (!empty($event->getEntity()['actions']['update'])) { - $where = [ - ['is_active', '=', $isActive], - ['relationship_type_id', '=', $relationship['relationship_type_id']], - ]; - // Reciprocal relationship types need an extra check - if ($isReciprocal) { - $where[] = [ - 'OR', [ - ['AND', [['contact_id_a', '=', $contact_id_a], ['contact_id_b', '=', $contact_id_b]]], - ['AND', [['contact_id_a', '=', $contact_id_b], ['contact_id_b', '=', $contact_id_a]]], - ], + // Iterate through multiple relationships (if using af-repeat) + foreach ($event->records as $relationship) { + $relationship = $relationship['fields'] ?? []; + if (empty($relationship['contact_id_a']) || empty($relationship['contact_id_b']) || empty($relationship['relationship_type_id'])) { + return; + } + $relationshipType = RelationshipType::get(FALSE) + ->addWhere('id', '=', $relationship['relationship_type_id']) + ->execute()->single(); + $isReciprocal = $relationshipType['label_a_b'] == $relationshipType['label_b_a']; + $isActive = !isset($relationship['is_active']) || !empty($relationship['is_active']); + // Each contact id could be multivalued (e.g. using `af-repeat`) + foreach ((array) $relationship['contact_id_a'] as $contact_id_a) { + foreach ((array) $relationship['contact_id_b'] as $contact_id_b) { + $params = $relationship; + $params['contact_id_a'] = $contact_id_a; + $params['contact_id_b'] = $contact_id_b; + // Check for existing relationships (if allowed) + if (!empty($event->getEntity()['actions']['update'])) { + $where = [ + ['is_active', '=', $isActive], + ['relationship_type_id', '=', $relationship['relationship_type_id']], ]; + // Reciprocal relationship types need an extra check + if ($isReciprocal) { + $where[] = [ + 'OR', [ + ['AND', [['contact_id_a', '=', $contact_id_a], ['contact_id_b', '=', $contact_id_b]]], + ['AND', [['contact_id_a', '=', $contact_id_b], ['contact_id_b', '=', $contact_id_a]]], + ], + ]; + } + else { + $where[] = ['contact_id_a', '=', $contact_id_a]; + $where[] = ['contact_id_b', '=', $contact_id_b]; + } + $existing = $api4('Relationship', 'get', ['where' => $where])->first(); + if ($existing) { + $params['id'] = $existing['id']; + unset($params['contact_id_a'], $params['contact_id_b']); + // If this is a flipped reciprocal relationship, also flip the permissions + $params['is_permission_a_b'] = $relationship['is_permission_b_a'] ?? NULL; + $params['is_permission_b_a'] = $relationship['is_permission_a_b'] ?? NULL; + } } - else { - $where[] = ['contact_id_a', '=', $contact_id_a]; - $where[] = ['contact_id_b', '=', $contact_id_b]; - } - $existing = $api4('Relationship', 'get', ['where' => $where])->first(); - if ($existing) { - $params['id'] = $existing['id']; - unset($params['contact_id_a'], $params['contact_id_b']); - // If this is a flipped reciprocal relationship, also flip the permissions - $params['is_permission_a_b'] = $relationship['is_permission_b_a'] ?? NULL; - $params['is_permission_b_a'] = $relationship['is_permission_a_b'] ?? NULL; - } + $api4('Relationship', 'save', [ + 'records' => [$params], + ]); } - $api4('Relationship', 'save', [ - 'records' => [$params], - ]); } } } diff --git a/ext/afform/mock/tests/phpunit/api/v4/AfformRelationshipUsageTest.php b/ext/afform/mock/tests/phpunit/api/v4/AfformRelationshipUsageTest.php index a31a7b66cf..bb12c0e857 100644 --- a/ext/afform/mock/tests/phpunit/api/v4/AfformRelationshipUsageTest.php +++ b/ext/afform/mock/tests/phpunit/api/v4/AfformRelationshipUsageTest.php @@ -12,7 +12,7 @@ class api_v4_AfformRelationshipUsageTest extends api_v4_AfformUsageTestCase { /** * Tests creating a relationship between multiple contacts */ - public function testCreateContactsWithRelationships(): void { + public function testCreateContactsWithPresetRelationships(): void { $layout = << @@ -53,13 +53,90 @@ EOHTML; $saved = Relationship::get(FALSE) ->addWhere('contact_id_b.last_name', '=', $lastName) - ->addSelect('contact_id_a.first_name', 'is_active') + ->addSelect('contact_id_a.first_name', 'is_active', 'relationship_type_id') ->addOrderBy('contact_id_a.first_name') ->execute(); $this->assertCount(2, $saved); $this->assertEquals('Firsty2', $saved[0]['contact_id_a.first_name']); $this->assertEquals('Firsty3', $saved[1]['contact_id_a.first_name']); + $this->assertEquals(1, $saved[0]['relationship_type_id']); + $this->assertEquals(1, $saved[1]['relationship_type_id']); + } + + /** + * Tests creating multiple relationships using af-repeat + */ + public function testCreateContactsWithMultipleRelationships(): void { + $layout = << + + + +
+ +
+
+ +
+
+ +
+ + +EOHTML; + + $this->useValues([ + 'layout' => $layout, + 'permission' => CRM_Core_Permission::ALWAYS_ALLOW_PERMISSION, + ]); + + $types = [ + uniqid(__FUNCTION__), + uniqid(__FUNCTION__), + ]; + $typeIds = []; + + foreach ($types as $type) { + $typeIds[] = \Civi\Api4\RelationshipType::create(FALSE) + ->addValue('contact_type_a', 'Organization') + ->addValue('contact_type_b', 'Individual') + ->addValue('name_a_b', $type) + ->addValue('name_b_a', "$type of") + ->execute()->first()['id']; + } + + $lastName = uniqid(__FUNCTION__); + + $submission = [ + 'Individual1' => [ + ['fields' => ['first_name' => 'Firsty', 'last_name' => $lastName]], + ], + 'Org1' => [ + ['fields' => ['organization_name' => "Hello $lastName"]], + ], + 'Relationship1' => [ + ['fields' => ['relationship_type_id' => $typeIds[0]]], + ['fields' => ['relationship_type_id' => $typeIds[1]]], + ], + ]; + + Civi\Api4\Afform::submit() + ->setName($this->formName) + ->setValues($submission) + ->execute(); + + $saved = Relationship::get(FALSE) + ->addWhere('contact_id_b.last_name', '=', $lastName) + ->addSelect('contact_id_a.organization_name', 'is_active', 'relationship_type_id') + ->addOrderBy('relationship_type_id') + ->execute(); + + $this->assertEquals("Hello $lastName", $saved[0]['contact_id_a.organization_name']); + $this->assertEquals($typeIds[0], $saved[0]['relationship_type_id']); + $this->assertEquals("Hello $lastName", $saved[1]['contact_id_a.organization_name']); + $this->assertEquals($typeIds[1], $saved[1]['relationship_type_id']); + $this->assertCount(2, $saved); } public function testPrefillContactsByRelationship(): void { -- 2.25.1