diff --git a/CRM/Contact/BAO/Relationship.php b/CRM/Contact/BAO/Relationship.php index c942c3f7a890..a4713106cb49 100644 --- a/CRM/Contact/BAO/Relationship.php +++ b/CRM/Contact/BAO/Relationship.php @@ -65,7 +65,7 @@ public static function create(&$params) { // Check if this is a "simple" disable relationship. If it is don't check the relationshipType $disableRelationship = !empty($params['id']) && array_key_exists('is_active', $params) && empty($params['is_active']); if (!$disableRelationship && !CRM_Contact_BAO_Relationship::checkRelationshipType($params['contact_id_a'], $params['contact_id_b'], $params['relationship_type_id'])) { - throw new CRM_Core_Exception('Invalid Relationship'); + throw new CRM_Core_Exception('Invalid Relationship', 'invalid_relationship'); } $relationship = self::add($params); if (!empty($params['contact_id_a'])) { @@ -114,38 +114,47 @@ public static function create(&$params) { */ public static function createMultiple($params, $primaryContactLetter) { $secondaryContactLetter = ($primaryContactLetter == 'a') ? 'b' : 'a'; - $secondaryContactIDs = $params['contact_id_' . $secondaryContactLetter]; - $valid = $invalid = $duplicate = $saved = 0; - $relationshipIDs = []; - foreach ($secondaryContactIDs as $secondaryContactID) { - try { - $params['contact_id_' . $secondaryContactLetter] = $secondaryContactID; - $relationship = civicrm_api3('relationship', 'create', $params); - $relationshipIDs[] = $relationship['id']; - $valid++; + $secondaryContactIDs = (array) ($params['contact_id_' . $secondaryContactLetter] ?? []); + unset($params['contact_id_' . $secondaryContactLetter]); + + // Convert custom data to api4-style params + foreach ($params as $fieldName => $param) { + $customFieldName = CRM_Core_BAO_CustomField::getLongNameFromShortName($fieldName); + if ($customFieldName) { + $params[$customFieldName] = $param; + unset($params[$fieldName]); } - catch (CRM_Core_Exception $e) { - switch ($e->getMessage()) { - case 'Duplicate Relationship': - $duplicate++; - break; - - case 'Invalid Relationship': - $invalid++; - break; - - default: - throw new CRM_Core_Exception('unknown relationship create error ' . $e->getMessage()); - } + } + + $invalid = $duplicate = 0; + + $saveAction = \Civi\Api4\Relationship::save(FALSE) + ->setDefaults($params); + foreach ($secondaryContactIDs as $secondaryContactID) { + $saveAction->addRecord(['contact_id_' . $secondaryContactLetter => $secondaryContactID]); + } + $saveResult = $saveAction->execute(); + + foreach ($saveResult->getErrors() as $error) { + switch ($error->getCode()) { + case 'duplicate': + $duplicate++; + break; + + case 'invalid_relationship': + $invalid++; + break; + + default: + throw new CRM_Core_Exception('unknown relationship create error ' . $error->getMessage()); } } return [ - 'valid' => $valid, + 'valid' => $saveResult->count(), 'invalid' => $invalid, 'duplicate' => $duplicate, - 'saved' => $saved, - 'relationship_ids' => $relationshipIDs, + 'relationship_ids' => $saveResult->column('id'), ]; } diff --git a/Civi/Api4/Action/Relationship/RelationshipSaveTrait.php b/Civi/Api4/Action/Relationship/RelationshipSaveTrait.php index 2a9850718376..c6ef6394e884 100644 --- a/Civi/Api4/Action/Relationship/RelationshipSaveTrait.php +++ b/Civi/Api4/Action/Relationship/RelationshipSaveTrait.php @@ -12,27 +12,34 @@ namespace Civi\Api4\Action\Relationship; +use Civi\Api4\Generic\Result; + /** * @inheritDoc */ trait RelationshipSaveTrait { + /** + * @var \Civi\Api4\Generic\Result|null + */ + private $_result; + + public function _run(Result $result) { + $this->_result = $result; + parent::_run($result); + } + /** * @inheritDoc */ protected function write(array $items) { $result = []; - foreach ($items as $item) { + foreach ($items as $index => $item) { try { - $result[] = \CRM_Contact_BAO_Relationship::create($item); + $result[$index] = \CRM_Contact_BAO_Relationship::create($item); } catch (\CRM_Core_Exception $e) { - if ($e->getErrorCode() === 'duplicate') { - $result[] = $e->getErrorData(); - } - else { - throw $e; - } + $this->_result->addError($e->getMessage(), code: $e->getErrorCode(), metadata: $e->getErrorData()); } } return $result; diff --git a/tests/phpunit/CRM/Contact/BAO/RelationshipTest.php b/tests/phpunit/CRM/Contact/BAO/RelationshipTest.php index 6045f1cb33d6..8e7e764a2a78 100644 --- a/tests/phpunit/CRM/Contact/BAO/RelationshipTest.php +++ b/tests/phpunit/CRM/Contact/BAO/RelationshipTest.php @@ -374,4 +374,85 @@ public function testDisableInvalidRelationship(): void { ->execute(); } + /** + * Test CRM_Contact_BAO_Relationship::createMultiple handling of valid, duplicate, and invalid relationships. + * + * @throws \CRM_Core_Exception + */ + public function testCreateMultiple(): void { + $relTypeId = $this->relationshipTypeCreate([ + 'name_a_b' => 'Individual_To_Individual_A_B', + 'name_b_a' => 'Individual_To_Individual_B_A', + 'contact_type_a' => 'Individual', + 'contact_type_b' => 'Individual', + ]); + + $individualA = $this->individualCreate(['display_name' => 'Primary Individual A']); + $individualBValid = $this->individualCreate(['display_name' => 'Secondary Individual Valid']); + $individualBDuplicate = $this->individualCreate(['display_name' => 'Secondary Individual Duplicate']); + $organizationBInvalid = $this->organizationCreate(['organization_name' => 'Secondary Org Invalid']); + + // Create existing relationship for duplicate check + Relationship::create(FALSE) + ->addValue('contact_id_a', $individualA) + ->addValue('contact_id_b', $individualBDuplicate) + ->addValue('relationship_type_id', $relTypeId) + ->execute(); + + // Call createMultiple with primaryContactLetter = 'a' + $params = [ + 'contact_id_a' => $individualA, + 'contact_id_b' => [$individualBValid, $individualBDuplicate, $organizationBInvalid], + 'relationship_type_id' => $relTypeId, + ]; + + $result = CRM_Contact_BAO_Relationship::createMultiple($params, 'a'); + + $this->assertEquals(1, $result['valid']); + $this->assertEquals(1, $result['duplicate']); + $this->assertEquals(1, $result['invalid']); + $this->assertCount(1, $result['relationship_ids']); + + // Verify created relationship in DB + $createdRel = Relationship::get(FALSE) + ->addWhere('id', '=', $result['relationship_ids'][0]) + ->execute() + ->first(); + $this->assertEquals($individualA, $createdRel['contact_id_a']); + $this->assertEquals($individualBValid, $createdRel['contact_id_b']); + + // Call createMultiple with primaryContactLetter = 'b' + $individualB = $this->individualCreate(['display_name' => 'Primary Individual B']); + $individualAValid = $this->individualCreate(['display_name' => 'Secondary Individual A Valid']); + $individualADuplicate = $this->individualCreate(['display_name' => 'Secondary Individual A Duplicate']); + $organizationAInvalid = $this->organizationCreate(['organization_name' => 'Secondary Org A Invalid']); + $organizationBInvalid = $this->organizationCreate(['organization_name' => 'Secondary Org B Invalid']); + + Relationship::create(FALSE) + ->addValue('contact_id_a', $individualADuplicate) + ->addValue('contact_id_b', $individualB) + ->addValue('relationship_type_id', $relTypeId) + ->execute(); + + $paramsB = [ + 'contact_id_a' => [$individualAValid, $individualADuplicate, $organizationAInvalid, $organizationBInvalid], + 'contact_id_b' => $individualB, + 'relationship_type_id' => $relTypeId, + ]; + + $resultB = CRM_Contact_BAO_Relationship::createMultiple($paramsB, 'b'); + + $this->assertEquals(1, $resultB['valid']); + $this->assertEquals(1, $resultB['duplicate']); + $this->assertEquals(2, $resultB['invalid']); + $this->assertCount(1, $resultB['relationship_ids']); + + $createdRelB = Relationship::get(FALSE) + ->addWhere('id', '=', $resultB['relationship_ids'][0]) + ->execute() + ->first(); + $this->assertEquals($individualAValid, $createdRelB['contact_id_a']); + $this->assertEquals($individualB, $createdRelB['contact_id_b']); + } + } diff --git a/tests/phpunit/api/v3/RelationshipTest.php b/tests/phpunit/api/v3/RelationshipTest.php index a8451dfe8ce2..a0755383e850 100644 --- a/tests/phpunit/api/v3/RelationshipTest.php +++ b/tests/phpunit/api/v3/RelationshipTest.php @@ -333,21 +333,24 @@ public function testRelationshipCreateEditWithCustomData(): void { $this->getCustomFieldName('link') => 'http://example.com', ]; - $params = [ - 'contact_id_a' => $this->_cId_a, - 'contact_id_b' => $this->_cId_b, - 'relationship_type_id' => $this->relationshipTypeID, + $createParams = array_merge([ + 'relationship_type_id' => $this->relationshipTypeID . '_a_b', + 'related_contact_id' => $this->_cId_b, 'start_date' => '2008-12-20', 'is_active' => 1, - ]; - $params = array_merge($params, $custom_params); - $result = $this->callAPISuccess('relationship', 'create', $params); + ], $custom_params); - $relationParams = ['id' => $result['id']]; - $this->assertDBState('CRM_Contact_DAO_Relationship', $result['id'], $relationParams); + $reln = new CRM_Contact_Form_Relationship(); + $reln->_action = CRM_Core_Action::ADD; + $reln->_contactId = $this->_cId_a; + [, $relationshipIds] = $reln->submit($createParams); + $relationshipId = $relationshipIds[0]; + + $getParams = ['id' => $relationshipId]; + $check = $this->callAPISuccess('relationship', 'get', $getParams); + $this->assertEquals('Hello! this is custom data for relationship', $check['values'][$check['id']][$this->getCustomFieldName('text')]); //Test Edit of custom field from the form. - $getParams = ['id' => $result['id']]; $updateParams = array_merge($getParams, [ $this->getCustomFieldName('text') => 'Edited Text Value', 'relationship_type_id' => $this->relationshipTypeID . '_b_a', @@ -355,7 +358,7 @@ public function testRelationshipCreateEditWithCustomData(): void { ]); $reln = new CRM_Contact_Form_Relationship(); $reln->_action = CRM_Core_Action::UPDATE; - $reln->_relationshipId = $result['id']; + $reln->_relationshipId = $relationshipId; $reln->submit($updateParams); $check = $this->callAPISuccess('relationship', 'get', $getParams);