4 * File for the TestContact class.
8 * @author Walt Haas <walt@dharmatech.org> (801) 534-1262
9 * @copyright Copyright CiviCRM LLC (C) 2009
10 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html
11 * GNU Affero General Public License version 3
12 * @version $Id: ContactTest.php 31254 2010-12-15 10:09:29Z eileen $
15 * This file is part of CiviCRM
17 * CiviCRM is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU Affero General Public License
19 * as published by the Free Software Foundation; either version 3 of
20 * the License, or (at your option) any later version.
22 * CiviCRM is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU Affero General Public License for more details.
27 * You should have received a copy of the GNU Affero General Public
28 * License along with this program. If not, see
29 * <http://www.gnu.org/licenses/>.
33 * Test APIv3 civicrm_contact* functions
35 * @package CiviCRM_APIv3
36 * @subpackage API_Contact
39 class api_v3_ContactTest
extends CiviUnitTestCase
{
41 use CRMTraits_Custom_CustomDataTrait
;
43 public $DBResetRequired = FALSE;
45 protected $_apiversion;
51 protected $_contactID;
53 protected $_financialTypeId = 1;
56 * Entity to be extended.
60 protected $entity = 'Contact';
63 * Test setup for every test.
65 * Connect to the database, truncate the tables that will be used
66 * and redirect stdin to a temporary file
68 public function setUp() {
69 // Connect to the database.
71 $this->_entity
= 'contact';
73 'first_name' => 'abc1',
74 'contact_type' => 'Individual',
75 'last_name' => 'xyz1',
80 * Restore the DB for the next test.
84 public function tearDown() {
85 $this->_apiversion
= 3;
86 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => TRUE]);
87 // truncate a few tables
90 'civicrm_contribution',
93 'civicrm_relationship',
97 'civicrm_acl_contact_cache',
98 'civicrm_activity_contact',
101 'civicrm_group_contact',
102 'civicrm_saved_search',
103 'civicrm_group_contact_cache',
104 'civicrm_prevnext_cache',
107 $this->quickCleanup($tablesToTruncate, TRUE);
112 * Test civicrm_contact_create.
114 * Verify that attempt to create individual contact with only
115 * first and last names succeeds
117 * @param int $version
119 * @dataProvider versionThreeAndFour
121 * @throws \CRM_Core_Exception
123 public function testAddCreateIndividual($version) {
124 $this->_apiversion
= $version;
125 $oldCount = CRM_Core_DAO
::singleValueQuery('select count(*) from civicrm_contact');
127 'first_name' => 'abc1',
128 'contact_type' => 'Individual',
129 'last_name' => 'xyz1',
132 $contact = $this->callAPISuccess('contact', 'create', $params);
133 $this->assertTrue(is_numeric($contact['id']));
134 $this->assertTrue($contact['id'] > 0);
135 $newCount = CRM_Core_DAO
::singleValueQuery('select count(*) from civicrm_contact');
136 $this->assertEquals($oldCount +
1, $newCount);
138 $this->assertDBState('CRM_Contact_DAO_Contact',
145 * Test that it is possible to prevent cache clearing via option.
147 * Cache clearing is bypassed if 'options' => array('do_not_reset_cache' => 1 is used.
149 * @throws \CRM_Core_Exception
151 public function testCreateIndividualNoCacheClear() {
153 $contact = $this->callAPISuccess('contact', 'create', $this->_params
);
154 $groupID = $this->groupCreate();
156 $this->putGroupContactCacheInClearableState($groupID, $contact);
158 $this->callAPISuccess('contact', 'create', ['id' => $contact['id']]);
159 $this->assertEquals(0, CRM_Core_DAO
::singleValueQuery("SELECT count(*) FROM civicrm_group_contact_cache"));
161 // Rinse & repeat, but with the option.
162 $this->putGroupContactCacheInClearableState($groupID, $contact);
163 CRM_Core_Config
::setPermitCacheFlushMode(FALSE);
164 $this->callAPISuccess('contact', 'create', ['id' => $contact['id']]);
165 $this->assertEquals(1, CRM_Core_DAO
::singleValueQuery("SELECT count(*) FROM civicrm_group_contact_cache"));
166 CRM_Core_Config
::setPermitCacheFlushMode(TRUE);
170 * Test for international string acceptance (CRM-10210).
171 * Requires the database to be in utf8.
173 * @dataProvider getInternationalStrings
175 * @param string $string
176 * String to be tested.
178 * Bool to see if we should check charset.
180 * @throws \CRM_Core_Exception
182 public function testInternationalStrings($string) {
183 $this->callAPISuccess('Contact', 'create', array_merge(
185 ['first_name' => $string]
188 $result = $this->callAPISuccessGetSingle('Contact', ['first_name' => $string]);
189 $this->assertEquals($string, $result['first_name']);
191 $organizationParams = [
192 'organization_name' => $string,
193 'contact_type' => 'Organization',
196 $this->callAPISuccess('Contact', 'create', $organizationParams);
197 $result = $this->callAPISuccessGetSingle('Contact', $organizationParams);
198 $this->assertEquals($string, $result['organization_name']);
202 * Get international string data for testing against api calls.
204 public function getInternationalStrings() {
206 $invocations[] = ['Scarabée'];
207 $invocations[] = ['Iñtërnâtiônàlizætiøn'];
208 $invocations[] = ['これは日本語のテキストです。読めますか'];
209 $invocations[] = ['देखें हिन्दी कैसी नजर आती है। अरे वाह ये तो नजर आती है।'];
214 * Test civicrm_contact_create.
216 * Verify that preferred language can be set.
218 * @param int $version
220 * @dataProvider versionThreeAndFour
221 * @throws \CRM_Core_Exception
223 public function testAddCreateIndividualWithPreferredLanguage($version) {
224 $this->_apiversion
= $version;
226 'first_name' => 'abc1',
227 'contact_type' => 'Individual',
228 'last_name' => 'xyz1',
229 'preferred_language' => 'es_ES',
232 $contact = $this->callAPISuccess('contact', 'create', $params);
233 $this->getAndCheck($params, $contact['id'], 'Contact');
237 * Test civicrm_contact_create with sub-types.
239 * Verify that sub-types are created successfully and not deleted by subsequent updates.
241 * @param int $version
243 * @dataProvider versionThreeAndFour
244 * @throws \CRM_Core_Exception
246 public function testIndividualSubType($version) {
247 $this->_apiversion
= $version;
249 'first_name' => 'test abc',
250 'contact_type' => 'Individual',
251 'last_name' => 'test xyz',
252 'contact_sub_type' => ['Student', 'Staff'],
254 $contact = $this->callAPISuccess('contact', 'create', $params);
255 $cid = $contact['id'];
259 'middle_name' => 'foo',
261 $this->callAPISuccess('contact', 'create', $params);
263 $contact = $this->callAPISuccess('contact', 'get', ['id' => $cid]);
265 $this->assertEquals(['Student', 'Staff'], $contact['values'][$cid]['contact_sub_type']);
267 $this->callAPISuccess('Contact', 'create', [
269 'contact_sub_type' => [],
272 $contact = $this->callAPISuccess('contact', 'get', ['id' => $cid]);
273 $this->assertTrue(empty($contact['values'][$cid]['contact_sub_type']));
277 * Verify that we can retreive contacts of different sub types
279 * @param int $version
281 * @throws \CRM_Core_Exception
282 * @throws \CiviCRM_API3_Exception
284 * @dataProvider versionThreeAndFour
286 public function testGetMultipleContactSubTypes($version) {
287 $this->_apiversion
= $version;
289 // This test presumes that there are no parents or students in the dataset
292 $student = $this->callAPISuccess('contact', 'create', [
293 'email' => 'student@example.com',
294 'contact_type' => 'Individual',
295 'contact_sub_type' => 'Student',
299 $parent = $this->callAPISuccess('contact', 'create', [
300 'email' => 'parent@example.com',
301 'contact_type' => 'Individual',
302 'contact_sub_type' => 'Parent',
306 $this->callAPISuccess('contact', 'create', [
307 'email' => 'parent@example.com',
308 'contact_type' => 'Individual',
311 // get all students and parents
312 $getParams = ['contact_sub_type' => ['IN' => ['Parent', 'Student']]];
313 $result = civicrm_api3('contact', 'get', $getParams);
315 // check that we retrieved the student and the parent
316 $this->assertArrayHasKey($student['id'], $result['values']);
317 $this->assertArrayHasKey($parent['id'], $result['values']);
318 $this->assertEquals(2, $result['count']);
323 * Verify that attempt to create contact with empty params fails.
325 public function testCreateEmptyContact() {
326 $this->callAPIFailure('contact', 'create', []);
330 * Verify that attempt to create contact with bad contact type fails.
332 public function testCreateBadTypeContact() {
334 'email' => 'man1@yahoo.com',
335 'contact_type' => 'Does not Exist',
337 $this->callAPIFailure('contact', 'create', $params, "'Does not Exist' is not a valid option for field contact_type");
341 * Verify that attempt to create individual contact without required fields fails.
343 public function testCreateBadRequiredFieldsIndividual() {
345 'middle_name' => 'This field is not required',
346 'contact_type' => 'Individual',
348 $this->callAPIFailure('contact', 'create', $params);
352 * Verify that attempt to create household contact without required fields fails.
354 public function testCreateBadRequiredFieldsHousehold() {
356 'middle_name' => 'This field is not required',
357 'contact_type' => 'Household',
359 $this->callAPIFailure('contact', 'create', $params);
363 * Test required field check.
365 * Verify that attempt to create organization contact without required fields fails.
367 public function testCreateBadRequiredFieldsOrganization() {
369 'middle_name' => 'This field is not required',
370 'contact_type' => 'Organization',
373 $this->callAPIFailure('contact', 'create', $params);
377 * Verify that attempt to create individual contact with only an email succeeds.
379 * @throws \CRM_Core_Exception
381 public function testCreateEmailIndividual() {
382 $primaryEmail = 'man3@yahoo.com';
383 $notPrimaryEmail = 'man4@yahoo.com';
385 'email' => $primaryEmail,
386 'contact_type' => 'Individual',
387 'location_type_id' => 1,
390 $contact1 = $this->callAPISuccess('contact', 'create', $params);
392 $this->assertEquals(3, $contact1['id']);
393 $email1 = $this->callAPISuccess('email', 'get', ['contact_id' => $contact1['id']]);
394 $this->assertEquals(1, $email1['count']);
395 $this->assertEquals($primaryEmail, $email1['values'][$email1['id']]['email']);
397 $this->callAPISuccess('email', 'create', ['contact_id' => $contact1['id'], 'is_primary' => 0, 'email' => $notPrimaryEmail]);
399 // Case 1: Check with criteria primary 'email' => array('IS NOT NULL' => 1)
400 $result = $this->callAPISuccess('contact', 'get', ['email' => ['IS NOT NULL' => 1]]);
401 $this->assertEquals($primaryEmail, $email1['values'][$email1['id']]['email']);
403 // Case 2: Check with criteria primary 'email' => array('<>' => '')
404 $result = $this->callAPISuccess('contact', 'get', ['email' => ['<>' => '']]);
405 $this->assertEquals($primaryEmail, $email1['values'][$email1['id']]['email']);
407 // Case 3: Check with email_id='primary email id'
408 $result = $this->callAPISuccessGetSingle('contact', ['email_id' => $email1['id']]);
409 $this->assertEquals($contact1['id'], $result['id']);
411 // Check no wildcard is appended
412 $this->callAPISuccessGetCount('Contact', ['email' => 'man3@yahoo.co'], 0);
414 $this->callAPISuccess('contact', 'delete', $contact1);
418 * Test creating individual by name.
420 * Verify create individual contact with only first and last names succeeds.
422 * @param int $version
424 * @dataProvider versionThreeAndFour
425 * @throws \CRM_Core_Exception
427 public function testCreateNameIndividual($version) {
428 $this->_apiversion
= $version;
430 'first_name' => 'abc1',
431 'contact_type' => 'Individual',
432 'last_name' => 'xyz1',
435 $this->callAPISuccess('contact', 'create', $params);
439 * Test creating individual by display_name.
441 * Display name & sort name should be set.
443 * @param int $version
445 * @dataProvider versionThreeAndFour
446 * @throws \CRM_Core_Exception
448 public function testCreateDisplayNameIndividual($version) {
449 $this->_apiversion
= $version;
451 'display_name' => 'abc1',
452 'contact_type' => 'Individual',
455 $contact = $this->callAPISuccess('contact', 'create', $params);
456 $params['sort_name'] = 'abc1';
457 $this->getAndCheck($params, $contact['id'], 'contact');
461 * Test that name searches are case insensitive.
463 * @param int $version
465 * @dataProvider versionThreeAndFour
466 * @throws \CRM_Core_Exception
468 public function testGetNameVariantsCaseInsensitive($version) {
469 $this->_apiversion
= $version;
470 $this->callAPISuccess('contact', 'create', [
471 'display_name' => 'Abc1',
472 'contact_type' => 'Individual',
474 $this->callAPISuccessGetSingle('Contact', ['display_name' => 'aBc1']);
475 $this->callAPISuccessGetSingle('Contact', ['sort_name' => 'aBc1']);
476 Civi
::settings()->set('includeNickNameInName', TRUE);
477 $this->callAPISuccessGetSingle('Contact', ['display_name' => 'aBc1']);
478 $this->callAPISuccessGetSingle('Contact', ['sort_name' => 'aBc1']);
479 Civi
::settings()->set('includeNickNameInName', FALSE);
483 * Test old keys still work.
485 * Verify that attempt to create individual contact with
486 * first and last names and old key values works
488 * @throws \CRM_Core_Exception
490 public function testCreateNameIndividualOldKeys() {
492 'individual_prefix' => 'Dr.',
493 'first_name' => 'abc1',
494 'contact_type' => 'Individual',
495 'last_name' => 'xyz1',
496 'individual_suffix' => 'Jr.',
499 $contact = $this->callAPISuccess('contact', 'create', $params);
500 $result = $this->callAPISuccess('contact', 'getsingle', ['id' => $contact['id']]);
502 $this->assertArrayKeyExists('prefix_id', $result);
503 $this->assertArrayKeyExists('suffix_id', $result);
504 $this->assertArrayKeyExists('gender_id', $result);
505 $this->assertEquals(4, $result['prefix_id']);
506 $this->assertEquals(1, $result['suffix_id']);
510 * Test preferred keys work.
512 * Verify that attempt to create individual contact with
513 * first and last names and old key values works
515 public function testCreateNameIndividualRecommendedKeys2() {
517 'prefix_id' => 'Dr.',
518 'first_name' => 'abc1',
519 'contact_type' => 'Individual',
520 'last_name' => 'xyz1',
521 'suffix_id' => 'Jr.',
522 'gender_id' => 'Male',
525 $contact = $this->callAPISuccess('contact', 'create', $params);
526 $result = $this->callAPISuccess('contact', 'getsingle', ['id' => $contact['id']]);
528 $this->assertArrayKeyExists('prefix_id', $result);
529 $this->assertArrayKeyExists('suffix_id', $result);
530 $this->assertArrayKeyExists('gender_id', $result);
531 $this->assertEquals(4, $result['prefix_id']);
532 $this->assertEquals(1, $result['suffix_id']);
536 * Test household name is sufficient for create.
538 * Verify that attempt to create household contact with only
539 * household name succeeds
541 * @param int $version
543 * @dataProvider versionThreeAndFour
544 * @throws \CRM_Core_Exception
546 public function testCreateNameHousehold($version) {
547 $this->_apiversion
= $version;
549 'household_name' => 'The abc Household',
550 'contact_type' => 'Household',
552 $this->callAPISuccess('contact', 'create', $params);
556 * Test organization name is sufficient for create.
558 * Verify that attempt to create organization contact with only
559 * organization name succeeds.
561 * @param int $version
563 * @dataProvider versionThreeAndFour
565 public function testCreateNameOrganization($version) {
566 $this->_apiversion
= $version;
568 'organization_name' => 'The abc Organization',
569 'contact_type' => 'Organization',
571 $this->callAPISuccess('contact', 'create', $params);
575 * Verify that attempt to create organization contact without organization name fails.
577 public function testCreateNoNameOrganization() {
579 'first_name' => 'The abc Organization',
580 'contact_type' => 'Organization',
582 $this->callAPIFailure('contact', 'create', $params);
586 * Check that permissions on API key are restricted (CRM-18112).
588 * @param int $version
590 * @dataProvider versionThreeAndFour
592 public function testCreateApiKey($version) {
593 $this->_apiversion
= $version;
594 $config = CRM_Core_Config
::singleton();
595 $contactId = $this->individualCreate([
600 // Allow edit -- because permissions aren't being checked
601 $config->userPermissionClass
->permissions
= [];
602 $result = $this->callAPISuccess('Contact', 'create', [
604 'api_key' => 'original',
606 $this->assertEquals('original', $result['values'][$contactId]['api_key']);
608 // Allow edit -- because we have adequate permission
609 $config->userPermissionClass
->permissions
= ['access CiviCRM', 'edit all contacts', 'edit api keys'];
610 $result = $this->callAPISuccess('Contact', 'create', [
611 'check_permissions' => 1,
613 'api_key' => 'abcd1234',
615 $this->assertEquals('abcd1234', $result['values'][$contactId]['api_key']);
617 // Disallow edit -- because we don't have permission
618 $config->userPermissionClass
->permissions
= ['access CiviCRM', 'edit all contacts'];
619 $result = $this->callAPIFailure('Contact', 'create', [
620 'check_permissions' => 1,
622 'api_key' => 'defg4321',
624 $this->assertRegExp(';Permission denied to modify api key;', $result['error_message']);
626 // Return everything -- because permissions are not being checked
627 $config->userPermissionClass
->permissions
= [];
628 $result = $this->callAPISuccess('Contact', 'create', [
630 'first_name' => 'A2',
632 $this->assertEquals('A2', $result['values'][$contactId]['first_name']);
633 $this->assertEquals('B', $result['values'][$contactId]['last_name']);
634 $this->assertEquals('abcd1234', $result['values'][$contactId]['api_key']);
636 // Return everything -- because we have adequate permission
637 $config->userPermissionClass
->permissions
= ['access CiviCRM', 'edit all contacts', 'edit api keys'];
638 $result = $this->callAPISuccess('Contact', 'create', [
639 'check_permissions' => 1,
641 'first_name' => 'A3',
643 $this->assertEquals('A3', $result['values'][$contactId]['first_name']);
644 $this->assertEquals('B', $result['values'][$contactId]['last_name']);
645 $this->assertEquals('abcd1234', $result['values'][$contactId]['api_key']);
647 // Should also be returned via join
648 $joinResult = $this->callAPISuccessGetSingle('Email', [
649 'check_permissions' => 1,
650 'contact_id' => $contactId,
651 'return' => 'contact_id.api_key',
653 $field = $this->_apiversion
== 4 ?
'contact.api_key' : 'contact_id.api_key';
654 $this->assertEquals('abcd1234', $joinResult[$field]);
656 // Restricted return -- because we don't have permission
657 $config->userPermissionClass
->permissions
= ['access CiviCRM', 'view all contacts', 'edit all contacts'];
658 $result = $this->callAPISuccess('Contact', 'create', [
659 'check_permissions' => 1,
661 'first_name' => 'A4',
663 $this->assertEquals('A4', $result['values'][$contactId]['first_name']);
664 $this->assertEquals('B', $result['values'][$contactId]['last_name']);
665 $this->assertTrue(empty($result['values'][$contactId]['api_key']));
667 // Should also be restricted via join
668 $joinResult = $this->callAPISuccessGetSingle('Email', [
669 'check_permissions' => 1,
670 'contact_id' => $contactId,
671 'return' => ['email', 'contact_id.api_key'],
673 $this->assertTrue(empty($joinResult['contact_id.api_key']));
677 * Check with complete array + custom field.
679 * Note that the test is written on purpose without any
680 * variables specific to participant so it can be replicated into other entities
681 * and / or moved to the automated test suite
683 * @param int $version
685 * @dataProvider versionThreeAndFour
687 public function testCreateWithCustom($version) {
688 $this->_apiversion
= $version;
689 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
691 $params = $this->_params
;
692 $params['custom_' . $ids['custom_field_id']] = "custom string";
693 $description = "This demonstrates setting a custom field through the API.";
694 $result = $this->callAPIAndDocument($this->_entity
, 'create', $params, __FUNCTION__
, __FILE__
, $description);
696 $check = $this->callAPISuccess($this->_entity
, 'get', [
697 'return.custom_' . $ids['custom_field_id'] => 1,
698 'id' => $result['id'],
700 $this->assertEquals("custom string", $check['values'][$check['id']]['custom_' . $ids['custom_field_id']]);
702 $this->customFieldDelete($ids['custom_field_id']);
703 $this->customGroupDelete($ids['custom_group_id']);
707 * CRM-12773 - expectation is that civicrm quietly ignores fields without values.
709 public function testCreateWithNULLCustomCRM12773() {
710 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
711 $params = $this->_params
;
712 $params['custom_' . $ids['custom_field_id']] = NULL;
713 $this->callAPISuccess('contact', 'create', $params);
714 $this->customFieldDelete($ids['custom_field_id']);
715 $this->customGroupDelete($ids['custom_group_id']);
719 * CRM-14232 test preferred language set to site default if not passed.
721 * @param int $version
723 * @dataProvider versionThreeAndFour
725 public function testCreatePreferredLanguageUnset($version) {
726 $this->_apiversion
= $version;
727 $this->callAPISuccess('Contact', 'create', [
728 'first_name' => 'Snoop',
729 'last_name' => 'Dog',
730 'contact_type' => 'Individual',
732 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Dog']);
733 $this->assertEquals('en_US', $result['preferred_language']);
737 * CRM-14232 test preferred language returns setting if not passed.
739 * @param int $version
741 * @dataProvider versionThreeAndFour
743 public function testCreatePreferredLanguageSet($version) {
744 $this->_apiversion
= $version;
745 $this->callAPISuccess('Setting', 'create', ['contact_default_language' => 'fr_FR']);
746 $this->callAPISuccess('Contact', 'create', [
747 'first_name' => 'Snoop',
748 'last_name' => 'Dog',
749 'contact_type' => 'Individual',
751 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Dog']);
752 $this->assertEquals('fr_FR', $result['preferred_language']);
756 * CRM-14232 test preferred language returns setting if not passed where setting is NULL.
759 public function testCreatePreferredLanguageNull() {
760 $this->callAPISuccess('Setting', 'create', ['contact_default_language' => 'null']);
761 $this->callAPISuccess('Contact', 'create', [
762 'first_name' => 'Snoop',
763 'last_name' => 'Dog',
764 'contact_type' => 'Individual',
766 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Dog']);
767 $this->assertEquals(NULL, $result['preferred_language']);
771 * CRM-14232 test preferred language returns setting if not passed where setting is NULL.
773 * @param int $version
775 * @dataProvider versionThreeAndFour
777 public function testCreatePreferredLanguagePassed($version) {
778 $this->_apiversion
= $version;
779 $this->callAPISuccess('Setting', 'create', ['contact_default_language' => 'null']);
780 $this->callAPISuccess('Contact', 'create', [
781 'first_name' => 'Snoop',
782 'last_name' => 'Dog',
783 'contact_type' => 'Individual',
784 'preferred_language' => 'en_AU',
786 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Dog']);
787 $this->assertEquals('en_AU', $result['preferred_language']);
791 * CRM-15792 - create/update datetime field for contact.
793 public function testCreateContactCustomFldDateTime() {
794 $customGroup = $this->customGroupCreate(['extends' => 'Individual', 'title' => 'datetime_test_group']);
795 $dateTime = CRM_Utils_Date
::currentDBDate();
796 //check date custom field is saved along with time when time_format is set
798 'first_name' => 'abc3',
799 'last_name' => 'xyz3',
800 'contact_type' => 'Individual',
801 'email' => 'man3@yahoo.com',
802 'api.CustomField.create' => [
803 'custom_group_id' => $customGroup['id'],
804 'name' => 'test_datetime',
805 'label' => 'Demo Date',
806 'html_type' => 'Select Date',
807 'data_type' => 'Date',
811 'is_searchable' => 0,
816 $result = $this->callAPISuccess('Contact', 'create', $params);
817 $customFldId = $result['values'][$result['id']]['api.CustomField.create']['id'];
818 $this->assertNotNull($result['id']);
819 $this->assertNotNull($customFldId);
822 'id' => $result['id'],
823 "custom_{$customFldId}" => $dateTime,
824 'api.CustomValue.get' => 1,
827 $result = $this->callAPISuccess('Contact', 'create', $params);
828 $this->assertNotNull($result['id']);
829 $customFldDate = date("YmdHis", strtotime($result['values'][$result['id']]['api.CustomValue.get']['values'][0]['latest']));
830 $this->assertNotNull($customFldDate);
831 $this->assertEquals($dateTime, $customFldDate);
832 $customValueId = $result['values'][$result['id']]['api.CustomValue.get']['values'][0]['id'];
833 $dateTime = date('Ymd');
834 //date custom field should not contain time part when time_format is null
836 'id' => $result['id'],
837 'api.CustomField.create' => [
838 'id' => $customFldId,
839 'html_type' => 'Select Date',
840 'data_type' => 'Date',
843 'api.CustomValue.create' => [
844 'id' => $customValueId,
845 'entity_id' => $result['id'],
846 "custom_{$customFldId}" => $dateTime,
848 'api.CustomValue.get' => 1,
850 $result = $this->callAPISuccess('Contact', 'create', $params);
851 $this->assertNotNull($result['id']);
852 $customFldDate = date("Ymd", strtotime($result['values'][$result['id']]['api.CustomValue.get']['values'][0]['latest']));
853 $customFldTime = date("His", strtotime($result['values'][$result['id']]['api.CustomValue.get']['values'][0]['latest']));
854 $this->assertNotNull($customFldDate);
855 $this->assertEquals($dateTime, $customFldDate);
856 $this->assertEquals(000000, $customFldTime);
857 $this->callAPISuccess('Contact', 'create', $params);
861 * Test creating a current employer through API.
863 public function testContactCreateCurrentEmployer() {
864 // Here we will just do the get for set-up purposes.
865 $count = $this->callAPISuccess('contact', 'getcount', [
866 'organization_name' => 'new employer org',
867 'contact_type' => 'Organization',
869 $this->assertEquals(0, $count);
870 $employerResult = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, [
871 'current_employer' => 'new employer org',
873 // do it again as an update to check it doesn't cause an error
874 $employerResult = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, [
875 'current_employer' => 'new employer org',
876 'id' => $employerResult['id'],
879 $this->callAPISuccess('contact', 'getcount', [
880 'organization_name' => 'new employer org',
881 'contact_type' => 'Organization',
884 $result = $this->callAPISuccess('contact', 'getsingle', [
885 'id' => $employerResult['id'],
888 $this->assertEquals('new employer org', $result['current_employer']);
893 * Test creating a current employer through API.
895 * Check it will re-activate a de-activated employer
897 public function testContactCreateDuplicateCurrentEmployerEnables() {
898 // Set up - create employer relationship.
899 $employerResult = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, ['current_employer' => 'new employer org']));
900 $relationship = $this->callAPISuccess('relationship', 'get', [
901 'contact_id_a' => $employerResult['id'],
904 //disable & check it is disabled
905 $this->callAPISuccess('relationship', 'create', ['id' => $relationship['id'], 'is_active' => 0]);
906 $this->callAPISuccess('relationship', 'getvalue', [
907 'id' => $relationship['id'],
908 'return' => 'is_active',
911 // Re-set the current employer - thus enabling the relationship.
912 $this->callAPISuccess('contact', 'create', array_merge($this->_params
, [
913 'current_employer' => 'new employer org',
914 'id' => $employerResult['id'],
916 //check is_active is now 1
917 $relationship = $this->callAPISuccess('relationship', 'getsingle', ['return' => 'is_active']);
918 $this->assertEquals(1, $relationship['is_active']);
922 * Check deceased contacts are not retrieved.
924 * Note at time of writing the default is to return default. This should possibly be changed & test added.
926 * @param int $version
928 * @dataProvider versionThreeAndFour
930 public function testGetDeceasedRetrieved($version) {
931 $this->_apiversion
= $version;
932 $this->callAPISuccess($this->_entity
, 'create', $this->_params
);
933 $c2 = $this->callAPISuccess($this->_entity
, 'create', [
934 'first_name' => 'bb',
935 'last_name' => 'ccc',
936 'contact_type' => 'Individual',
939 $result = $this->callAPISuccess($this->_entity
, 'get', ['is_deceased' => 0]);
940 $this->assertFalse(array_key_exists($c2['id'], $result['values']));
944 * Test that sort works - old syntax.
946 public function testGetSort() {
947 $c1 = $this->callAPISuccess($this->_entity
, 'create', $this->_params
);
948 $c2 = $this->callAPISuccess($this->_entity
, 'create', [
949 'first_name' => 'bb',
950 'last_name' => 'ccc',
951 'contact_type' => 'Individual',
953 $result = $this->callAPISuccess($this->_entity
, 'get', [
954 'sort' => 'first_name ASC',
955 'return.first_name' => 1,
958 'contact_type' => 'Individual',
961 $this->assertEquals('abc1', $result['values'][0]['first_name']);
962 $result = $this->callAPISuccess($this->_entity
, 'get', [
963 'sort' => 'first_name DESC',
964 'return.first_name' => 1,
968 $this->assertEquals('bb', $result['values'][0]['first_name']);
970 $this->callAPISuccess($this->_entity
, 'delete', ['id' => $c1['id']]);
971 $this->callAPISuccess($this->_entity
, 'delete', ['id' => $c2['id']]);
975 * Test the like operator works for Contact.get
977 * @throws \CRM_Core_Exception
979 public function testGetEmailLike() {
980 $this->individualCreate();
981 $this->callAPISuccessGetCount('Contact', ['email' => ['LIKE' => 'an%']], 1);
982 $this->callAPISuccessGetCount('Contact', ['email' => ['LIKE' => 'ab%']], 0);
986 * Test that we can retrieve contacts using array syntax.
988 * I.e 'id' => array('IN' => array('3,4')).
990 * @param int $version
992 * @dataProvider versionThreeAndFour
994 public function testGetINIDArray($version) {
995 $this->_apiversion
= $version;
996 $c1 = $this->callAPISuccess($this->_entity
, 'create', $this->_params
);
997 $c2 = $this->callAPISuccess($this->_entity
, 'create', [
998 'first_name' => 'bb',
999 'last_name' => 'ccc',
1000 'contact_type' => 'Individual',
1002 $c3 = $this->callAPISuccess($this->_entity
, 'create', [
1003 'first_name' => 'hh',
1004 'last_name' => 'll',
1005 'contact_type' => 'Individual',
1007 $result = $this->callAPISuccess($this->_entity
, 'get', ['id' => ['IN' => [$c1['id'], $c3['id']]]]);
1008 $this->assertEquals(2, $result['count']);
1009 $this->assertEquals([$c1['id'], $c3['id']], array_keys($result['values']));
1010 $this->callAPISuccess($this->_entity
, 'delete', ['id' => $c1['id']]);
1011 $this->callAPISuccess($this->_entity
, 'delete', ['id' => $c2['id']]);
1012 $this->callAPISuccess($this->_entity
, 'delete', ['id' => $c3['id']]);
1016 * Test variants on deleted behaviour.
1018 public function testGetDeleted() {
1019 $params = $this->_params
;
1020 $contact1 = $this->callAPISuccess('contact', 'create', $params);
1021 $params['is_deleted'] = 1;
1022 $params['last_name'] = 'bcd';
1023 $contact2 = $this->callAPISuccess('contact', 'create', $params);
1024 $countActive = $this->callAPISuccess('contact', 'getcount', [
1025 'showAll' => 'active',
1026 'contact_type' => 'Individual',
1028 $countAll = $this->callAPISuccess('contact', 'getcount', ['showAll' => 'all', 'contact_type' => 'Individual']);
1029 $countTrash = $this->callAPISuccess('contact', 'getcount', ['showAll' => 'trash', 'contact_type' => 'Individual']);
1030 $countDefault = $this->callAPISuccess('contact', 'getcount', ['contact_type' => 'Individual']);
1031 $countDeleted = $this->callAPISuccess('contact', 'getcount', [
1032 'contact_type' => 'Individual',
1033 'contact_is_deleted' => 1,
1035 $countNotDeleted = $this->callAPISuccess('contact', 'getcount', [
1036 'contact_is_deleted' => 0,
1037 'contact_type' => 'Individual',
1039 $this->callAPISuccess('contact', 'delete', ['id' => $contact1['id']]);
1040 $this->callAPISuccess('contact', 'delete', ['id' => $contact2['id']]);
1041 $this->assertEquals(1, $countNotDeleted, 'contact_is_deleted => 0 is respected');
1042 $this->assertEquals(1, $countActive);
1043 $this->assertEquals(1, $countTrash);
1044 $this->assertEquals(2, $countAll);
1045 $this->assertEquals(1, $countDeleted);
1046 $this->assertEquals(1, $countDefault, 'Only active by default in line');
1050 * Test that sort works - new syntax.
1052 * @param int $version
1054 * @dataProvider versionThreeAndFour
1056 public function testGetSortNewSyntax($version) {
1057 $this->_apiversion
= $version;
1058 $c1 = $this->callAPISuccess($this->_entity
, 'create', $this->_params
);
1059 $c2 = $this->callAPISuccess($this->_entity
, 'create', [
1060 'first_name' => 'bb',
1061 'last_name' => 'ccc',
1062 'contact_type' => 'Individual',
1064 $result = $this->callAPISuccess($this->_entity
, 'getvalue', [
1065 'return' => 'first_name',
1066 'contact_type' => 'Individual',
1069 'sort' => 'first_name',
1072 $this->assertEquals('abc1', $result);
1074 $result = $this->callAPISuccess($this->_entity
, 'getvalue', [
1075 'return' => 'first_name',
1076 'contact_type' => 'Individual',
1079 'sort' => 'first_name DESC',
1082 $this->assertEquals('bb', $result);
1084 $this->callAPISuccess($this->_entity
, 'delete', ['id' => $c1['id']]);
1085 $this->callAPISuccess($this->_entity
, 'delete', ['id' => $c2['id']]);
1089 * Test sort and limit for chained relationship get.
1091 * https://issues.civicrm.org/jira/browse/CRM-15983
1092 * @param int $version
1094 * @dataProvider versionThreeAndFour
1096 public function testSortLimitChainedRelationshipGetCRM15983($version) {
1097 $this->_apiversion
= $version;
1099 $create_result_1 = $this->callAPISuccess('contact', 'create', [
1100 'first_name' => 'Jules',
1101 'last_name' => 'Smos',
1102 'contact_type' => 'Individual',
1105 // Create another contact with two relationships.
1107 'first_name' => 'Jos',
1108 'last_name' => 'Smos',
1109 'contact_type' => 'Individual',
1110 'api.relationship.create' => [
1112 'contact_id_a' => '$value.id',
1113 'contact_id_b' => $create_result_1['id'],
1115 'relationship_type_id' => 2,
1116 'start_date' => '2005-01-12',
1117 'end_date' => '2006-01-11',
1118 'description' => 'old',
1121 'contact_id_a' => '$value.id',
1122 'contact_id_b' => $create_result_1['id'],
1123 // spouse of (was married twice :))
1124 'relationship_type_id' => 2,
1125 'start_date' => '2006-07-01',
1126 'end_date' => '2010-07-01',
1127 'description' => 'new',
1131 $create_result = $this->callAPISuccess('contact', 'create', $create_params);
1133 // Try to retrieve the contact and the most recent relationship.
1136 'id' => $create_result['id'],
1137 'api.relationship.get' => [
1138 'contact_id_a' => '$value.id',
1141 'sort' => 'start_date DESC',
1145 $get_result = $this->callAPISuccess('contact', 'getsingle', $get_params);
1148 $this->callAPISuccess('contact', 'delete', [
1149 'id' => $create_result['id'],
1153 $this->assertEquals(1, $get_result['api.relationship.get']['count']);
1154 $this->assertEquals('new', $get_result['api.relationship.get']['values'][0]['description']);
1158 * Test apostrophe works in get & create.
1160 * @param int $version
1162 * @dataProvider versionThreeAndFour
1164 public function testGetApostropheCRM10857($version) {
1165 $this->_apiversion
= $version;
1166 $params = array_merge($this->_params
, ['last_name' => "O'Connor"]);
1167 $this->callAPISuccess($this->_entity
, 'create', $params);
1168 $result = $this->callAPISuccess($this->_entity
, 'getsingle', [
1169 'last_name' => "O'Connor",
1172 $this->assertEquals("O'Connor", $result['last_name']);
1176 * Test between accepts zero.
1178 * In the past it incorrectly required !empty.
1180 * @param int $version
1182 * @dataProvider versionThreeAndFour
1184 public function testGetBetweenZeroWorks($version) {
1185 $this->_apiversion
= $version;
1186 $this->callAPISuccess($this->_entity
, 'get', [
1187 'contact_id' => ['BETWEEN' => [0, 9]],
1189 $this->callAPISuccess($this->_entity
, 'get', [
1200 * Test retrieval by addressee id.
1201 * V3 only - the "skip_greeting_processing" param is not currently in v4
1203 public function testGetByAddresseeID() {
1204 $individual1ID = $this->individualCreate([
1205 'skip_greeting_processing' => 1,
1206 'addressee_id' => 'null',
1207 'email_greeting_id' => 'null',
1208 'postal_greeting_id' => 'null',
1210 $individual2ID = $this->individualCreate();
1212 $this->assertEquals($individual1ID,
1213 $this->callAPISuccessGetValue('Contact', ['contact_type' => 'Individual', 'addressee_id' => ['IS NULL' => 1], 'return' => 'id'])
1215 $this->assertEquals($individual1ID,
1216 $this->callAPISuccessGetValue('Contact', ['contact_type' => 'Individual', 'email_greeting_id' => ['IS NULL' => 1], 'return' => 'id'])
1218 $this->assertEquals($individual1ID,
1219 $this->callAPISuccessGetValue('Contact', ['contact_type' => 'Individual', 'postal_greeting_id' => ['IS NULL' => 1], 'return' => 'id'])
1222 $this->assertEquals($individual2ID,
1223 $this->callAPISuccessGetValue('Contact', ['contact_type' => 'Individual', 'addressee_id' => ['NOT NULL' => 1], 'return' => 'id'])
1228 * Check with complete array + custom field.
1230 * Note that the test is written on purpose without any
1231 * variables specific to participant so it can be replicated into other entities
1232 * and / or moved to the automated test suite
1234 public function testGetWithCustom() {
1235 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
1237 $params = $this->_params
;
1238 $params['custom_' . $ids['custom_field_id']] = "custom string";
1239 $description = "This demonstrates setting a custom field through the API.";
1240 $subfile = "CustomFieldGet";
1241 $result = $this->callAPISuccess($this->_entity
, 'create', $params);
1243 $check = $this->callAPIAndDocument($this->_entity
, 'get', [
1244 'return.custom_' . $ids['custom_field_id'] => 1,
1245 'id' => $result['id'],
1246 ], __FUNCTION__
, __FILE__
, $description, $subfile);
1248 $this->assertEquals("custom string", $check['values'][$check['id']]['custom_' . $ids['custom_field_id']]);
1249 $fields = ($this->callAPISuccess('contact', 'getfields', $params));
1250 $this->assertTrue(is_array($fields['values']['custom_' . $ids['custom_field_id']]));
1251 $this->customFieldDelete($ids['custom_field_id']);
1252 $this->customGroupDelete($ids['custom_group_id']);
1256 * Tests that using 'return' with a custom field not of type contact does not inappropriately filter.
1258 * https://lab.civicrm.org/dev/core/issues/1025
1260 * @throws \CRM_Core_Exception
1262 public function testGetWithCustomOfActivityType() {
1263 $this->createCustomGroupWithFieldOfType(['extends' => 'Activity']);
1264 $this->createCustomGroupWithFieldOfType(['extends' => 'Contact'], 'text', 'contact_');
1265 $contactID = $this->individualCreate();
1266 $this->callAPISuccessGetSingle('Contact', ['id' => $contactID, 'return' => ['external_identifier', $this->getCustomFieldName('contact_text')]]);
1270 * Check with complete array + custom field.
1272 * Note that the test is written on purpose without any
1273 * variables specific to participant so it can be replicated into other entities
1274 * and / or moved to the automated test suite
1276 public function testGetWithCustomReturnSyntax() {
1277 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
1279 $params = $this->_params
;
1280 $params['custom_' . $ids['custom_field_id']] = "custom string";
1281 $description = "This demonstrates setting a custom field through the API.";
1282 $subfile = "CustomFieldGetReturnSyntaxVariation";
1283 $result = $this->callAPISuccess($this->_entity
, 'create', $params);
1284 $params = ['return' => 'custom_' . $ids['custom_field_id'], 'id' => $result['id']];
1285 $check = $this->callAPIAndDocument($this->_entity
, 'get', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
1287 $this->assertEquals("custom string", $check['values'][$check['id']]['custom_' . $ids['custom_field_id']]);
1288 $this->customFieldDelete($ids['custom_field_id']);
1289 $this->customGroupDelete($ids['custom_group_id']);
1293 * Check that address name, ID is returned if required.
1295 public function testGetReturnAddress() {
1296 $contactID = $this->individualCreate();
1297 $result = $this->callAPISuccess('address', 'create', [
1298 'contact_id' => $contactID,
1299 'address_name' => 'My house',
1300 'location_type_id' => 'Home',
1301 'street_address' => '1 my road',
1303 $addressID = $result['id'];
1305 $result = $this->callAPISuccessGetSingle('contact', [
1306 'return' => 'address_name, street_address, address_id',
1309 $this->assertEquals($addressID, $result['address_id']);
1310 $this->assertEquals('1 my road', $result['street_address']);
1311 $this->assertEquals('My house', $result['address_name']);
1316 * Test group filter syntaxes.
1318 public function testGetGroupIDFromContact() {
1319 $groupId = $this->groupCreate();
1321 'email' => 'man2@yahoo.com',
1322 'contact_type' => 'Individual',
1323 'location_type_id' => 1,
1324 'api.group_contact.create' => ['group_id' => $groupId],
1327 $this->callAPISuccess('contact', 'create', $params);
1328 // testing as integer
1330 'filter.group_id' => $groupId,
1331 'contact_type' => 'Individual',
1333 $result = $this->callAPISuccess('contact', 'get', $params);
1334 $this->assertEquals(1, $result['count']);
1335 // group 26 doesn't exist, but we can still search contacts in it.
1337 'filter.group_id' => 26,
1338 'contact_type' => 'Individual',
1340 $this->callAPISuccess('contact', 'get', $params);
1341 // testing as string
1343 'filter.group_id' => "$groupId, 26",
1344 'contact_type' => 'Individual',
1346 $result = $this->callAPISuccess('contact', 'get', $params);
1347 $this->assertEquals(1, $result['count']);
1349 'filter.group_id' => "26,27",
1350 'contact_type' => 'Individual',
1352 $this->callAPISuccess('contact', 'get', $params);
1354 // testing as string
1356 'filter.group_id' => [$groupId, 26],
1357 'contact_type' => 'Individual',
1359 $result = $this->callAPISuccess('contact', 'get', $params);
1360 $this->assertEquals(1, $result['count']);
1362 //test in conjunction with other criteria
1364 'filter.group_id' => [$groupId, 26],
1365 'contact_type' => 'Organization',
1367 $this->callAPISuccess('contact', 'get', $params);
1369 'filter.group_id' => [26, 27],
1370 'contact_type' => 'Individual',
1372 $result = $this->callAPISuccess('contact', 'get', $params);
1373 $this->assertEquals(0, $result['count']);
1377 * Verify that attempt to create individual contact with two chained websites succeeds.
1379 public function testCreateIndividualWithContributionDottedSyntax() {
1380 $description = "This demonstrates the syntax to create 2 chained entities.";
1381 $subFile = "ChainTwoWebsites";
1383 'first_name' => 'abc3',
1384 'last_name' => 'xyz3',
1385 'contact_type' => 'Individual',
1386 'email' => 'man3@yahoo.com',
1387 'api.contribution.create' => [
1388 'receive_date' => '2010-01-01',
1389 'total_amount' => 100.00,
1390 'financial_type_id' => $this->_financialTypeId
,
1391 'payment_instrument_id' => 1,
1392 'non_deductible_amount' => 10.00,
1393 'fee_amount' => 50.00,
1394 'net_amount' => 90.00,
1396 'invoice_id' => 67990,
1398 'contribution_status_id' => 1,
1399 'skipCleanMoney' => 1,
1401 'api.website.create' => [
1402 'url' => "http://civicrm.org",
1404 'api.website.create.2' => [
1405 'url' => "http://chained.org",
1409 $result = $this->callAPIAndDocument('Contact', 'create', $params, __FUNCTION__
, __FILE__
, $description, $subFile);
1411 // checking child function result not covered in callAPIAndDocument
1412 $this->assertAPISuccess($result['values'][$result['id']]['api.website.create']);
1413 $this->assertEquals("http://chained.org", $result['values'][$result['id']]['api.website.create.2']['values'][0]['url']);
1414 $this->assertEquals("http://civicrm.org", $result['values'][$result['id']]['api.website.create']['values'][0]['url']);
1416 // delete the contact
1417 $this->callAPISuccess('contact', 'delete', $result);
1421 * Verify that attempt to create individual contact with chained contribution and website succeeds.
1423 public function testCreateIndividualWithContributionChainedArrays() {
1425 'first_name' => 'abc3',
1426 'last_name' => 'xyz3',
1427 'contact_type' => 'Individual',
1428 'email' => 'man3@yahoo.com',
1429 'api.contribution.create' => [
1430 'receive_date' => '2010-01-01',
1431 'total_amount' => 100.00,
1432 'financial_type_id' => $this->_financialTypeId
,
1433 'payment_instrument_id' => 1,
1434 'non_deductible_amount' => 10.00,
1435 'fee_amount' => 50.00,
1436 'net_amount' => 90.00,
1438 'invoice_id' => 67890,
1440 'contribution_status_id' => 1,
1441 'skipCleanMoney' => 1,
1443 'api.website.create' => [
1445 'url' => "http://civicrm.org",
1448 'url' => "http://chained.org",
1449 'website_type_id' => 2,
1454 $description = "Demonstrates creating two websites as an array.";
1455 $subfile = "ChainTwoWebsitesSyntax2";
1456 $result = $this->callAPIAndDocument('Contact', 'create', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
1458 // the callAndDocument doesn't check the chained call
1459 $this->assertEquals(0, $result['values'][$result['id']]['api.website.create'][0]['is_error']);
1460 $this->assertEquals("http://chained.org", $result['values'][$result['id']]['api.website.create'][1]['values'][0]['url']);
1461 $this->assertEquals("http://civicrm.org", $result['values'][$result['id']]['api.website.create'][0]['values'][0]['url']);
1463 $this->callAPISuccess('contact', 'delete', $result);
1467 * Test for direction when chaining relationships.
1469 * https://issues.civicrm.org/jira/browse/CRM-16084
1470 * @param int $version
1472 * @dataProvider versionThreeAndFour
1474 public function testDirectionChainingRelationshipsCRM16084($version) {
1475 $this->_apiversion
= $version;
1476 // Some contact, called Jules.
1477 $create_result_1 = $this->callAPISuccess('contact', 'create', [
1478 'first_name' => 'Jules',
1479 'last_name' => 'Smos',
1480 'contact_type' => 'Individual',
1483 // Another contact: Jos, child of Jules.
1485 'first_name' => 'Jos',
1486 'last_name' => 'Smos',
1487 'contact_type' => 'Individual',
1488 'api.relationship.create' => [
1490 'contact_id_a' => '$value.id',
1491 'contact_id_b' => $create_result_1['id'],
1493 'relationship_type_id' => 1,
1497 $create_result_2 = $this->callAPISuccess('contact', 'create', $create_params);
1499 // Mia is the child of Jos.
1501 'first_name' => 'Mia',
1502 'last_name' => 'Smos',
1503 'contact_type' => 'Individual',
1504 'api.relationship.create' => [
1506 'contact_id_a' => '$value.id',
1507 'contact_id_b' => $create_result_2['id'],
1509 'relationship_type_id' => 1,
1513 $create_result_3 = $this->callAPISuccess('contact', 'create', $create_params);
1515 // Get Jos and his children.
1518 'id' => $create_result_2['id'],
1519 'api.relationship.get' => [
1520 'contact_id_b' => '$value.id',
1521 'relationship_type_id' => 1,
1524 $get_result = $this->callAPISuccess('contact', 'getsingle', $get_params);
1527 $this->callAPISuccess('contact', 'delete', [
1528 'id' => $create_result_1['id'],
1530 $this->callAPISuccess('contact', 'delete', [
1531 'id' => $create_result_2['id'],
1535 $this->assertEquals(1, $get_result['api.relationship.get']['count']);
1536 $this->assertEquals($create_result_3['id'], $get_result['api.relationship.get']['values'][0]['contact_id_a']);
1540 * Verify that attempt to create individual contact with first, and last names and email succeeds.
1542 public function testCreateIndividualWithNameEmail() {
1544 'first_name' => 'abc3',
1545 'last_name' => 'xyz3',
1546 'contact_type' => 'Individual',
1547 'email' => 'man3@yahoo.com',
1550 $contact = $this->callAPISuccess('contact', 'create', $params);
1552 $this->callAPISuccess('contact', 'delete', $contact);
1556 * Verify that attempt to create individual contact with no data fails.
1558 public function testCreateIndividualWithOutNameEmail() {
1560 'contact_type' => 'Individual',
1562 $this->callAPIFailure('contact', 'create', $params);
1566 * Test create individual contact with first &last names, email and location type succeeds.
1568 public function testCreateIndividualWithNameEmailLocationType() {
1570 'first_name' => 'abc4',
1571 'last_name' => 'xyz4',
1572 'email' => 'man4@yahoo.com',
1573 'contact_type' => 'Individual',
1574 'location_type_id' => 1,
1576 $result = $this->callAPISuccess('contact', 'create', $params);
1578 $this->callAPISuccess('contact', 'delete', ['id' => $result['id']]);
1582 * Verify that when changing employers the old employer relationship becomes inactive.
1584 public function testCreateIndividualWithEmployer() {
1585 $employer = $this->organizationCreate();
1586 $employer2 = $this->organizationCreate();
1589 'email' => 'man4@yahoo.com',
1590 'contact_type' => 'Individual',
1591 'employer_id' => $employer,
1594 $result = $this->callAPISuccess('contact', 'create', $params);
1595 $relationships = $this->callAPISuccess('relationship', 'get', [
1596 'contact_id_a' => $result['id'],
1600 $this->assertEquals($employer, $relationships['values'][0]['contact_id_b']);
1602 // Add more random relationships to make the test more realistic
1603 foreach (['Employee of', 'Volunteer for'] as $relationshipType) {
1604 $relTypeId = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_RelationshipType', $relationshipType, 'id', 'name_a_b');
1605 $this->callAPISuccess('relationship', 'create', [
1606 'contact_id_a' => $result['id'],
1607 'contact_id_b' => $this->organizationCreate(),
1609 'relationship_type_id' => $relTypeId,
1613 // Add second employer
1614 $params['employer_id'] = $employer2;
1615 $params['id'] = $result['id'];
1616 $result = $this->callAPISuccess('contact', 'create', $params);
1618 $relationships = $this->callAPISuccess('relationship', 'get', [
1619 'contact_id_a' => $result['id'],
1624 $this->assertEquals($employer, $relationships['values'][0]['contact_id_b']);
1628 * Verify that attempt to create household contact with details succeeds.
1630 public function testCreateHouseholdDetails() {
1632 'household_name' => 'abc8\'s House',
1633 'nick_name' => 'x House',
1634 'email' => 'man8@yahoo.com',
1635 'contact_type' => 'Household',
1638 $contact = $this->callAPISuccess('contact', 'create', $params);
1640 $this->callAPISuccess('contact', 'delete', $contact);
1644 * Verify that attempt to create household contact with inadequate details fails.
1646 public function testCreateHouseholdInadequateDetails() {
1648 'nick_name' => 'x House',
1649 'email' => 'man8@yahoo.com',
1650 'contact_type' => 'Household',
1652 $this->callAPIFailure('contact', 'create', $params);
1656 * Verify successful update of individual contact.
1658 public function testUpdateIndividualWithAll() {
1659 $contactID = $this->individualCreate();
1663 'first_name' => 'abcd',
1664 'contact_type' => 'Individual',
1665 'nick_name' => 'This is nickname first',
1666 'do_not_email' => '1',
1667 'do_not_phone' => '1',
1668 'do_not_mail' => '1',
1669 'do_not_trade' => '1',
1670 'legal_identifier' => 'ABC23853ZZ2235',
1671 'external_identifier' => '1928837465',
1672 'image_URL' => 'http://some.url.com/image.jpg',
1673 'home_url' => 'http://www.example.org',
1676 $this->callAPISuccess('Contact', 'Update', $params);
1677 $getResult = $this->callAPISuccess('Contact', 'Get', $params);
1678 unset($params['contact_id']);
1679 //Todo - neither API v2 or V3 are testing for home_url - not sure if it is being set.
1680 //reducing this test partially back to api v2 level to get it through
1681 unset($params['home_url']);
1682 foreach ($params as $key => $value) {
1683 $this->assertEquals($value, $getResult['values'][$contactID][$key]);
1688 * Verify successful update of organization contact.
1690 * @throws \Exception
1692 public function testUpdateOrganizationWithAll() {
1693 $contactID = $this->organizationCreate();
1697 'organization_name' => 'WebAccess India Pvt Ltd',
1698 'legal_name' => 'WebAccess',
1699 'sic_code' => 'ABC12DEF',
1700 'contact_type' => 'Organization',
1703 $this->callAPISuccess('Contact', 'Update', $params);
1704 $this->getAndCheck($params, $contactID, 'Contact');
1708 * Test merging 2 organizations.
1710 * CRM-20421: This test make sure that inherited memberships are deleted upon merging organization.
1712 public function testMergeOrganizations() {
1713 $organizationID1 = $this->organizationCreate([], 0);
1714 $organizationID2 = $this->organizationCreate([], 1);
1715 $contact = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, [
1716 'employer_id' => $organizationID1,
1718 $contact = $contact["values"][$contact["id"]];
1720 $membershipType = $this->createEmployerOfMembership();
1721 $membershipParams = [
1722 'membership_type_id' => $membershipType["id"],
1723 'contact_id' => $organizationID1,
1724 'start_date' => "01/01/2015",
1725 'join_date' => "01/01/2010",
1726 'end_date' => "12/31/2015",
1728 $ownermembershipid = $this->contactMembershipCreate($membershipParams);
1730 $contactmembership = $this->callAPISuccess("membership", "getsingle", [
1731 "contact_id" => $contact["id"],
1734 $this->assertEquals($ownermembershipid, $contactmembership["owner_membership_id"], "Contact membership must be inherited from Organization");
1736 CRM_Dedupe_Merger
::moveAllBelongings($organizationID2, $organizationID1, [
1737 "move_rel_table_memberships" => "0",
1738 "move_rel_table_relationships" => "1",
1740 "contact_id" => $organizationID2,
1741 "contact_type" => "Organization",
1743 "other_details" => [
1744 "contact_id" => $organizationID1,
1745 "contact_type" => "Organization",
1749 $contactmembership = $this->callAPISuccess("membership", "get", [
1750 "contact_id" => $contact["id"],
1753 $this->assertEquals(0, $contactmembership["count"], "Contact membership must be deleted after merging organization without memberships.");
1757 * Test the function that determines if 2 contacts have conflicts.
1759 * @throws \CRM_Core_Exception
1761 public function testMergeGetConflicts() {
1762 list($contact1, $contact2) = $this->createDeeplyConflictedContacts();
1763 $conflicts = $this->callAPISuccess('Contact', 'get_merge_conflicts', ['to_keep_id' => $contact1, 'to_remove_id' => $contact2])['values'];
1764 $this->assertEquals([
1768 'first_name' => [$contact1 => 'Anthony', $contact2 => 'different', 'title' => 'First Name'],
1769 'external_identifier' => [$contact1 => 'unique and special', $contact2 => 'uniquer and specialler', 'title' => 'External Identifier'],
1770 $this->getCustomFieldName('text') => [$contact1 => 'mummy loves me', $contact2 => 'mummy loves me more', 'title' => 'Enter text here'],
1774 'location_type_id' => '1',
1775 'title' => 'Address 1 (Home)',
1776 'street_address' => [
1777 $contact1 => 'big house',
1778 $contact2 => 'medium house',
1781 $contact1 => "big house\nsmall city, \n",
1782 $contact2 => "medium house\nsmall city, \n",
1786 'location_type_id' => '2',
1787 'street_address' => [
1788 $contact1 => 'big office',
1789 $contact2 => 'medium office',
1791 'title' => 'Address 2 (Work)',
1793 $contact1 => "big office\nsmall city, \n",
1794 $contact2 => "medium office\nsmall city, \n",
1800 'location_type_id' => '1',
1802 $contact1 => 'bob@example.com',
1803 $contact2 => 'anthony_anderson@civicrm.org',
1805 'title' => 'Email 1 (Home)',
1807 $contact1 => 'bob@example.com',
1808 $contact2 => 'anthony_anderson@civicrm.org',
1817 $this->callAPISuccess('Job', 'process_batch_merge');
1818 $defaultRuleGroupID = $this->callAPISuccessGetValue('RuleGroup', [
1819 'contact_type' => 'Individual',
1820 'used' => 'Unsupervised',
1822 'options' => ['limit' => 1],
1825 $duplicates = $this->callAPISuccess('Dedupe', 'getduplicates', ['rule_group_id' => $defaultRuleGroupID]);
1826 $this->assertEquals($conflicts['safe']['conflicts'], $duplicates['values'][0]['safe']['conflicts']);
1831 * @throws \CRM_Core_Exception
1833 public function testGetConflictsAggressiveMode() {
1834 list($contact1, $contact2) = $this->createDeeplyConflictedContacts();
1835 $conflicts = $this->callAPISuccess('Contact', 'get_merge_conflicts', ['to_keep_id' => $contact1, 'to_remove_id' => $contact2, 'mode' => ['safe', 'aggressive']])['values'];
1836 $this->assertEquals([
1838 'external_identifier' => 'uniquer and specialler',
1839 'first_name' => 'different',
1840 'custom_1' => 'mummy loves me more',
1842 ], $conflicts['aggressive']['resolved']);
1846 * Create inherited membership type for employer relationship.
1850 * @throws \CRM_Core_Exception
1852 private function createEmployerOfMembership() {
1854 'domain_id' => CRM_Core_Config
::domainID(),
1855 'name' => 'Organization Membership',
1856 'member_of_contact_id' => 1,
1857 'financial_type_id' => 1,
1858 'minimum_fee' => 10,
1859 'duration_unit' => 'year',
1860 'duration_interval' => 1,
1861 'period_type' => 'rolling',
1862 'relationship_type_id' => 5,
1863 'relationship_direction' => 'b_a',
1864 'visibility' => 'Public',
1867 $membershipType = $this->callAPISuccess('membership_type', 'create', $params);
1868 return $membershipType['values'][$membershipType['id']];
1872 * Verify successful update of household contact.
1874 * @param int $version
1876 * @dataProvider versionThreeAndFour
1878 public function testUpdateHouseholdWithAll($version) {
1879 $this->_apiversion
= $version;
1880 $contactID = $this->householdCreate();
1884 'household_name' => 'ABC household',
1885 'nick_name' => 'ABC House',
1886 'contact_type' => 'Household',
1889 $result = $this->callAPISuccess('Contact', 'Update', $params);
1892 'contact_type' => 'Household',
1894 'sort_name' => 'ABC household',
1895 'display_name' => 'ABC household',
1896 'nick_name' => 'ABC House',
1898 $this->getAndCheck($expected, $result['id'], 'contact');
1902 * Test civicrm_update() without contact type.
1904 * Deliberately exclude contact_type as it should still cope using civicrm_api.
1908 * @param int $version
1910 * @dataProvider versionThreeAndFour
1912 public function testUpdateCreateWithID($version) {
1913 $this->_apiversion
= $version;
1914 $contactID = $this->individualCreate();
1915 $this->callAPISuccess('Contact', 'Update', [
1917 'first_name' => 'abcd',
1918 'last_name' => 'wxyz',
1923 * Test civicrm_contact_delete() with no contact ID.
1925 * @param int $version
1927 * @dataProvider versionThreeAndFour
1929 public function testContactDeleteNoID($version) {
1930 $this->_apiversion
= $version;
1934 $this->callAPIFailure('contact', 'delete', $params);
1938 * Test civicrm_contact_delete() with error.
1940 * @param int $version
1942 * @dataProvider versionThreeAndFour
1944 public function testContactDeleteError($version) {
1945 $this->_apiversion
= $version;
1946 $params = ['contact_id' => 999];
1947 $this->callAPIFailure('contact', 'delete', $params);
1951 * Test civicrm_contact_delete().
1953 * @param int $version
1955 * @dataProvider versionThreeAndFour
1957 public function testContactDelete($version) {
1958 $this->_apiversion
= $version;
1959 $contactID = $this->individualCreate();
1963 $this->callAPIAndDocument('contact', 'delete', $params, __FUNCTION__
, __FILE__
);
1967 * Test civicrm_contact_get() return only first name.
1969 * @param int $version
1971 * @dataProvider versionThreeAndFour
1973 public function testContactGetRetFirst($version) {
1974 $this->_apiversion
= $version;
1975 $contact = $this->callAPISuccess('contact', 'create', $this->_params
);
1977 'contact_id' => $contact['id'],
1978 'return_first_name' => TRUE,
1979 'sort' => 'first_name',
1981 $result = $this->callAPISuccess('contact', 'get', $params);
1982 $this->assertEquals(1, $result['count']);
1983 $this->assertEquals($contact['id'], $result['id']);
1984 $this->assertEquals('abc1', $result['values'][$contact['id']]['first_name']);
1988 * Test civicrm_contact_get() return only first name & last name.
1990 * Use comma separated string return with a space.
1992 * @param int $version
1994 * @dataProvider versionThreeAndFour
1996 public function testContactGetReturnFirstLast($version) {
1997 $this->_apiversion
= $version;
1998 $contact = $this->callAPISuccess('contact', 'create', $this->_params
);
2000 'contact_id' => $contact['id'],
2001 'return' => 'first_name, last_name',
2003 $result = $this->callAPISuccess('contact', 'getsingle', $params);
2004 $this->assertEquals('abc1', $result['first_name']);
2005 $this->assertEquals('xyz1', $result['last_name']);
2006 //check that other defaults not returns
2007 $this->assertArrayNotHasKey('sort_name', $result);
2009 'contact_id' => $contact['id'],
2010 'return' => 'first_name,last_name',
2012 $result = $this->callAPISuccess('contact', 'getsingle', $params);
2013 $this->assertEquals('abc1', $result['first_name']);
2014 $this->assertEquals('xyz1', $result['last_name']);
2015 //check that other defaults not returns
2016 $this->assertArrayNotHasKey('sort_name', $result);
2020 * Test civicrm_contact_get() return only first name & last name.
2022 * Use comma separated string return without a space
2024 * @param int $version
2026 * @dataProvider versionThreeAndFour
2028 public function testContactGetReturnFirstLastNoComma($version) {
2029 $this->_apiversion
= $version;
2030 $contact = $this->callAPISuccess('contact', 'create', $this->_params
);
2032 'contact_id' => $contact['id'],
2033 'return' => 'first_name,last_name',
2035 $result = $this->callAPISuccess('contact', 'getsingle', $params);
2036 $this->assertEquals('abc1', $result['first_name']);
2037 $this->assertEquals('xyz1', $result['last_name']);
2038 //check that other defaults not returns
2039 $this->assertArrayNotHasKey('sort_name', $result);
2043 * Test civicrm_contact_get() with default return properties.
2045 public function testContactGetRetDefault() {
2046 $contactID = $this->individualCreate();
2048 'contact_id' => $contactID,
2049 'sort' => 'first_name',
2051 $result = $this->callAPISuccess('contact', 'get', $params);
2052 $this->assertEquals($contactID, $result['values'][$contactID]['contact_id']);
2053 $this->assertEquals('Anthony', $result['values'][$contactID]['first_name']);
2057 * Test civicrm_contact_getquick() with empty name param.
2059 public function testContactGetQuick() {
2060 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact', 'email' => 'TestContact@example.com']);
2062 $result = $this->callAPISuccess('contact', 'getquick', ['name' => 'T']);
2063 $this->assertEquals($contactID, $result['values'][0]['id']);
2065 'name' => "TestContact@example.com",
2066 'field_name' => 'sort_name',
2068 $result = $this->callAPISuccess('contact', 'getquick', $params);
2069 $this->assertEquals($contactID, $result['values'][0]['id']);
2073 * Test civicrm_contact_get) with empty params.
2075 * @param int $version
2077 * @dataProvider versionThreeAndFour
2079 public function testContactGetEmptyParams($version) {
2080 $this->_apiversion
= $version;
2081 $this->callAPISuccess('contact', 'get', []);
2085 * Test civicrm_contact_get(,true) with no matches.
2087 * @param int $version
2089 * @dataProvider versionThreeAndFour
2091 public function testContactGetOldParamsNoMatches($version) {
2092 $this->_apiversion
= $version;
2093 $this->individualCreate();
2094 $result = $this->callAPISuccess('contact', 'get', ['first_name' => 'Fred']);
2095 $this->assertEquals(0, $result['count']);
2099 * Test civicrm_contact_get(,true) with one match.
2101 public function testContactGetOldParamsOneMatch() {
2102 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2104 $result = $this->callAPISuccess('contact', 'get', ['first_name' => 'Test']);
2105 $this->assertEquals($contactID, $result['values'][$contactID]['contact_id']);
2106 $this->assertEquals($contactID, $result['id']);
2110 * Test civicrm_contact_get(,true) with space in sort_name.
2112 public function testContactGetSpaceMatches() {
2113 $contactParams_1 = [
2114 'first_name' => 'Sanford',
2115 'last_name' => 'Blackwell',
2116 'sort_name' => 'Blackwell, Sanford',
2117 'contact_type' => 'Individual',
2119 $this->individualCreate($contactParams_1);
2121 $contactParams_2 = [
2122 'household_name' => 'Blackwell family',
2123 'sort_name' => 'Blackwell family',
2124 'contact_type' => 'Household',
2126 $this->individualCreate($contactParams_2);
2128 $result = $this->callAPISuccess('contact', 'get', ['sort_name' => 'Blackwell F']);
2129 $this->assertEquals(1, $result['count']);
2133 * Test civicrm_contact_search_count().
2135 public function testContactGetEmail() {
2137 'email' => 'man2@yahoo.com',
2138 'contact_type' => 'Individual',
2139 'location_type_id' => 1,
2142 $contact = $this->callAPISuccess('contact', 'create', $params);
2145 'email' => 'man2@yahoo.com',
2147 $result = $this->callAPIAndDocument('contact', 'get', $params, __FUNCTION__
, __FILE__
);
2148 $this->assertEquals('man2@yahoo.com', $result['values'][$result['id']]['email']);
2150 $this->callAPISuccess('contact', 'delete', $contact);
2154 * Ensure consistent return format for option group fields.
2156 * @param int $version
2158 * @dataProvider versionThreeAndFour
2160 public function testSetPreferredCommunicationNull($version) {
2161 $this->_apiversion
= $version;
2162 $contact = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, [
2163 'preferred_communication_method' => ['Phone', 'SMS'],
2165 $preferredCommunicationMethod = $this->callAPISuccessGetValue('Contact', [
2166 'id' => $contact['id'],
2167 'return' => 'preferred_communication_method',
2169 $this->assertNotEmpty($preferredCommunicationMethod);
2170 $contact = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, [
2171 'preferred_communication_method' => 'null',
2172 'id' => $contact['id'],
2174 $preferredCommunicationMethod = $this->callAPISuccessGetValue('Contact', [
2175 'id' => $contact['id'],
2176 'return' => 'preferred_communication_method',
2178 $this->assertEmpty($preferredCommunicationMethod);
2182 * Ensure consistent return format for option group fields.
2184 * @throws \CRM_Core_Exception
2186 public function testPseudoFields() {
2188 'preferred_communication_method' => ['Phone', 'SMS'],
2189 'preferred_language' => 'en_US',
2190 'gender_id' => 'Female',
2191 'prefix_id' => 'Mrs.',
2192 'suffix_id' => 'II',
2193 'communication_style_id' => 'Formal',
2196 $contact = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, $params));
2198 $result = $this->callAPISuccess('contact', 'getsingle', ['id' => $contact['id']]);
2199 $this->assertEquals('Both', $result['preferred_mail_format']);
2201 $this->assertEquals('en_US', $result['preferred_language']);
2202 $this->assertEquals(1, $result['communication_style_id']);
2203 $this->assertEquals(1, $result['gender_id']);
2204 $this->assertEquals('Female', $result['gender']);
2205 $this->assertEquals('Mrs.', $result['individual_prefix']);
2206 $this->assertEquals(1, $result['prefix_id']);
2207 $this->assertEquals('II', $result['individual_suffix']);
2208 $this->assertEquals(CRM_Core_PseudoConstant
::getKey("CRM_Contact_BAO_Contact", 'suffix_id', 'II'), $result['suffix_id']);
2209 $this->callAPISuccess('contact', 'delete', $contact);
2210 $this->assertEquals([
2211 CRM_Core_PseudoConstant
::getKey("CRM_Contact_BAO_Contact", 'preferred_communication_method', 'Phone'),
2212 CRM_Core_PseudoConstant
::getKey("CRM_Contact_BAO_Contact", 'preferred_communication_method', 'SMS'),
2213 ], $result['preferred_communication_method']);
2217 * Test birth date parameters.
2219 * These include value, array & birth_date_high, birth_date_low
2222 * @throws \CRM_Core_Exception
2224 public function testContactGetBirthDate() {
2225 $contact1 = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, ['birth_date' => 'first day of next month - 2 years']));
2226 $contact2 = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, ['birth_date' => 'first day of next month - 5 years']));
2227 $contact3 = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, ['birth_date' => 'first day of next month -20 years']));
2229 $result = $this->callAPISuccess('contact', 'get', []);
2230 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -2 years')), $result['values'][$contact1['id']]['birth_date']);
2231 $result = $this->callAPISuccess('contact', 'get', ['birth_date' => 'first day of next month -5 years']);
2232 $this->assertEquals(1, $result['count']);
2233 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['birth_date']);
2234 $result = $this->callAPISuccess('contact', 'get', ['birth_date_high' => date('Y-m-d', strtotime('-6 years'))]);
2235 $this->assertEquals(1, $result['count']);
2236 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -20 years')), $result['values'][$contact3['id']]['birth_date']);
2237 $result = $this->callAPISuccess('contact', 'get', [
2238 'birth_date_low' => date('Y-m-d', strtotime('-6 years')),
2239 'birth_date_high' => date('Y-m-d', strtotime('- 3 years')),
2241 $this->assertEquals(1, $result['count']);
2242 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['birth_date']);
2243 $result = $this->callAPISuccess('contact', 'get', [
2244 'birth_date_low' => '-6 years',
2245 'birth_date_high' => '- 3 years',
2247 $this->assertEquals(1, $result['count']);
2248 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['birth_date']);
2252 * Test Address parameters
2254 * This include state_province, state_province_name, country
2256 public function testContactGetWithAddressFields() {
2259 'first_name' => 'abc1',
2260 'contact_type' => 'Individual',
2261 'last_name' => 'xyz1',
2262 'api.address.create' => [
2263 'country' => 'United States',
2264 'state_province_id' => 'Michigan',
2265 'location_type_id' => 1,
2269 'first_name' => 'abc2',
2270 'contact_type' => 'Individual',
2271 'last_name' => 'xyz2',
2272 'api.address.create' => [
2273 'country' => 'United States',
2274 'state_province_id' => 'Alabama',
2275 'location_type_id' => 1,
2279 foreach ($individuals as $params) {
2280 $contact = $this->callAPISuccess('contact', 'create', $params);
2283 // Check whether Contact get API return successfully with below Address params.
2285 'state_province_name' => 'Michigan',
2286 'state_province' => 'Michigan',
2287 'country' => 'United States',
2288 'state_province_name' => ['IN' => ['Michigan', 'Alabama']],
2289 'state_province' => ['IN' => ['Michigan', 'Alabama']],
2291 foreach ($fieldsToTest as $field => $value) {
2293 'id' => $contact['id'],
2296 $result = $this->callAPISuccess('Contact', 'get', $getParams);
2297 $this->assertEquals(1, $result['count']);
2302 * Test Deceased date parameters.
2304 * These include value, array & Deceased_date_high, Deceased date_low
2307 public function testContactGetDeceasedDate() {
2308 $contact1 = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, ['deceased_date' => 'first day of next month - 2 years']));
2309 $contact2 = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, ['deceased_date' => 'first day of next month - 5 years']));
2310 $contact3 = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, ['deceased_date' => 'first day of next month -20 years']));
2312 $result = $this->callAPISuccess('contact', 'get', []);
2313 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -2 years')), $result['values'][$contact1['id']]['deceased_date']);
2314 $result = $this->callAPISuccess('contact', 'get', ['deceased_date' => 'first day of next month -5 years']);
2315 $this->assertEquals(1, $result['count']);
2316 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['deceased_date']);
2317 $result = $this->callAPISuccess('contact', 'get', ['deceased_date_high' => date('Y-m-d', strtotime('-6 years'))]);
2318 $this->assertEquals(1, $result['count']);
2319 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -20 years')), $result['values'][$contact3['id']]['deceased_date']);
2320 $result = $this->callAPISuccess('contact', 'get', [
2321 'deceased_date_low' => '-6 years',
2322 'deceased_date_high' => date('Y-m-d', strtotime('- 3 years')),
2324 $this->assertEquals(1, $result['count']);
2325 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['deceased_date']);
2329 * Test for Contact.get id=@user:username.
2331 public function testContactGetByUsername() {
2332 // Setup - create contact with a uf-match.
2333 $cid = $this->individualCreate([
2334 'contact_type' => 'Individual',
2335 'first_name' => 'testGetByUsername',
2336 'last_name' => 'testGetByUsername',
2340 'domain_id' => CRM_Core_Config
::domainID(),
2342 'uf_name' => 'the-email-matching-key-is-not-really-the-username',
2343 'contact_id' => $cid,
2345 $ufMatch = CRM_Core_BAO_UFMatch
::create($ufMatchParams);
2346 $this->assertTrue(is_numeric($ufMatch->id
));
2348 // setup - mock the calls to CRM_Utils_System_*::getUfId
2349 $mockFunction = $this->mockMethod
;
2350 $userSystem = $this->$mockFunction('CRM_Utils_System_UnitTests', ['getUfId']);
2351 $userSystem->expects($this->once())
2353 ->with($this->equalTo('exampleUser'))
2354 ->will($this->returnValue(99));
2355 CRM_Core_Config
::singleton()->userSystem
= $userSystem;
2358 $result = $this->callAPISuccess('Contact', 'get', [
2359 'id' => '@user:exampleUser',
2361 $this->assertEquals('testGetByUsername', $result['values'][$cid]['first_name']);
2363 // Check search of contacts with & without uf records
2364 $result = $this->callAPISuccess('Contact', 'get', ['uf_user' => 1]);
2365 $this->assertArrayHasKey($cid, $result['values']);
2367 $result = $this->callAPISuccess('Contact', 'get', ['uf_user' => 0]);
2368 $this->assertArrayNotHasKey($cid, $result['values']);
2372 * Test to check return works OK.
2374 public function testContactGetReturnValues() {
2376 'nick_name' => 'Bob',
2378 'email' => 'e@mail.com',
2380 $contactID = $this->individualCreate($extraParams);
2381 //actually it turns out the above doesn't create a phone
2382 $this->callAPISuccess('phone', 'create', ['contact_id' => $contactID, 'phone' => '456']);
2383 $result = $this->callAPISuccess('contact', 'getsingle', ['id' => $contactID]);
2384 foreach ($extraParams as $key => $value) {
2385 $this->assertEquals($result[$key], $value);
2387 //now we check they are still returned with 'return' key
2388 $result = $this->callAPISuccess('contact', 'getsingle', [
2390 'return' => array_keys($extraParams),
2392 foreach ($extraParams as $key => $value) {
2393 $this->assertEquals($result[$key], $value);
2398 * Test creating multiple phones using chaining.
2400 * @param int $version
2402 * @dataProvider versionThreeAndFour
2403 * @throws \Exception
2405 public function testCRM13252MultipleChainedPhones($version) {
2406 $this->_apiversion
= $version;
2407 $contactID = $this->householdCreate();
2408 $this->callAPISuccessGetCount('phone', ['contact_id' => $contactID], 0);
2410 'contact_id' => $contactID,
2411 'household_name' => 'Household 1',
2412 'contact_type' => 'Household',
2413 'api.phone.create' => [
2415 'phone' => '111-111-1111',
2416 'location_type_id' => 1,
2417 'phone_type_id' => 1,
2420 'phone' => '222-222-2222',
2421 'location_type_id' => 1,
2422 'phone_type_id' => 2,
2426 $this->callAPISuccess('contact', 'create', $params);
2427 $this->callAPISuccessGetCount('phone', ['contact_id' => $contactID], 2);
2432 * Test for Contact.get id=@user:username (with an invalid username).
2434 public function testContactGetByUnknownUsername() {
2435 // setup - mock the calls to CRM_Utils_System_*::getUfId
2436 $mockFunction = $this->mockMethod
;
2437 $userSystem = $this->$mockFunction('CRM_Utils_System_UnitTests', ['getUfId']);
2438 $userSystem->expects($this->once())
2440 ->with($this->equalTo('exampleUser'))
2441 ->will($this->returnValue(NULL));
2442 CRM_Core_Config
::singleton()->userSystem
= $userSystem;
2445 $result = $this->callAPIFailure('Contact', 'get', [
2446 'id' => '@user:exampleUser',
2448 $this->assertRegExp('/cannot be resolved to a contact ID/', $result['error_message']);
2452 * Verify attempt to create individual with chained arrays and sequential.
2454 * @param int $version
2456 * @dataProvider versionThreeAndFour
2458 public function testGetIndividualWithChainedArraysAndSequential($version) {
2459 $this->_apiversion
= $version;
2460 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
2461 $params['custom_' . $ids['custom_field_id']] = "custom string";
2463 $moreIDs = $this->CustomGroupMultipleCreateWithFields();
2466 'first_name' => 'abc3',
2467 'last_name' => 'xyz3',
2468 'contact_type' => 'Individual',
2469 'email' => 'man3@yahoo.com',
2470 'api.website.create' => [
2472 'url' => "http://civicrm.org",
2475 'url' => "https://civicrm.org",
2480 $result = $this->callAPISuccess('Contact', 'create', $params);
2482 // delete the contact and custom groups
2483 $this->callAPISuccess('contact', 'delete', ['id' => $result['id']]);
2484 $this->customGroupDelete($ids['custom_group_id']);
2485 $this->customGroupDelete($moreIDs['custom_group_id']);
2487 $this->assertEquals($result['id'], $result['values'][0]['id']);
2488 $this->assertArrayKeyExists('api.website.create', $result['values'][0]);
2492 * Verify attempt to create individual with chained arrays.
2494 public function testGetIndividualWithChainedArrays() {
2495 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
2496 $params['custom_' . $ids['custom_field_id']] = "custom string";
2498 $moreIDs = $this->CustomGroupMultipleCreateWithFields();
2499 $description = "This demonstrates the usage of chained api functions.\nIn this case no notes or custom fields have been created.";
2500 $subfile = "APIChainedArray";
2502 'first_name' => 'abc3',
2503 'last_name' => 'xyz3',
2504 'contact_type' => 'Individual',
2505 'email' => 'man3@yahoo.com',
2506 'api.contribution.create' => [
2507 'receive_date' => '2010-01-01',
2508 'total_amount' => 100.00,
2509 'financial_type_id' => 1,
2510 'payment_instrument_id' => 1,
2511 'non_deductible_amount' => 10.00,
2512 'fee_amount' => 50.00,
2513 'net_amount' => 90.00,
2515 'invoice_id' => 67890,
2517 'contribution_status_id' => 1,
2519 'api.contribution.create.1' => [
2520 'receive_date' => '2011-01-01',
2521 'total_amount' => 120.00,
2522 'financial_type_id' => $this->_financialTypeId
= 1,
2523 'payment_instrument_id' => 1,
2524 'non_deductible_amount' => 10.00,
2525 'fee_amount' => 50.00,
2526 'net_amount' => 90.00,
2528 'invoice_id' => 67830,
2530 'contribution_status_id' => 1,
2532 'api.website.create' => [
2534 'url' => "http://civicrm.org",
2539 $result = $this->callAPISuccess('Contact', 'create', $params);
2541 'id' => $result['id'],
2542 'api.website.get' => [],
2543 'api.Contribution.get' => [
2544 'total_amount' => '120.00',
2546 'api.CustomValue.get' => 1,
2547 'api.Note.get' => 1,
2549 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
2550 // delete the contact
2551 $this->callAPISuccess('contact', 'delete', $result);
2552 $this->customGroupDelete($ids['custom_group_id']);
2553 $this->customGroupDelete($moreIDs['custom_group_id']);
2554 $this->assertEquals(0, $result['values'][$result['id']]['api.website.get']['is_error']);
2555 $this->assertEquals("http://civicrm.org", $result['values'][$result['id']]['api.website.get']['values'][0]['url']);
2559 * Verify attempt to create individual with chained arrays and sequential.
2561 * @see https://issues.civicrm.org/jira/browse/CRM-15815
2563 * @param int $version
2565 * @dataProvider versionThreeAndFour
2567 public function testCreateIndividualWithChainedArrayAndSequential($version) {
2568 $this->_apiversion
= $version;
2571 'first_name' => 'abc5',
2572 'last_name' => 'xyz5',
2573 'contact_type' => 'Individual',
2574 'email' => 'woman5@yahoo.com',
2575 'api.phone.create' => [
2576 ['phone' => '03-231 07 95'],
2577 ['phone' => '03-232 51 62'],
2579 'api.website.create' => [
2580 'url' => 'http://civicrm.org',
2583 $result = $this->callAPISuccess('Contact', 'create', $params);
2585 // I could try to parse the result to see whether the two phone numbers
2586 // and the website are there, but I am not sure about the correct format.
2587 // So I will just fetch it again before checking.
2588 // See also http://forum.civicrm.org/index.php/topic,35393.0.html
2591 'id' => $result['id'],
2592 'api.website.get' => [],
2593 'api.phone.get' => [],
2595 $result = $this->callAPISuccess('Contact', 'get', $params);
2597 // delete the contact
2598 $this->callAPISuccess('contact', 'delete', $result);
2600 $this->assertEquals(2, $result['values'][0]['api.phone.get']['count']);
2601 $this->assertEquals(1, $result['values'][0]['api.website.get']['count']);
2605 * Test retrieving an individual with chained array syntax.
2607 public function testGetIndividualWithChainedArraysFormats() {
2608 $description = "This demonstrates the usage of chained api functions.\nIn this case no notes or custom fields have been created.";
2609 $subfile = "APIChainedArrayFormats";
2610 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
2611 $params['custom_' . $ids['custom_field_id']] = "custom string";
2613 $moreIDs = $this->CustomGroupMultipleCreateWithFields();
2615 'first_name' => 'abc3',
2616 'last_name' => 'xyz3',
2617 'contact_type' => 'Individual',
2618 'email' => 'man3@yahoo.com',
2619 'api.contribution.create' => [
2620 'receive_date' => '2010-01-01',
2621 'total_amount' => 100.00,
2622 'financial_type_id' => $this->_financialTypeId
,
2623 'payment_instrument_id' => 1,
2624 'non_deductible_amount' => 10.00,
2625 'fee_amount' => 50.00,
2626 'net_amount' => 90.00,
2628 'contribution_status_id' => 1,
2629 'skipCleanMoney' => 1,
2631 'api.contribution.create.1' => [
2632 'receive_date' => '2011-01-01',
2633 'total_amount' => 120.00,
2634 'financial_type_id' => $this->_financialTypeId
,
2635 'payment_instrument_id' => 1,
2636 'non_deductible_amount' => 10.00,
2637 'fee_amount' => 50.00,
2638 'net_amount' => 90.00,
2640 'contribution_status_id' => 1,
2641 'skipCleanMoney' => 1,
2643 'api.website.create' => [
2645 'url' => "http://civicrm.org",
2650 $result = $this->callAPISuccess('Contact', 'create', $params);
2652 'id' => $result['id'],
2653 'api.website.getValue' => ['return' => 'url'],
2654 'api.Contribution.getCount' => [],
2655 'api.CustomValue.get' => 1,
2656 'api.Note.get' => 1,
2657 'api.Membership.getCount' => [],
2659 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
2660 $this->assertEquals(2, $result['values'][$result['id']]['api.Contribution.getCount']);
2661 $this->assertEquals(0, $result['values'][$result['id']]['api.Note.get']['is_error']);
2662 $this->assertEquals("http://civicrm.org", $result['values'][$result['id']]['api.website.getValue']);
2664 $this->callAPISuccess('contact', 'delete', $result);
2665 $this->customGroupDelete($ids['custom_group_id']);
2666 $this->customGroupDelete($moreIDs['custom_group_id']);
2670 * Test complex chaining.
2672 public function testGetIndividualWithChainedArraysAndMultipleCustom() {
2673 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
2674 $params['custom_' . $ids['custom_field_id']] = "custom string";
2675 $moreIDs = $this->CustomGroupMultipleCreateWithFields();
2676 $andMoreIDs = $this->CustomGroupMultipleCreateWithFields([
2677 'title' => "another group",
2678 'name' => 'another name',
2680 $description = "This demonstrates the usage of chained api functions with multiple custom fields.";
2681 $subfile = "APIChainedArrayMultipleCustom";
2683 'first_name' => 'abc3',
2684 'last_name' => 'xyz3',
2685 'contact_type' => 'Individual',
2686 'email' => 'man3@yahoo.com',
2687 'api.contribution.create' => [
2688 'receive_date' => '2010-01-01',
2689 'total_amount' => 100.00,
2690 'financial_type_id' => 1,
2691 'payment_instrument_id' => 1,
2692 'non_deductible_amount' => 10.00,
2693 'fee_amount' => 50.00,
2694 'net_amount' => 90.00,
2696 'invoice_id' => 67890,
2698 'contribution_status_id' => 1,
2699 'skipCleanMoney' => 1,
2701 'api.contribution.create.1' => [
2702 'receive_date' => '2011-01-01',
2703 'total_amount' => 120.00,
2704 'financial_type_id' => 1,
2705 'payment_instrument_id' => 1,
2706 'non_deductible_amount' => 10.00,
2707 'fee_amount' => 50.00,
2708 'net_amount' => 90.00,
2710 'invoice_id' => 67830,
2712 'contribution_status_id' => 1,
2713 'skipCleanMoney' => 1,
2715 'api.website.create' => [
2717 'url' => "http://civicrm.org",
2720 'custom_' . $ids['custom_field_id'] => "value 1",
2721 'custom_' . $moreIDs['custom_field_id'][0] => "value 2",
2722 'custom_' . $moreIDs['custom_field_id'][1] => "warm beer",
2723 'custom_' . $andMoreIDs['custom_field_id'][1] => "vegemite",
2726 $result = $this->callAPISuccess('Contact', 'create', $params);
2727 $result = $this->callAPISuccess('Contact', 'create', [
2728 'contact_type' => 'Individual',
2729 'id' => $result['id'],
2731 $moreIDs['custom_field_id'][0] => "value 3",
2733 $ids['custom_field_id'] => "value 4",
2737 'id' => $result['id'],
2738 'api.website.getValue' => ['return' => 'url'],
2739 'api.Contribution.getCount' => [],
2740 'api.CustomValue.get' => 1,
2742 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
2744 $this->customGroupDelete($ids['custom_group_id']);
2745 $this->customGroupDelete($moreIDs['custom_group_id']);
2746 $this->customGroupDelete($andMoreIDs['custom_group_id']);
2747 $this->assertEquals(0, $result['values'][$result['id']]['api.CustomValue.get']['is_error']);
2748 $this->assertEquals('http://civicrm.org', $result['values'][$result['id']]['api.website.getValue']);
2752 * Test checks usage of $values to pick & choose inputs.
2754 * Api3 Only - chaining syntax is too funky for v4 (assuming entityTag "entity_id" field will be filled by magic)
2756 public function testChainingValuesCreate() {
2757 $description = "This demonstrates the usage of chained api functions. Specifically it has one 'parent function' &
2758 2 child functions - one receives values from the parent (Contact) and the other child (Tag).";
2759 $subfile = "APIChainedArrayValuesFromSiblingFunction";
2761 'display_name' => 'batman',
2762 'contact_type' => 'Individual',
2763 'api.tag.create' => [
2764 'name' => '$value.id',
2765 'description' => '$value.display_name',
2766 'format.only_id' => 1,
2768 'api.entity_tag.create' => ['tag_id' => '$value.api.tag.create'],
2770 $result = $this->callAPIAndDocument('Contact', 'Create', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
2771 $this->assertEquals(0, $result['values'][$result['id']]['api.entity_tag.create']['is_error']);
2773 $tablesToTruncate = [
2776 'civicrm_entity_tag',
2779 $this->quickCleanup($tablesToTruncate, TRUE);
2783 * Test TrueFalse format - I couldn't come up with an easy way to get an error on Get.
2785 * @param int $version
2787 * @dataProvider versionThreeAndFour
2789 public function testContactGetFormatIsSuccessTrue($version) {
2790 $this->_apiversion
= $version;
2791 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2792 $description = "This demonstrates use of the 'format.is_success' param.
2793 This param causes only the success or otherwise of the function to be returned as BOOLEAN";
2794 $subfile = "FormatIsSuccess_True";
2795 $params = ['id' => $contactID, 'format.is_success' => 1];
2796 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
2797 $this->assertEquals(1, $result);
2798 $this->callAPISuccess('Contact', 'Delete', $params);
2802 * Test TrueFalse format.
2804 * @param int $version
2806 * @dataProvider versionThreeAndFour
2808 public function testContactCreateFormatIsSuccessFalse($version) {
2809 $this->_apiversion
= $version;
2811 $description = "This demonstrates use of the 'format.is_success' param.
2812 This param causes only the success or otherwise of the function to be returned as BOOLEAN";
2813 $subfile = "FormatIsSuccess_Fail";
2814 $params = ['id' => 500, 'format.is_success' => 1];
2815 $result = $this->callAPIAndDocument('Contact', 'Create', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
2816 $this->assertEquals(0, $result);
2820 * Test long display names.
2824 * @param int $version
2826 * @dataProvider versionThreeAndFour
2828 public function testContactCreateLongDisplayName($version) {
2829 $this->_apiversion
= $version;
2830 $result = $this->callAPISuccess('Contact', 'Create', [
2831 'first_name' => str_pad('a', 64, 'a'),
2832 'last_name' => str_pad('a', 64, 'a'),
2833 'contact_type' => 'Individual',
2835 $this->assertEquals('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', $result['values'][$result['id']]['display_name']);
2836 $this->assertEquals('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', $result['values'][$result['id']]['sort_name']);
2840 * Test that we can set the sort name via the api or alter it via a hook.
2842 * As of writing this is being fixed for Organization & Household but it makes sense to do for individuals too.
2844 * @param int $version
2846 * @dataProvider versionThreeAndFour
2848 public function testCreateAlterSortName($version) {
2849 $this->_apiversion
= $version;
2850 $organizationID = $this->organizationCreate(['organization_name' => 'The Justice League', 'sort_name' => 'Justice League, The']);
2851 $organization = $this->callAPISuccessGetSingle('Contact', ['return' => ['sort_name', 'display_name'], 'id' => $organizationID]);
2852 $this->assertEquals('Justice League, The', $organization['sort_name']);
2853 $this->assertEquals('The Justice League', $organization['display_name']);
2854 $this->hookClass
->setHook('civicrm_pre', [$this, 'killTheJusticeLeague']);
2855 $this->organizationCreate(['id' => $organizationID, 'sort_name' => 'Justice League, The']);
2856 $organization = $this->callAPISuccessGetSingle('Contact', ['return' => ['sort_name', 'display_name', 'is_deceased'], 'id' => $organizationID]);
2857 $this->assertEquals('Steppenwolf wuz here', $organization['display_name']);
2858 $this->assertEquals('Steppenwolf wuz here', $organization['sort_name']);
2859 $this->assertEquals(1, $organization['is_deceased']);
2861 $householdID = $this->householdCreate();
2862 $household = $this->callAPISuccessGetSingle('Contact', ['return' => ['sort_name', 'display_name'], 'id' => $householdID]);
2863 $this->assertEquals('Steppenwolf wuz here', $household['display_name']);
2864 $this->assertEquals('Steppenwolf wuz here', $household['sort_name']);
2868 * Implements hook_pre().
2870 public function killTheJusticeLeague($op, $entity, $id, &$params) {
2871 $params['sort_name'] = 'Steppenwolf wuz here';
2872 $params['display_name'] = 'Steppenwolf wuz here';
2873 $params['is_deceased'] = 1;
2877 * Test Single Entity format.
2879 * @param int $version
2881 * @dataProvider versionThreeAndFour
2883 public function testContactGetSingleEntityArray($version) {
2884 $this->_apiversion
= $version;
2885 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2886 $description = "This demonstrates use of the 'format.single_entity_array' param.
2887 This param causes the only contact to be returned as an array without the other levels.
2888 It will be ignored if there is not exactly 1 result";
2889 $subfile = "GetSingleContact";
2890 $result = $this->callAPIAndDocument('Contact', 'GetSingle', ['id' => $contactID], __FUNCTION__
, __FILE__
, $description, $subfile);
2891 $this->assertEquals('Mr. Test Contact II', $result['display_name']);
2892 $this->callAPISuccess('Contact', 'Delete', ['id' => $contactID]);
2896 * Test Single Entity format.
2898 * @param int $version
2900 * @dataProvider versionThreeAndFour
2902 public function testContactGetFormatCountOnly($version) {
2903 $this->_apiversion
= $version;
2904 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2905 $description = "This demonstrates use of the 'getCount' action.
2906 This param causes the count of the only function to be returned as an integer.";
2907 $params = ['id' => $contactID];
2908 $result = $this->callAPIAndDocument('Contact', 'GetCount', $params, __FUNCTION__
, __FILE__
, $description,
2910 $this->assertEquals('1', $result);
2911 $this->callAPISuccess('Contact', 'Delete', $params);
2915 * Test id only format.
2917 * @param int $version
2919 * @dataProvider versionThreeAndFour
2921 public function testContactGetFormatIDOnly($version) {
2922 $this->_apiversion
= $version;
2923 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2924 $description = "This demonstrates use of the 'format.id_only' param.
2925 This param causes the id of the only entity to be returned as an integer.
2926 It will be ignored if there is not exactly 1 result";
2927 $subfile = "FormatOnlyID";
2928 $params = ['id' => $contactID, 'format.only_id' => 1];
2929 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
2930 $this->assertEquals($contactID, $result);
2931 $this->callAPISuccess('Contact', 'Delete', $params);
2935 * Test id only format.
2937 * @param int $version
2939 * @dataProvider versionThreeAndFour
2941 public function testContactGetFormatSingleValue($version) {
2942 $this->_apiversion
= $version;
2943 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2944 $description = "This demonstrates use of the 'format.single_value' param.
2945 This param causes only a single value of the only entity to be returned as an string.
2946 It will be ignored if there is not exactly 1 result";
2947 $subFile = "FormatSingleValue";
2948 $params = ['id' => $contactID, 'return' => 'display_name'];
2949 $result = $this->callAPIAndDocument('Contact', 'getvalue', $params, __FUNCTION__
, __FILE__
, $description, $subFile);
2950 $this->assertEquals('Mr. Test Contact II', $result);
2951 $this->callAPISuccess('Contact', 'Delete', $params);
2955 * Test that permissions are respected when creating contacts.
2957 * @param int $version
2959 * @dataProvider versionThreeAndFour
2961 public function testContactCreationPermissions($version) {
2962 $this->_apiversion
= $version;
2964 'contact_type' => 'Individual',
2965 'first_name' => 'Foo',
2966 'last_name' => 'Bear',
2967 'check_permissions' => TRUE,
2969 $config = CRM_Core_Config
::singleton();
2970 $config->userPermissionClass
->permissions
= ['access CiviCRM'];
2971 $result = $this->callAPIFailure('contact', 'create', $params);
2972 $this->assertContains('failed', $result['error_message'], 'lacking permissions should not be enough to create a contact');
2974 $config->userPermissionClass
->permissions
= ['access CiviCRM', 'add contacts', 'import contacts'];
2975 $this->callAPISuccess('contact', 'create', $params);
2979 * Test that delete with skip undelete respects permissions.
2982 * @throws \CRM_Core_Exception
2984 public function testContactDeletePermissions() {
2985 $contactID = $this->individualCreate();
2986 $tag = $this->callAPISuccess('Tag', 'create', ['name' => 'to be deleted']);
2987 $this->callAPISuccess('EntityTag', 'create', ['entity_id' => $contactID, 'tag_id' => $tag['id']]);
2988 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access CiviCRM'];
2989 $this->callAPIFailure('Contact', 'delete', [
2991 'check_permissions' => 1,
2992 'skip_undelete' => 1,
2994 $this->callAPISuccess('Contact', 'delete', [
2996 'check_permissions' => 0,
2997 'skip_undelete' => 1,
2999 $this->callAPISuccessGetCount('EntityTag', ['entity_id' => $contactID], 0);
3003 * Test update with check permissions set.
3005 * @param int $version
3007 * @dataProvider versionThreeAndFour
3009 public function testContactUpdatePermissions($version) {
3010 $this->_apiversion
= $version;
3012 'contact_type' => 'Individual',
3013 'first_name' => 'Foo',
3014 'last_name' => 'Bear',
3015 'check_permissions' => TRUE,
3017 $result = $this->callAPISuccess('contact', 'create', $params);
3018 $config = CRM_Core_Config
::singleton();
3020 'id' => $result['id'],
3021 'contact_type' => 'Individual',
3022 'last_name' => 'Bar',
3023 'check_permissions' => TRUE,
3026 $config->userPermissionClass
->permissions
= ['access CiviCRM'];
3027 $result = $this->callAPIFailure('contact', 'update', $params);
3028 $this->assertEquals('Permission denied to modify contact record', $result['error_message']);
3030 $config->userPermissionClass
->permissions
= [
3033 'view all contacts',
3034 'edit all contacts',
3037 $this->callAPISuccess('contact', 'update', $params);
3041 * Test contact proximity api.
3043 public function testContactProximity() {
3044 // first create a contact with a SF location with a specific
3046 $contactID = $this->organizationCreate();
3048 // now create the address
3050 'street_address' => '123 Main Street',
3051 'city' => 'San Francisco',
3053 'country_id' => 1228,
3054 'state_province_id' => 1004,
3055 'geo_code_1' => '37.79',
3056 'geo_code_2' => '-122.40',
3057 'location_type_id' => 1,
3058 'contact_id' => $contactID,
3061 $result = $this->callAPISuccess('address', 'create', $params);
3062 $this->assertEquals(1, $result['count']);
3064 // now do a proximity search with a close enough geocode and hope to match
3065 // that specific contact only!
3068 'longitude' => -122.3,
3072 $result = $this->callAPISuccess('contact', 'proximity', $proxParams);
3073 $this->assertEquals(1, $result['count']);
3077 * Test that Ajax API permission is sufficient to access getquick api.
3079 * (note that getquick api is required for autocomplete & has ACL permissions applied)
3081 public function testGetquickPermissionCRM13744() {
3082 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access CiviEvent'];
3083 $this->callAPIFailure('contact', 'getquick', ['name' => 'b', 'check_permissions' => TRUE]);
3084 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access CiviCRM'];
3085 $this->callAPISuccess('contact', 'getquick', ['name' => 'b', 'check_permissions' => TRUE]);
3086 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access AJAX API'];
3087 $this->callAPISuccess('contact', 'getquick', ['name' => 'b', 'check_permissions' => TRUE]);
3091 * Test that getquick returns contacts with an exact first name match first.
3093 * The search string 'b' & 'bob' both return ordered by sort_name if includeOrderByClause
3094 * is true (default) but if it is false then matches are returned in ID order.
3096 * @dataProvider getSearchSortOptions
3098 public function testGetQuickExactFirst($searchParameters, $settings, $firstContact, $secondContact = NULL) {
3099 $this->getQuickSearchSampleData();
3100 $this->callAPISuccess('Setting', 'create', $settings);
3101 $result = $this->callAPISuccess('contact', 'getquick', $searchParameters);
3102 $this->assertEquals($firstContact, $result['values'][0]['sort_name']);
3103 $this->assertEquals($secondContact, $result['values'][1]['sort_name']);
3104 $this->callAPISuccess('Setting', 'create', ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE]);
3107 public function getSearchSortOptions() {
3108 $firstAlphabeticalContactBySortName = 'A Bobby, Bobby';
3109 $secondAlphabeticalContactBySortName = 'Aadvark, Bob';
3110 $secondAlphabeticalContactWithEmailBySortName = 'Bob, Bob';
3111 $firstAlphabeticalContactFirstNameBob = 'Aadvark, Bob';
3112 $secondAlphabeticalContactFirstNameBob = 'Bob, Bob';
3113 $firstByIDContactFirstNameBob = 'Bob, Bob';
3114 $secondByIDContactFirstNameBob = 'K Bobby, Bob';
3115 $firstContactByID = 'Bob, Bob';
3116 $secondContactByID = 'E Bobby, Bobby';
3117 $bobLikeEmail = 'A Bobby, Bobby';
3120 'empty_search_basic' => [
3121 'search_parameters' => ['name' => '%'],
3122 'settings' => ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE],
3123 'first_contact' => $firstAlphabeticalContactBySortName,
3124 'second_contact' => $secondAlphabeticalContactBySortName,
3126 'empty_search_basic_no_wildcard' => [
3127 'search_parameters' => ['name' => '%'],
3128 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3129 'first_contact' => $firstAlphabeticalContactBySortName,
3130 'second_contact' => $secondAlphabeticalContactBySortName,
3132 'single_letter_search_basic' => [
3133 'search_parameters' => ['name' => 'b'],
3134 'settings' => ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE],
3135 'first_contact' => $firstAlphabeticalContactBySortName,
3136 'second_contact' => $secondAlphabeticalContactBySortName,
3138 'bob_search_basic' => [
3139 'search_parameters' => ['name' => 'bob'],
3140 'settings' => ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE],
3141 'first_contact' => $firstAlphabeticalContactBySortName,
3142 'second_contact' => $secondAlphabeticalContactBySortName,
3144 // This test has been disabled as is proving to be problematic to reproduce due to MySQL sorting issues between different versions
3145 // 'bob_search_no_orderby' => array(
3146 // 'search_parameters' => array('name' => 'bob'),
3147 // 'settings' => array('includeWildCardInName' => TRUE, 'includeOrderByClause' => FALSE),
3148 // 'first_contact' => $firstContactByID,
3149 // 'second_contact' => $secondContactByID,
3151 'bob_search_no_wildcard' => [
3152 'search_parameters' => ['name' => 'bob'],
3153 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3154 'second_contact' => $bobLikeEmail,
3155 'first_contact' => $secondAlphabeticalContactFirstNameBob,
3157 // This should be the same as just no wildcard as if we had an exactMatch while searching by
3158 // sort name it would rise to the top CRM-19547
3159 'bob_search_no_wildcard_no_orderby' => [
3160 'search_parameters' => ['name' => 'bob'],
3161 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3162 'second_contact' => $bobLikeEmail,
3163 'first_contact' => $secondAlphabeticalContactFirstNameBob,
3165 'first_name_search_basic' => [
3166 'search_parameters' => ['name' => 'bob', 'field_name' => 'first_name'],
3167 'settings' => ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE],
3168 'first_contact' => $firstAlphabeticalContactFirstNameBob,
3169 'second_contact' => $secondAlphabeticalContactFirstNameBob,
3171 'first_name_search_no_wildcard' => [
3172 'search_parameters' => ['name' => 'bob', 'field_name' => 'first_name'],
3173 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3174 'first_contact' => $firstAlphabeticalContactFirstNameBob,
3175 'second_contact' => $secondAlphabeticalContactFirstNameBob,
3177 // This test has been disabled as is proving to be problematic to reproduce due to MySQL sorting issues between different versions
3178 //'first_name_search_no_orderby' => array(
3179 // 'search_parameters' => array('name' => 'bob', 'field_name' => 'first_name'),
3180 // 'settings' => array('includeWildCardInName' => TRUE, 'includeOrderByClause' => FALSE),
3181 // 'first_contact' => $firstByIDContactFirstNameBob,
3182 // 'second_contact' => $secondByIDContactFirstNameBob,
3184 'email_search_basic' => [
3185 'search_parameters' => ['name' => 'bob', 'field_name' => 'email', 'table_name' => 'eml'],
3186 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3187 'first_contact' => $firstAlphabeticalContactBySortName,
3188 'second_contact' => $secondAlphabeticalContactWithEmailBySortName,
3194 * Test that getquick returns contacts with an exact first name match first.
3196 public function testGetQuickEmail() {
3197 $this->getQuickSearchSampleData();
3198 $loggedInContactID = $this->createLoggedInUser();
3199 $result = $this->callAPISuccess('contact', 'getquick', [
3203 'A Bobby, Bobby :: bob@bobby.com',
3204 'Bob, Bob :: bob@bob.com',
3206 'H Bobby, Bobby :: bob@h.com',
3208 $this->callAPISuccessGetValue('Contact', ['id' => $loggedInContactID, 'return' => 'last_name']) . ', Logged In :: anthony_anderson@civicrm.org',
3210 $this->assertEquals(6, $result['count']);
3211 foreach ($expectedData as $index => $value) {
3212 $this->assertEquals($value, $result['values'][$index]['data']);
3214 $result = $this->callAPISuccess('contact', 'getquick', [
3218 'H Bobby, Bobby :: bob@h.com',
3220 foreach ($expectedData as $index => $value) {
3221 $this->assertEquals($value, $result['values'][$index]['data']);
3223 $this->callAPISuccess('Setting', 'create', ['includeWildCardInName' => FALSE]);
3224 $result = $this->callAPISuccess('contact', 'getquick', [
3227 $this->callAPISuccess('Setting', 'create', ['includeWildCardInName' => TRUE]);
3228 $this->assertEquals(0, $result['count']);
3232 * Test that getquick returns contacts with an exact first name match first.
3234 public function testGetQuickEmailACL() {
3235 $this->getQuickSearchSampleData();
3236 $loggedInContactID = $this->createLoggedInUser();
3237 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= [];
3238 $result = $this->callAPISuccess('contact', 'getquick', [
3241 $this->assertEquals(0, $result['count']);
3243 $this->hookClass
->setHook('civicrm_aclWhereClause', [$this, 'aclWhereNoBobH']);
3244 CRM_Contact_BAO_Contact_Permission
::cache($loggedInContactID, CRM_Core_Permission
::VIEW
, TRUE);
3245 $result = $this->callAPISuccess('contact', 'getquick', [
3249 // Without the acl it would be 6 like the previous email getquick test.
3250 $this->assertEquals(5, $result['count']);
3252 'A Bobby, Bobby :: bob@bobby.com',
3253 'Bob, Bob :: bob@bob.com',
3256 $this->callAPISuccessGetValue('Contact', ['id' => $loggedInContactID, 'return' => 'last_name']) . ', Logged In :: anthony_anderson@civicrm.org',
3258 foreach ($expectedData as $index => $value) {
3259 $this->assertEquals($value, $result['values'][$index]['data']);
3264 * Test that getquick returns contacts with an exact first name match first.
3266 public function testGetQuickExternalID() {
3267 $this->getQuickSearchSampleData();
3268 $result = $this->callAPISuccess('contact', 'getquick', [
3270 'field_name' => 'external_identifier',
3271 'table_name' => 'cc',
3273 $this->assertEquals(0, $result['count']);
3274 $result = $this->callAPISuccess('contact', 'getquick', [
3276 'field_name' => 'external_identifier',
3277 'table_name' => 'cc',
3279 $this->assertEquals(1, $result['count']);
3280 $this->assertEquals('Bob, Bob', $result['values'][0]['sort_name']);
3284 * Test that getquick returns contacts with an exact first name match first.
3286 public function testGetQuickID() {
3287 $max = CRM_Core_DAO
::singleValueQuery("SELECT max(id) FROM civicrm_contact");
3288 $this->getQuickSearchSampleData();
3289 $result = $this->callAPISuccess('contact', 'getquick', [
3291 'field_name' => 'id',
3292 'table_name' => 'cc',
3294 $this->assertEquals(1, $result['count']);
3295 $this->assertEquals('E Bobby, Bobby', $result['values'][0]['sort_name']);
3296 $result = $this->callAPISuccess('contact', 'getquick', [
3298 'field_name' => 'contact_id',
3299 'table_name' => 'cc',
3301 $this->assertEquals(1, $result['count']);
3302 $this->assertEquals('E Bobby, Bobby', $result['values'][0]['sort_name']);
3306 * Test that getquick returns contacts with different cases of phone substring.
3308 public function testGetQuickPhone() {
3309 $this->getQuickSearchSampleData();
3314 'field_name' => 'phone_numeric',
3325 'field_name' => 'phone_numeric',
3335 'field_name' => 'phone_numeric',
3344 'name' => '8a7abc6',
3345 'field_name' => 'phone_numeric',
3355 foreach ($criterias as $criteria) {
3356 $result = $this->callAPISuccess('contact', 'getquick', $criteria['criteria']);
3357 $this->assertEquals($result['count'], $criteria['count']);
3358 foreach ($criteria['sort_names'] as $key => $sortName) {
3359 $this->assertEquals($sortName, $result['values'][$key]['sort_name']);
3365 * Test that getquick returns contacts with an exact first name match first.
3367 * Depending on the setting the sort name sort might click in next or not - test!
3369 public function testGetQuickFirstName() {
3370 $this->getQuickSearchSampleData();
3371 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => TRUE]);
3372 $result = $this->callAPISuccess('contact', 'getquick', [
3374 'field_name' => 'first_name',
3375 'table_name' => 'cc',
3384 foreach ($expected as $index => $value) {
3385 $this->assertEquals($value, $result['values'][$index]['sort_name']);
3387 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => FALSE]);
3388 $result = $this->callAPISuccess('contact', 'getquick', ['name' => 'bob']);
3389 $this->assertEquals('Bob, Bob', $result['values'][0]['sort_name']);
3390 // This test has been disabled as is proving to be problematic to reproduce due to MySQL sorting issues between different versions
3391 //$this->assertEquals('E Bobby, Bobby', $result['values'][1]['sort_name']);
3395 * Test that getquick applies ACLs.
3397 public function testGetQuickFirstNameACLs() {
3398 $this->getQuickSearchSampleData();
3399 $userID = $this->createLoggedInUser();
3400 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => TRUE, 'search_autocomplete_count' => 15]);
3401 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= [];
3402 $result = $this->callAPISuccess('contact', 'getquick', [
3404 'field_name' => 'first_name',
3405 'table_name' => 'cc',
3407 $this->assertEquals(0, $result['count']);
3409 $this->hookClass
->setHook('civicrm_aclWhereClause', [$this, 'aclWhereNoBobH']);
3410 CRM_Contact_BAO_Contact_Permission
::cache($userID, CRM_Core_Permission
::VIEW
, TRUE);
3411 $result = $this->callAPISuccess('contact', 'getquick', [
3413 'field_name' => 'first_name',
3414 'table_name' => 'cc',
3416 $this->assertEquals('K Bobby, Bob', $result['values'][2]['sort_name']);
3417 // Without the ACL 9 would be bob@h.com.
3418 $this->assertEquals('I Bobby, Bobby', $result['values'][10]['sort_name']);
3422 * Full results returned.
3424 * @implements CRM_Utils_Hook::aclWhereClause
3426 * @param string $type
3427 * @param array $tables
3428 * @param array $whereTables
3429 * @param int $contactID
3430 * @param string $where
3432 public function aclWhereNoBobH($type, &$tables, &$whereTables, &$contactID, &$where) {
3433 $where = " (email <> 'bob@h.com' OR email IS NULL) ";
3434 $whereTables['civicrm_email'] = "LEFT JOIN civicrm_email e ON contact_a.id = e.contact_id";
3438 * Test that getquick returns contacts with an exact last name match first.
3440 public function testGetQuickLastName() {
3441 $this->getQuickSearchSampleData();
3442 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => TRUE]);
3443 $result = $this->callAPISuccess('contact', 'getquick', [
3445 'field_name' => 'last_name',
3446 'table_name' => 'cc',
3454 foreach ($expected as $index => $value) {
3455 $this->assertEquals($value, $result['values'][$index]['sort_name']);
3457 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => FALSE]);
3458 $result = $this->callAPISuccess('contact', 'getquick', ['name' => 'bob']);
3459 $this->assertEquals('Bob, Bob :: bob@bob.com', $result['values'][0]['data']);
3463 * Test that getquick returns contacts by city.
3465 public function testGetQuickCity() {
3466 $this->getQuickSearchSampleData();
3467 $result = $this->callAPISuccess('contact', 'getquick', [
3469 'field_name' => 'city',
3470 'table_name' => 'sts',
3472 $this->assertEquals('B Bobby, Bobby :: Toronto', $result['values'][0]['data']);
3473 $result = $this->callAPISuccess('contact', 'getquick', [
3475 'field_name' => 'city',
3476 'table_name' => 'sts',
3478 $this->assertEquals('B Bobby, Bobby :: Toronto', $result['values'][0]['data']);
3479 $this->assertEquals('C Bobby, Bobby :: Whanganui', $result['values'][1]['data']);
3483 * Set up some sample data for testing quicksearch.
3485 public function getQuickSearchSampleData() {
3487 ['first_name' => 'Bob', 'last_name' => 'Bob', 'external_identifier' => 'abc', 'email' => 'bob@bob.com'],
3488 ['first_name' => 'Bobby', 'last_name' => 'E Bobby', 'external_identifier' => 'abcd'],
3490 'first_name' => 'Bobby',
3491 'last_name' => 'B Bobby',
3492 'external_identifier' => 'bcd',
3493 'api.address.create' => [
3494 'street_address' => 'Sesame Street',
3495 'city' => 'Toronto',
3496 'location_type_id' => 1,
3500 'first_name' => 'Bobby',
3501 'last_name' => 'C Bobby',
3502 'external_identifier' => 'bcde',
3503 'api.address.create' => [
3504 'street_address' => 'Te huarahi',
3505 'city' => 'Whanganui',
3506 'location_type_id' => 1,
3509 ['first_name' => 'Bobby', 'last_name' => 'D Bobby', 'external_identifier' => 'efg'],
3510 ['first_name' => 'Bobby', 'last_name' => 'A Bobby', 'external_identifier' => 'hij', 'email' => 'bob@bobby.com'],
3511 ['first_name' => 'Bobby', 'last_name' => 'F Bobby', 'external_identifier' => 'klm'],
3512 ['first_name' => 'Bobby', 'last_name' => 'G Bobby', 'external_identifier' => 'nop'],
3513 ['first_name' => 'Bobby', 'last_name' => 'H Bobby', 'external_identifier' => 'qrs', 'email' => 'bob@h.com'],
3515 'first_name' => 'Bobby',
3516 'last_name' => 'I Bobby',
3517 'api.phone.create' => [
3518 'phone' => '876-123',
3519 'phone_ext' => '444',
3520 "phone_type_id" => "Phone",
3521 'location_type_id' => 1,
3526 'first_name' => 'Bobby',
3527 'last_name' => 'J Bobby',
3528 'api.phone.create' => [
3529 'phone' => '87-6-234',
3530 'phone_ext' => '134',
3531 "phone_type_id" => "Phone",
3532 'location_type_id' => 1,
3536 ['first_name' => 'Bob', 'last_name' => 'K Bobby', 'external_identifier' => 'bcdef'],
3537 ['first_name' => 'Bob', 'last_name' => 'Aadvark'],
3539 foreach ($contacts as $type => $contact) {
3540 $contact['contact_type'] = 'Individual';
3541 $this->callAPISuccess('Contact', 'create', $contact);
3546 * Test get ref api - gets a list of references to an entity.
3548 public function testGetReferenceCounts() {
3549 $result = $this->callAPISuccess('Contact', 'create', [
3550 'first_name' => 'Testily',
3551 'last_name' => 'McHaste',
3552 'contact_type' => 'Individual',
3553 'api.Address.replace' => [
3556 'api.Email.replace' => [
3559 'email' => 'spam@dev.null',
3561 'location_type_id' => 1,
3565 'api.Phone.replace' => [
3568 'phone' => '234-567-0001',
3570 'location_type_id' => 1,
3573 'phone' => '234-567-0002',
3575 'location_type_id' => 1,
3581 //$dao = new CRM_Contact_BAO_Contact();
3582 //$dao->id = $result['id'];
3583 //$this->assertTrue((bool) $dao->find(TRUE));
3585 //$refCounts = $dao->getReferenceCounts();
3586 //$this->assertTrue(is_array($refCounts));
3587 //$refCountsIdx = CRM_Utils_Array::index(array('name'), $refCounts);
3589 $refCounts = $this->callAPISuccess('Contact', 'getrefcount', [
3590 'id' => $result['id'],
3592 $refCountsIdx = CRM_Utils_Array
::index(['name'], $refCounts['values']);
3594 $this->assertEquals(1, $refCountsIdx['sql:civicrm_email:contact_id']['count']);
3595 $this->assertEquals('civicrm_email', $refCountsIdx['sql:civicrm_email:contact_id']['table']);
3596 $this->assertEquals(2, $refCountsIdx['sql:civicrm_phone:contact_id']['count']);
3597 $this->assertEquals('civicrm_phone', $refCountsIdx['sql:civicrm_phone:contact_id']['table']);
3598 $this->assertTrue(!isset($refCountsIdx['sql:civicrm_address:contact_id']));
3602 * Test the use of sql operators.
3604 * @param int $version
3606 * @dataProvider versionThreeAndFour
3608 public function testSQLOperatorsOnContactAPI($version) {
3609 $this->_apiversion
= $version;
3610 $this->individualCreate();
3611 $this->organizationCreate();
3612 $this->householdCreate();
3613 $contacts = $this->callAPISuccess('contact', 'get', ['legal_name' => ['IS NOT NULL' => TRUE]]);
3614 $this->assertEquals($contacts['count'], CRM_Core_DAO
::singleValueQuery('select count(*) FROM civicrm_contact WHERE legal_name IS NOT NULL'));
3615 $contacts = $this->callAPISuccess('contact', 'get', ['legal_name' => ['IS NULL' => TRUE]]);
3616 $this->assertEquals($contacts['count'], CRM_Core_DAO
::singleValueQuery('select count(*) FROM civicrm_contact WHERE legal_name IS NULL'));
3620 * CRM-14743 - test api respects search operators.
3622 * @param int $version
3624 * @dataProvider versionThreeAndFour
3626 public function testGetModifiedDateByOperators($version) {
3627 $this->_apiversion
= $version;
3628 $preExistingContactCount = CRM_Core_DAO
::singleValueQuery('select count(*) FROM civicrm_contact');
3629 $contact1 = $this->individualCreate();
3630 $sql = "UPDATE civicrm_contact SET created_date = '2012-01-01', modified_date = '2013-01-01' WHERE id = " . $contact1;
3631 CRM_Core_DAO
::executeQuery($sql);
3632 $contact2 = $this->individualCreate();
3633 $sql = "UPDATE civicrm_contact SET created_date = '2012-02-01', modified_date = '2013-02-01' WHERE id = " . $contact2;
3634 CRM_Core_DAO
::executeQuery($sql);
3635 $contact3 = $this->householdCreate();
3636 $sql = "UPDATE civicrm_contact SET created_date = '2012-03-01', modified_date = '2013-03-01' WHERE id = " . $contact3;
3637 CRM_Core_DAO
::executeQuery($sql);
3638 $contacts = $this->callAPISuccess('contact', 'get', ['modified_date' => ['<' => '2014-01-01']]);
3639 $this->assertEquals($contacts['count'], 3);
3640 $contacts = $this->callAPISuccess('contact', 'get', ['modified_date' => ['>' => '2014-01-01']]);
3641 $this->assertEquals($contacts['count'], $preExistingContactCount);
3645 * CRM-14743 - test api respects search operators.
3647 * @param int $version
3649 * @dataProvider versionThreeAndFour
3651 public function testGetCreatedDateByOperators($version) {
3652 $this->_apiversion
= $version;
3653 $preExistingContactCount = CRM_Core_DAO
::singleValueQuery('select count(*) FROM civicrm_contact');
3654 $contact1 = $this->individualCreate();
3655 $sql = "UPDATE civicrm_contact SET created_date = '2012-01-01' WHERE id = " . $contact1;
3656 CRM_Core_DAO
::executeQuery($sql);
3657 $contact2 = $this->individualCreate();
3658 $sql = "UPDATE civicrm_contact SET created_date = '2012-02-01' WHERE id = " . $contact2;
3659 CRM_Core_DAO
::executeQuery($sql);
3660 $contact3 = $this->householdCreate();
3661 $sql = "UPDATE civicrm_contact SET created_date = '2012-03-01' WHERE id = " . $contact3;
3662 CRM_Core_DAO
::executeQuery($sql);
3663 $contacts = $this->callAPISuccess('contact', 'get', ['created_date' => ['<' => '2014-01-01']]);
3664 $this->assertEquals($contacts['count'], 3);
3665 $contacts = $this->callAPISuccess('contact', 'get', ['created_date' => ['>' => '2014-01-01']]);
3666 $this->assertEquals($contacts['count'], $preExistingContactCount);
3670 * CRM-14263 check that API is not affected by search profile related bug.
3672 public function testReturnCityProfile() {
3673 $contactID = $this->individualCreate();
3674 CRM_Core_Config
::singleton()->defaultSearchProfileID
= 1;
3675 $this->callAPISuccess('address', 'create', [
3676 'contact_id' => $contactID,
3677 'city' => 'Cool City',
3678 'location_type_id' => 1,
3680 $result = $this->callAPISuccess('contact', 'get', ['city' => 'Cool City', 'return' => 'contact_type']);
3681 $this->assertEquals(1, $result['count']);
3685 * CRM-15443 - ensure getlist api does not return deleted contacts.
3687 * @throws \CRM_Core_Exception
3689 public function testGetlistExcludeConditions() {
3691 $contact = $this->individualCreate(['last_name' => $name]);
3692 $this->individualCreate(['last_name' => $name, 'is_deceased' => 1]);
3693 $this->individualCreate(['last_name' => $name, 'is_deleted' => 1]);
3694 // We should get all but the deleted contact.
3695 $result = $this->callAPISuccess('contact', 'getlist', ['input' => $name]);
3696 $this->assertEquals(2, $result['count']);
3697 // Force-exclude the deceased contact.
3698 $result = $this->callAPISuccess('contact', 'getlist', [
3700 'params' => ['is_deceased' => 0],
3702 $this->assertEquals(1, $result['count']);
3703 $this->assertEquals($contact, $result['values'][0]['id']);
3707 * Test contact getactions.
3709 public function testGetActions() {
3710 $description = "Getting the available actions for an entity.";
3711 $result = $this->callAPIAndDocument($this->_entity
, 'getactions', [], __FUNCTION__
, __FILE__
, $description);
3735 foreach ($expected as $action) {
3736 $this->assertTrue(in_array($action, $result['values']), "Expected action $action");
3738 foreach ($deprecated as $action) {
3739 $this->assertArrayKeyExists($action, $result['deprecated']);
3744 * Test the duplicate check function.
3746 public function testDuplicateCheck() {
3748 'first_name' => 'Harry',
3749 'last_name' => 'Potter',
3750 'email' => 'harry@hogwarts.edu',
3751 'contact_type' => 'Individual',
3753 $this->callAPISuccess('Contact', 'create', $harry);
3754 $result = $this->callAPISuccess('Contact', 'duplicatecheck', [
3758 $this->assertEquals(1, $result['count']);
3759 $result = $this->callAPISuccess('Contact', 'duplicatecheck', [
3761 'first_name' => 'Harry',
3762 'last_name' => 'Potter',
3763 'email' => 'no5@privet.drive',
3764 'contact_type' => 'Individual',
3767 $this->assertEquals(0, $result['count']);
3768 $this->callAPIFailure('Contact', 'create', array_merge($harry, ['dupe_check' => 1]));
3772 * Test the duplicate check function.
3774 public function testDuplicateCheckRuleNotReserved() {
3776 'first_name' => 'Harry',
3777 'last_name' => 'Potter',
3778 'email' => 'harry@hogwarts.edu',
3779 'contact_type' => 'Individual',
3781 $defaultRule = $this->callAPISuccess('RuleGroup', 'getsingle', ['used' => 'Unsupervised', 'is_reserved' => 1]);
3782 $this->callAPISuccess('RuleGroup', 'create', ['id' => $defaultRule['id'], 'is_reserved' => 0]);
3783 $this->callAPISuccess('Contact', 'create', $harry);
3784 $result = $this->callAPISuccess('Contact', 'duplicatecheck', [
3788 $this->assertEquals(1, $result['count']);
3789 $this->callAPISuccess('RuleGroup', 'create', ['id' => $defaultRule['id'], 'is_reserved' => 1]);
3793 * Test variants on retrieving contact by type.
3795 public function testGetByContactType() {
3796 $individual = $this->callAPISuccess('Contact', 'create', [
3797 'email' => 'individual@test.com',
3798 'contact_type' => 'Individual',
3800 $household = $this->callAPISuccess('Contact', 'create', [
3801 'household_name' => 'household@test.com',
3802 'contact_type' => 'Household',
3804 $organization = $this->callAPISuccess('Contact', 'create', [
3805 'organization_name' => 'organization@test.com',
3806 'contact_type' => 'Organization',
3808 // Test with id - getsingle will throw an exception if not found
3809 $this->callAPISuccess('Contact', 'getsingle', [
3810 'id' => $individual['id'],
3811 'contact_type' => 'Individual',
3813 $this->callAPISuccess('Contact', 'getsingle', [
3814 'id' => $individual['id'],
3815 'contact_type' => ['IN' => ['Individual']],
3818 $this->callAPISuccess('Contact', 'getsingle', [
3819 'id' => $organization['id'],
3820 'contact_type' => ['IN' => ['Individual', 'Organization']],
3823 $result = $this->callAPISuccess('Contact', 'get', [
3824 'contact_type' => ['IN' => ['Individual', 'Organization']],
3825 'options' => ['limit' => 0],
3828 $this->assertContains($organization['id'], array_keys($result['values']));
3829 $this->assertContains($individual['id'], array_keys($result['values']));
3830 $this->assertNotContains($household['id'], array_keys($result['values']));
3832 $result = $this->callAPISuccess('Contact', 'get', [
3833 'contact_type' => 'Household',
3834 'options' => ['limit' => 0],
3837 $this->assertNotContains($organization['id'], array_keys($result['values']));
3838 $this->assertNotContains($individual['id'], array_keys($result['values']));
3839 $this->assertContains($household['id'], array_keys($result['values']));
3843 * Test merging 2 contacts.
3845 * Someone kindly bequethed us the legacy of mixed up use of main_id & other_id
3846 * in the params for contact.merge api.
3848 * This test protects that legacy.
3850 public function testMergeBizzareOldParams() {
3851 $this->createLoggedInUser();
3852 $otherContact = $this->callAPISuccess('contact', 'create', $this->_params
);
3853 $mainContact = $this->callAPISuccess('contact', 'create', $this->_params
);
3854 $this->callAPISuccess('contact', 'merge', [
3855 'main_id' => $mainContact['id'],
3856 'other_id' => $otherContact['id'],
3858 $contacts = $this->callAPISuccess('contact', 'get', $this->_params
);
3859 $this->assertEquals($otherContact['id'], $contacts['id']);
3863 * Test merging 2 contacts.
3865 public function testMerge() {
3866 $this->createLoggedInUser();
3867 $otherContact = $this->callAPISuccess('contact', 'create', $this->_params
);
3868 $retainedContact = $this->callAPISuccess('contact', 'create', $this->_params
);
3869 $this->callAPISuccess('contact', 'merge', [
3870 'to_keep_id' => $retainedContact['id'],
3871 'to_remove_id' => $otherContact['id'],
3872 'auto_flip' => FALSE,
3875 $contacts = $this->callAPISuccess('contact', 'get', $this->_params
);
3876 $this->assertEquals($retainedContact['id'], $contacts['id']);
3877 $activity = $this->callAPISuccess('Activity', 'getsingle', [
3878 'target_contact_id' => $retainedContact['id'],
3879 'activity_type_id' => 'Contact Merged',
3881 $this->assertEquals(date('Y-m-d'), date('Y-m-d', strtotime($activity['activity_date_time'])));
3882 $activity2 = $this->callAPISuccess('Activity', 'getsingle', [
3883 'target_contact_id' => $otherContact['id'],
3884 'activity_type_id' => 'Contact Deleted by Merge',
3886 $this->assertEquals($activity['id'], $activity2['parent_id']);
3887 $this->assertEquals('Normal', civicrm_api3('option_value', 'getvalue', [
3888 'value' => $activity['priority_id'],
3889 'return' => 'label',
3890 'option_group_id' => 'priority',
3896 * Test merging 2 contacts with custom fields.
3898 * @throws \API_Exception
3899 * @throws \CRM_Core_Exception
3900 * @throws \Civi\API\Exception\UnauthorizedException
3902 public function testMergeCustomFields() {
3903 $contact1 = $this->individualCreate();
3904 // Not sure this is quite right but it does get it into the file table
3905 $file = $this->callAPISuccess('Attachment', 'create', [
3906 'name' => 'header.txt',
3907 'mime_type' => 'text/plain',
3908 'description' => 'My test description',
3909 'content' => 'My test content',
3910 'entity_table' => 'civicrm_contact',
3911 'entity_id' => $contact1,
3914 $this->createCustomGroupWithFieldsOfAllTypes();
3915 $fileField = $this->getCustomFieldName('file');
3916 $linkField = $this->getCustomFieldName('link');
3917 $dateField = $this->getCustomFieldName('select_date');
3918 $selectField = $this->getCustomFieldName('select_string');
3919 $countryField = $this->getCustomFieldName('country');
3920 $multiCountryField = $this->getCustomFieldName('multi_country');
3921 $referenceField = $this->getCustomFieldName('contact_reference');
3922 $stateField = $this->getCustomFieldName('state');
3923 $multiStateField = $this->getCustomFieldName('multi_state');
3924 $booleanStateField = $this->getCustomFieldName('boolean');
3926 $countriesByName = array_flip(CRM_Core_PseudoConstant
::country(FALSE, FALSE));
3927 $statesByName = array_flip(CRM_Core_PseudoConstant
::stateProvince(FALSE, FALSE));
3928 $customFieldValues = [
3929 $fileField => $file['id'],
3930 $linkField => 'http://example.org',
3931 $dateField => '2018-01-01 17:10:56',
3932 $selectField => 'G',
3933 $countryField => $countriesByName['New Zealand'],
3934 $multiCountryField => [$countriesByName['New Zealand'], $countriesByName['Australia']],
3935 $referenceField => $this->householdCreate(),
3936 $stateField => $statesByName['Victoria'],
3937 $multiStateField => [$statesByName['Victoria'], $statesByName['Tasmania']],
3938 $booleanStateField => 1,
3940 $this->callAPISuccess('Contact', 'create', array_merge([
3942 ], $customFieldValues));
3944 $contact2 = $this->individualCreate();
3945 $this->callAPISuccess('contact', 'merge', [
3946 'to_keep_id' => $contact2,
3947 'to_remove_id' => $contact1,
3948 'auto_flip' => FALSE,
3950 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact2, 'return' => array_keys($customFieldValues)]);
3951 $this->assertEquals($contact2, CRM_Core_DAO
::singleValueQuery('SELECT entity_id FROM civicrm_entity_file WHERE file_id = ' . $file['id']));
3952 foreach ($customFieldValues as $key => $value) {
3953 $this->assertEquals($value, $contact[$key]);
3958 * Test merging a contact that is the target of a contact reference field on another contact.
3960 * @throws \API_Exception
3961 * @throws \CRM_Core_Exception
3962 * @throws \Civi\API\Exception\UnauthorizedException
3964 public function testMergeContactReferenceCustomFieldTarget() {
3965 $this->createCustomGroupWithFieldOfType([], 'contact_reference');
3966 $contact1 = $this->individualCreate();
3967 $contact2 = $this->individualCreate();
3968 $contact3 = $this->individualCreate([$this->getCustomFieldName('contact_reference') => $contact2]);
3969 $this->callAPISuccess('contact', 'merge', [
3970 'to_keep_id' => $contact1,
3971 'to_remove_id' => $contact2,
3972 'auto_flip' => FALSE,
3974 $this->assertEquals($contact1, $this->callAPISuccessGetValue('Contact', ['id' => $contact3, 'return' => $this->getCustomFieldName('contact_reference')]));
3978 * Test merging when a multiple record set is in use.
3980 * @throws \API_Exception
3981 * @throws \CRM_Core_Exception
3982 * @throws \Civi\API\Exception\UnauthorizedException
3984 public function testMergeMultipleCustomValues() {
3985 $customGroupID = $this->createCustomGroup(['is_multiple' => TRUE]);
3986 $this->ids
['CustomField']['text'] = (int) $this->createTextCustomField(['custom_group_id' => $customGroupID])['id'];
3987 $contact1 = $this->individualCreate([$this->getCustomFieldName('text') => 'blah']);
3988 $contact2 = $this->individualCreate([$this->getCustomFieldName('text') => 'de blah']);
3989 $this->callAPISuccess('contact', 'merge', [
3990 'to_keep_id' => $contact1,
3991 'to_remove_id' => $contact2,
3992 'auto_flip' => FALSE,
3994 $column = $this->getCustomFieldColumnName('text');
3995 $table = $this->getCustomGroupTable();
3996 $this->assertEquals('blah,de blah', CRM_Core_DAO
::singleValueQuery(
3997 "SELECT GROUP_CONCAT({$column}) FROM $table WHERE entity_id = $contact1"
4002 * Test retrieving merged contacts.
4004 * The goal here is to start with a contact deleted by merged and find out the contact that is the current version of them.
4006 * @throws \CRM_Core_Exception
4008 public function testMergedGet() {
4009 $this->contactIDs
[] = $this->individualCreate();
4010 $this->contactIDs
[] = $this->individualCreate();
4011 $this->contactIDs
[] = $this->individualCreate();
4012 $this->contactIDs
[] = $this->individualCreate();
4014 // First do an 'unnatural merge' - they 'like to merge into the lowest but this will mean that contact 0 merged to contact [3].
4015 // When the batch merge runs.... the new lowest contact is contact[1]. All contacts will merge into that contact,
4016 // including contact[3], resulting in only 3 existing at the end. For each contact the correct answer to 'who did I eventually
4017 // wind up being should be [1]
4018 $this->callAPISuccess('Contact', 'merge', ['to_remove_id' => $this->contactIDs
[0], 'to_keep_id' => $this->contactIDs
[3]]);
4020 $this->callAPISuccess('Job', 'process_batch_merge', []);
4021 foreach ($this->contactIDs
as $contactID) {
4022 if ($contactID === $this->contactIDs
[1]) {
4025 $result = $this->callAPIAndDocument('Contact', 'getmergedto', ['sequential' => 1, 'contact_id' => $contactID], __FUNCTION__
, __FILE__
);
4026 $this->assertEquals(1, $result['count']);
4027 $this->assertEquals($this->contactIDs
[1], $result['values'][0]['id']);
4030 $result = $this->callAPIAndDocument('Contact', 'getmergedfrom', ['contact_id' => $this->contactIDs
[1]], __FUNCTION__
, __FILE__
)['values'];
4031 $mergedContactIds = array_merge(array_diff($this->contactIDs
, [$this->contactIDs
[1]]));
4032 $this->assertEquals($mergedContactIds, array_keys($result));
4036 * Test merging 2 contacts with delete to trash off.
4038 * We are checking that there is no error due to attempting to add an activity for the
4043 public function testMergeNoTrash() {
4044 $this->createLoggedInUser();
4045 $this->callAPISuccess('Setting', 'create', ['contact_undelete' => FALSE]);
4046 $otherContact = $this->callAPISuccess('contact', 'create', $this->_params
);
4047 $retainedContact = $this->callAPISuccess('contact', 'create', $this->_params
);
4048 $this->callAPISuccess('contact', 'merge', [
4049 'to_keep_id' => $retainedContact['id'],
4050 'to_remove_id' => $otherContact['id'],
4051 'auto_flip' => FALSE,
4053 $this->callAPISuccess('Setting', 'create', ['contact_undelete' => TRUE]);
4057 * Ensure format with return=group shows comma-separated group IDs.
4061 * @throws \CRM_Core_Exception
4063 public function testContactGetReturnGroup() {
4064 // Set up a contact, asser that they were created.
4066 'contact_type' => 'Individual',
4067 'first_name' => 'Test',
4068 'last_name' => 'Groupmember',
4069 'email' => 'test@example.org',
4071 $create_contact = $this->callApiSuccess('Contact', 'create', $contact_params);
4072 $this->assertEquals(0, $create_contact['is_error']);
4073 $this->assertInternalType('int', $create_contact['id']);
4075 $created_contact_id = $create_contact['id'];
4077 // Set up multiple groups, add the contact to the groups.
4078 $test_groups = ['Test group A', 'Test group B'];
4079 foreach ($test_groups as $title) {
4080 // Use this contact as group owner, since we know they exist.
4083 'created_id' => $created_contact_id,
4085 $create_group = $this->callApiSuccess('Group', 'create', $group_params);
4086 $this->assertEquals(0, $create_group['is_error']);
4087 $this->assertInternalType('int', $create_group['id']);
4089 $created_group_ids[] = $create_group['id'];
4091 // Add contact to the new group.
4092 $group_contact_params = [
4093 'contact_id' => $created_contact_id,
4094 'group_id' => $create_group['id'],
4096 $create_group_contact = $this->callApiSuccess('GroupContact', 'create', $group_contact_params);
4097 $this->assertEquals(0, $create_group_contact['is_error']);
4098 $this->assertInternalType('int', $create_group_contact['added']);
4101 // Use the Contact,get API to retrieve the contact
4102 $contact_get_params = [
4103 'id' => $created_contact_id,
4104 'return' => 'group',
4106 $contact_get = $this->callApiSuccess('Contact', 'get', $contact_get_params);
4107 $this->assertInternalType('array', $contact_get['values'][$created_contact_id]);
4108 $this->assertInternalType('string', $contact_get['values'][$created_contact_id]['groups']);
4110 // Ensure they are shown as being in each created group.
4111 $contact_group_ids = explode(',', $contact_get['values'][$created_contact_id]['groups']);
4112 foreach ($created_group_ids as $created_group_id) {
4113 $this->assertContains($created_group_id, $contact_group_ids);
4118 * CRM-20144 Verify that passing title of group works as well as id
4119 * Tests the following formats
4120 * contact.get group='title1'
4121 * contact.get group=id1
4123 public function testContactGetWithGroupTitle() {
4124 // Set up a contact, asser that they were created.
4126 'contact_type' => 'Individual',
4127 'first_name' => 'Test2',
4128 'last_name' => 'Groupmember',
4129 'email' => 'test@example.org',
4131 $create_contact = $this->callApiSuccess('Contact', 'create', $contact_params);
4132 $created_contact_id = $create_contact['id'];
4133 // Set up multiple groups, add the contact to the groups.
4134 $test_groups = ['Test group C', 'Test group D'];
4135 foreach ($test_groups as $title) {
4138 'created_id' => $created_contact_id,
4140 $create_group = $this->callApiSuccess('Group', 'create', $group_params);
4141 $created_group_id = $create_group['id'];
4143 // Add contact to the new group.
4144 $group_contact_params = [
4145 'contact_id' => $created_contact_id,
4146 'group_id' => $create_group['id'],
4148 $this->callApiSuccess('GroupContact', 'create', $group_contact_params);
4149 $contact_get = $this->callAPISuccess('contact', 'get', ['group' => $title, 'return' => 'group']);
4150 $this->assertEquals(1, $contact_get['count']);
4151 $this->assertEquals($created_contact_id, $contact_get['id']);
4152 $contact_groups = explode(',', $contact_get['values'][$created_contact_id]['groups']);
4153 $this->assertContains((string) $create_group['id'], $contact_groups);
4154 $contact_get2 = $this->callAPISuccess('contact', 'get', ['group' => $created_group_id, 'return' => 'group']);
4155 $this->assertEquals($created_contact_id, $contact_get2['id']);
4156 $contact_groups2 = explode(',', $contact_get2['values'][$created_contact_id]['groups']);
4157 $this->assertContains((string) $create_group['id'], $contact_groups2);
4158 $this->callAPISuccess('group', 'delete', ['id' => $created_group_id]);
4160 $this->callAPISuccess('contact', 'delete', ['id' => $created_contact_id, 'skip_undelete' => TRUE]);
4164 * CRM-20144 Verify that passing title of group works as well as id
4165 * Tests the following formats
4166 * contact.get group=array('title1', title1)
4167 * contact.get group=array('IN' => array('title1', 'title2)
4169 public function testContactGetWithGroupTitleMultipleGroups() {
4170 $description = "Get all from group and display contacts.";
4171 $subFile = "GroupFilterUsingContactAPI";
4172 // Set up a contact, asser that they were created.
4174 'contact_type' => 'Individual',
4175 'first_name' => 'Test2',
4176 'last_name' => 'Groupmember',
4177 'email' => 'test@example.org',
4179 $create_contact = $this->callApiSuccess('Contact', 'create', $contact_params);
4180 $created_contact_id = $create_contact['id'];
4181 $createdGroupsTitles = $createdGroupsIds = [];
4182 // Set up multiple groups, add the contact to the groups.
4183 $test_groups = ['Test group C', 'Test group D'];
4184 foreach ($test_groups as $title) {
4187 'created_id' => $created_contact_id,
4189 $create_group = $this->callApiSuccess('Group', 'create', $group_params);
4190 $created_group_id = $create_group['id'];
4191 $createdGroupsIds[] = $create_group['id'];
4192 $createdGroupTitles[] = $title;
4193 // Add contact to the new group.
4194 $group_contact_params = [
4195 'contact_id' => $created_contact_id,
4196 'group_id' => $create_group['id'],
4198 $create_group_contact = $this->callApiSuccess('GroupContact', 'create', $group_contact_params);
4200 $contact_get = $this->callAPISuccess('contact', 'get', ['group' => $createdGroupTitles, 'return' => 'group']);
4201 $this->assertEquals(1, $contact_get['count']);
4202 $this->assertEquals($created_contact_id, $contact_get['id']);
4203 $contact_groups = explode(',', $contact_get['values'][$created_contact_id]['groups']);
4204 foreach ($createdGroupsIds as $id) {
4205 $this->assertContains((string) $id, $contact_groups);
4207 $contact_get2 = $this->callAPIAndDocument('contact', 'get', ['group' => ['IN' => $createdGroupTitles]], __FUNCTION__
, __FILE__
, $description, $subFile);
4208 $contact_get2 = $this->callAPISuccess('contact', 'get', ['group' => ['IN' => $createdGroupTitles], 'return' => 'group']);
4209 $this->assertEquals($created_contact_id, $contact_get2['id']);
4210 $contact_groups2 = explode(',', $contact_get2['values'][$created_contact_id]['groups']);
4211 foreach ($createdGroupsIds as $id) {
4212 $this->assertContains((string) $id, $contact_groups2);
4214 foreach ($createdGroupsIds as $id) {
4215 $this->callAPISuccess('group', 'delete', ['id' => $id]);
4217 $this->callAPISuccess('contact', 'delete', ['id' => $created_contact_id, 'skip_undelete' => TRUE]);
4221 * CRM-20144 Verify that passing title of group works as well as id
4222 * Tests the following formats
4223 * contact.get group=array('title1' => 1)
4224 * contact.get group=array('titke1' => 1, 'title2' => 1)
4225 * contact.get group=array('id1' => 1)
4226 * contact.get group=array('id1' => 1, id2 => 1)
4228 public function testContactGetWithGroupTitleMultipleGroupsLegacyFormat() {
4229 // Set up a contact, asser that they were created.
4231 'contact_type' => 'Individual',
4232 'first_name' => 'Test2',
4233 'last_name' => 'Groupmember',
4234 'email' => 'test@example.org',
4236 $create_contact = $this->callApiSuccess('Contact', 'create', $contact_params);
4237 $created_contact_id = $create_contact['id'];
4238 $createdGroupsTitles = $createdGroupsIds = [];
4239 // Set up multiple groups, add the contact to the groups.
4240 $test_groups = ['Test group C', 'Test group D'];
4241 foreach ($test_groups as $title) {
4244 'created_id' => $created_contact_id,
4246 $create_group = $this->callApiSuccess('Group', 'create', $group_params);
4247 $created_group_id = $create_group['id'];
4248 $createdGroupsIds[] = $create_group['id'];
4249 $createdGroupTitles[] = $title;
4250 // Add contact to the new group.
4251 $group_contact_params = [
4252 'contact_id' => $created_contact_id,
4253 'group_id' => $create_group['id'],
4255 $create_group_contact = $this->callApiSuccess('GroupContact', 'create', $group_contact_params);
4257 $contact_get = $this->callAPISuccess('contact', 'get', ['group' => [$createdGroupTitles[0] => 1], 'return' => 'group']);
4258 $this->assertEquals(1, $contact_get['count']);
4259 $this->assertEquals($created_contact_id, $contact_get['id']);
4260 $contact_groups = explode(',', $contact_get['values'][$created_contact_id]['groups']);
4261 foreach ($createdGroupsIds as $id) {
4262 $this->assertContains((string) $id, $contact_groups);
4264 $contact_get2 = $this->callAPISuccess('contact', 'get', ['group' => [$createdGroupTitles[0] => 1, $createdGroupTitles[1] => 1], 'return' => 'group']);
4265 $this->assertEquals(1, $contact_get2['count']);
4266 $this->assertEquals($created_contact_id, $contact_get2['id']);
4267 $contact_groups2 = explode(',', $contact_get2['values'][$created_contact_id]['groups']);
4268 foreach ($createdGroupsIds as $id) {
4269 $this->assertContains((string) $id, $contact_groups2);
4271 $contact_get3 = $this->callAPISuccess('contact', 'get', ['group' => [$createdGroupsIds[0] => 1], 'return' => 'group']);
4272 $this->assertEquals($created_contact_id, $contact_get3['id']);
4273 $contact_groups3 = explode(',', $contact_get3['values'][$created_contact_id]['groups']);
4274 foreach ($createdGroupsIds as $id) {
4275 $this->assertContains((string) $id, $contact_groups3);
4277 $contact_get4 = $this->callAPISuccess('contact', 'get', ['group' => [$createdGroupsIds[0] => 1, $createdGroupsIds[1] => 1], 'return' => 'group']);
4278 $this->assertEquals($created_contact_id, $contact_get4['id']);
4279 $contact_groups4 = explode(',', $contact_get4['values'][$created_contact_id]['groups']);
4280 foreach ($createdGroupsIds as $id) {
4281 $this->assertContains((string) $id, $contact_groups4);
4283 foreach ($createdGroupsIds as $id) {
4284 $this->callAPISuccess('group', 'delete', ['id' => $id]);
4286 $this->callAPISuccess('contact', 'delete', ['id' => $created_contact_id, 'skip_undelete' => TRUE]);
4290 * Test the prox_distance functionality works.
4292 * This is primarily testing functionality in the BAO_Query object that 'happens to be'
4293 * accessible via the api.
4295 public function testContactGetProximity() {
4296 CRM_Core_Config
::singleton()->geocodeMethod
= 'CRM_Utils_MockGeocoder';
4297 $this->individualCreate();
4298 $contactID = $this->individualCreate();
4299 $this->callAPISuccess('Address', 'create', [
4300 'contact_id' => $contactID,
4302 'city' => 'Whangarei',
4303 'street_address' => 'Dent St',
4304 'geo_code_1' => '-35.8743325',
4305 'geo_code_2' => '174.4567136',
4306 'location_type_id' => 'Home',
4308 $contact = $this->callAPISuccess('Contact', 'get', [
4309 'prox_distance' => 100,
4310 'prox_geo_code_1' => '-35.72192',
4311 'prox_geo_code_2' => '174.32034',
4313 $this->assertEquals(1, $contact['count']);
4314 $this->assertEquals($contactID, $contact['id']);
4317 public function testLoggedInUserAPISupportToken() {
4318 $description = "Get contact id of the current logged in user";
4319 $subFile = "ContactIDOfLoggedInUserContactAPI";
4320 $cid = $this->createLoggedInUser();
4321 $contact = $this->callAPIAndDocument('contact', 'get', ['id' => 'user_contact_id'], __FUNCTION__
, __FILE__
, $description, $subFile);
4322 $this->assertEquals($cid, $contact['id']);
4329 protected function putGroupContactCacheInClearableState($groupID, $contact) {
4330 // We need to force the situation where there is invalid data in the cache and it
4331 // is due to be cleared.
4332 CRM_Core_DAO
::executeQuery("
4333 INSERT INTO civicrm_group_contact_cache (group_id, contact_id)
4334 VALUES ({$groupID}, {$contact['id']})
4336 CRM_Core_DAO
::executeQuery("UPDATE civicrm_group SET cache_date = '2017-01-01'");
4337 // Reset so it does not skip.
4338 Civi
::$statics['CRM_Contact_BAO_GroupContactCache']['is_refresh_init'] = FALSE;
4342 * CRM-21041 Test if 'communication style' is set to site default if not passed.
4344 * @param int $version
4346 * @dataProvider versionThreeAndFour
4347 * @throws \CRM_Core_Exception
4349 public function testCreateCommunicationStyleUnset($version) {
4350 $this->_apiversion
= $version;
4351 $this->callAPISuccess('Contact', 'create', [
4352 'first_name' => 'John',
4353 'last_name' => 'Doe',
4354 'contact_type' => 'Individual',
4356 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Doe']);
4357 $this->assertEquals(1, $result['communication_style_id']);
4361 * CRM-21041 Test if 'communication style' is set if value is passed.
4363 * @throws \CRM_Core_Exception
4364 * @throws \CiviCRM_API3_Exception
4366 public function testCreateCommunicationStylePassed() {
4367 $this->callAPISuccess('Contact', 'create', [
4368 'first_name' => 'John',
4369 'last_name' => 'Doe',
4370 'contact_type' => 'Individual',
4371 'communication_style_id' => 'Familiar',
4373 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Doe']);
4375 'option_group_id' => 'communication_style',
4376 'label' => 'Familiar',
4377 'return' => 'value',
4379 $optionResult = civicrm_api3('OptionValue', 'get', $params);
4380 $communicationStyle = reset($optionResult['values']);
4381 $this->assertEquals($communicationStyle['value'], $result['communication_style_id']);
4385 * Test that creating a contact with various contact greetings works.
4387 * @param int $version
4389 * @dataProvider versionThreeAndFour
4390 * @throws \CRM_Core_Exception
4392 public function testContactGreetingsCreate($version) {
4393 $this->_apiversion
= $version;
4394 // Api v4 takes a return parameter like postal_greeting_display which matches the field.
4395 // v3 has a customised parameter 'postal_greeting'. The v4 parameter is more correct so
4396 // we will not change it to match v3. The keyString value allows the test to support both.
4397 $keyString = $version === 4 ?
'_display' : '';
4399 $contact = $this->callAPISuccess('Contact', 'create', ['first_name' => 'Alan', 'last_name' => 'MouseMouse', 'contact_type' => 'Individual']);
4400 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => 'postal_greeting' . $keyString]);
4401 $this->assertEquals('Dear Alan', $contact['postal_greeting_display']);
4403 $contact = $this->callAPISuccess('Contact', 'create', ['id' => $contact['id'], 'postal_greeting_id' => 2]);
4404 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => 'postal_greeting' . $keyString]);
4405 $this->assertEquals('Dear Alan MouseMouse', $contact['postal_greeting_display']);
4407 $contact = $this->callAPISuccess('Contact', 'create', ['organization_name' => 'Alan\'s Show', 'contact_type' => 'Organization']);
4408 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => "postal_greeting{$keyString}, addressee{$keyString}, email_greeting{$keyString}"]);
4409 $this->assertEquals('', $contact['postal_greeting_display']);
4410 $this->assertEquals('', $contact['email_greeting_display']);
4411 $this->assertEquals('Alan\'s Show', $contact['addressee_display']);
4415 * Test that creating a contact with various contact greetings works.
4417 * @param int $version
4419 * @dataProvider versionThreeAndFour
4421 * @throws \CRM_Core_Exception
4423 public function testContactGreetingsCreateWithCustomField($version) {
4424 $this->_apiversion
= $version;
4425 // Api v4 takes a return parameter like postal_greeting_display which matches the field.
4426 // v3 has a customised parameter 'postal_greeting'. The v4 parameter is more correct so
4427 // we will not change it to match v3. The keyString value allows the test to support both.
4428 $keyString = $version === 4 ?
'_display' : '';
4430 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
4431 $contact = $this->callAPISuccess('Contact', 'create', ['first_name' => 'Alan', 'contact_type' => 'Individual', 'custom_' . $ids['custom_field_id'] => 'Mice']);
4433 // Change postal greeting to involve a custom field.
4434 $postalOption = $this->callAPISuccessGetSingle('OptionValue', ['option_group_id' => 'postal_greeting', 'filter' => 1, 'is_default' => 1]);
4435 $this->callAPISuccess('OptionValue', 'create', [
4436 'id' => $postalOption['id'],
4437 'name' => 'Dear {contact.first_name} {contact.custom_' . $ids['custom_field_id'] . '}',
4438 'label' => 'Dear {contact.first_name} {contact.custom_' . $ids['custom_field_id'] . '}',
4441 // Update contact & see if postal greeting now reflects the new string.
4442 $this->callAPISuccess('Contact', 'create', ['id' => $contact['id'], 'last_name' => 'MouseyMousey']);
4443 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => 'postal_greeting' . $keyString]);
4444 $this->assertEquals('Dear Alan Mice', $contact['postal_greeting_display']);
4446 // Set contact to have no postal greeting & check it is correct.
4447 $this->callAPISuccess('Contact', 'create', ['id' => $contact['id'], 'postal_greeting_id' => 'null']);
4448 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => 'postal_greeting' . $keyString]);
4449 $this->assertEquals('', $contact['postal_greeting_display']);
4452 $this->callAPISuccess('OptionValue', 'create', ['id' => $postalOption['id'], 'name' => 'Dear {contact.first_name}']);
4453 $this->customFieldDelete($ids['custom_field_id']);
4454 $this->customGroupDelete($ids['custom_group_id']);
4458 * Test that smarty variables are parsed if they exist in the greeting template.
4460 * In this test we have both a Civi token & a Smarty token and we check both are processed.
4462 * @param int $version
4464 * @dataProvider versionThreeAndFour
4465 * @throws \CRM_Core_Exception
4467 public function testGreetingParseSmarty($version) {
4468 $this->_apiversion
= $version;
4469 // Api v4 takes a return parameter like postal_greeting_display which matches the field.
4470 // v3 has a customised parameter 'postal_greeting'. The v4 parameter is more correct so
4471 // we will not change it to match v3. The keyString value allows the test to support both.
4472 $keyString = $version === 4 ?
'_display' : '';
4473 $postalOption = $this->callAPISuccessGetSingle('OptionValue', ['option_group_id' => 'postal_greeting', 'filter' => 1, 'is_default' => 1]);
4474 $this->callAPISuccess('OptionValue', 'create', [
4475 'id' => $postalOption['id'],
4476 'name' => "Dear {contact.first_name} {if \'{contact.first_name}\' === \'Tim\'}The Wise{/if}",
4477 'label' => "Dear {contact.first_name} {if '{contact.first_name}' === 'Tim'} The Wise{/if}",
4479 $contactID = $this->individualCreate();
4480 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contactID, 'return' => 'postal_greeting' . $keyString]);
4481 $this->assertEquals('Dear Anthony', $contact['postal_greeting_display']);
4483 $this->callAPISuccess('Contact', 'create', ['id' => $contactID, 'first_name' => 'Tim']);
4484 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contactID, 'return' => 'postal_greeting' . $keyString]);
4485 $this->assertEquals('Dear Tim The Wise', $contact['postal_greeting_display']);
4489 * Test getunique api call for Contact entity
4491 public function testContactGetUnique() {
4492 $result = $this->callAPIAndDocument($this->_entity
, 'getunique', [], __FUNCTION__
, __FILE__
);
4493 $this->assertEquals(1, $result['count']);
4494 $this->assertEquals(['external_identifier'], $result['values']['UI_external_identifier']);
4498 * API test to retrieve contact from group having different group title and name.
4500 public function testContactGetFromGroup() {
4501 $groupId = $this->groupCreate([
4502 'name' => 'Test_Group',
4504 'title' => 'New Test Group Created',
4505 'description' => 'New Test Group Created',
4507 'visibility' => 'User and User Admin Only',
4509 $contact = $this->callAPISuccess('contact', 'create', $this->_params
);
4510 $groupContactCreateParams = [
4511 'contact_id' => $contact['id'],
4512 'group_id' => $groupId,
4513 'status' => 'Pending',
4515 $groupContact = $this->callAPISuccess('groupContact', 'create', $groupContactCreateParams);
4516 $groupGetContact = $this->CallAPISuccess('groupContact', 'get', $groupContactCreateParams);
4517 $this->CallAPISuccess('Contact', 'getcount', [
4518 'group' => "Test_Group",
4523 * Test the related contacts filter.
4525 * @throws \Exception
4527 public function testSmartGroupsForRelatedContacts() {
4528 $rtype1 = $this->callAPISuccess('relationship_type', 'create', [
4529 "name_a_b" => uniqid() . " Child of",
4530 "name_b_a" => uniqid() . " Parent of",
4532 $rtype2 = $this->callAPISuccess('relationship_type', 'create', [
4533 "name_a_b" => uniqid() . " Household Member of",
4534 "name_b_a" => uniqid() . " Household Member is",
4536 $h1 = $this->householdCreate();
4537 $c1 = $this->individualCreate(['last_name' => 'Adams']);
4538 $c2 = $this->individualCreate(['last_name' => 'Adams']);
4539 $this->callAPISuccess('relationship', 'create', [
4540 'contact_id_a' => $c1,
4541 'contact_id_b' => $c2,
4544 'relationship_type_id' => $rtype1['id'],
4546 $this->callAPISuccess('relationship', 'create', [
4547 'contact_id_a' => $c1,
4548 'contact_id_b' => $h1,
4550 // Household Member of
4551 'relationship_type_id' => $rtype2['id'],
4553 $this->callAPISuccess('relationship', 'create', [
4554 'contact_id_a' => $c2,
4555 'contact_id_b' => $h1,
4557 // Household Member of
4558 'relationship_type_id' => $rtype2['id'],
4564 'display_relationship_type' => $rtype1['id'] . '_a_b',
4565 'sort_name' => 'Adams',
4568 $g1ID = $this->smartGroupCreate($ssParams, ['name' => uniqid(), 'title' => uniqid()]);
4571 // Household Member of
4572 'display_relationship_type' => $rtype2['id'] . '_a_b',
4575 $g2ID = $this->smartGroupCreate($ssParams, ['name' => uniqid(), 'title' => uniqid()]);
4578 // Household Member is
4579 'display_relationship_type' => $rtype2['id'] . '_b_a',
4582 // the reverse of g2 which adds another layer for overlap at related contact filter
4583 $g3ID = $this->smartGroupCreate($ssParams, ['name' => uniqid(), 'title' => uniqid()]);
4584 CRM_Contact_BAO_GroupContactCache
::loadAll();
4585 $this->callAPISuccessGetCount('contact', ['group' => $g1ID], 1);
4586 $this->callAPISuccessGetCount('contact', ['group' => $g2ID], 2);
4587 $this->callAPISuccessGetCount('contact', ['group' => $g3ID], 1);
4591 * Test creating a note from the contact.create API call when only passing the note as a string.
4593 public function testCreateNoteinCreate() {
4594 $loggedInContactID = $this->createLoggedInUser();
4595 $this->_params
['note'] = "Test note created by API Call as a String";
4596 $contact = $this->callAPISuccess('Contact', 'create', $this->_params
);
4597 $note = $this->callAPISuccess('Note', 'get', ['contact_id' => $loggedInContactID]);
4598 $this->assertEquals($note['values'][$note['id']]['note'], "Test note created by API Call as a String");
4599 $note = $this->callAPISuccess('Note', 'get', ['entity_id' => $contact['id']]);
4600 $this->assertEquals($note['values'][$note['id']]['note'], "Test note created by API Call as a String");
4601 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id'], 'skip_undelete' => TRUE]);
4605 * Test Creating a note from the contact.create api call when passing the note params as an array.
4607 public function testCreateNoteinCreateArrayFormat() {
4608 $contact1 = $this->callAPISuccess('Contact', 'create', ['first_name' => 'Alan', 'last_name' => 'MouseMouse', 'contact_type' => 'Individual']);
4609 $this->_params
['note'] = [['note' => "Test note created by API Call as array", 'contact_id' => $contact1['id']]];
4610 $contact2 = $this->callAPISuccess('Contact', 'create', $this->_params
);
4611 $note = $this->callAPISuccess('Note', 'get', ['contact_id' => $contact1['id']]);
4612 $this->assertEquals($note['values'][$note['id']]['note'], "Test note created by API Call as array");
4613 $note = $this->callAPISuccess('Note', 'get', ['entity_id' => $contact2['id']]);
4614 $this->assertEquals($note['values'][$note['id']]['note'], "Test note created by API Call as array");
4618 * Verify that passing tag IDs to Contact.get works
4620 * Tests the following formats
4621 * - Contact.get tag='id1'
4622 * - Contact.get tag='id1,id2'
4623 * - Contact.get tag='id1, id2'
4625 public function testContactGetWithTag() {
4626 $contact = $this->callApiSuccess('Contact', 'create', [
4627 'contact_type' => 'Individual',
4628 'first_name' => 'Test',
4629 'last_name' => 'Tagged',
4630 'email' => 'test@example.org',
4633 foreach (['Tag A', 'Tag B'] as $name) {
4634 $tags[] = $this->callApiSuccess('Tag', 'create', [
4639 // assign contact to "Tag B"
4640 $this->callApiSuccess('EntityTag', 'create', [
4641 'entity_table' => 'civicrm_contact',
4642 'entity_id' => $contact['id'],
4643 'tag_id' => $tags[1]['id'],
4646 // test format Contact.get tag='id1'
4647 $contact_get = $this->callAPISuccess('Contact', 'get', [
4648 'tag' => $tags[1]['id'],
4651 $this->assertEquals(1, $contact_get['count']);
4652 $this->assertEquals($contact['id'], $contact_get['id']);
4653 $this->assertEquals('Tag B', $contact_get['values'][$contact['id']]['tags']);
4655 // test format Contact.get tag='id1,id2'
4656 $contact_get = $this->callAPISuccess('Contact', 'get', [
4657 'tag' => $tags[0]['id'] . ',' . $tags[1]['id'],
4660 $this->assertEquals(1, $contact_get['count']);
4661 $this->assertEquals($contact['id'], $contact_get['id']);
4662 $this->assertEquals('Tag B', $contact_get['values'][$contact['id']]['tags']);
4664 // test format Contact.get tag='id1, id2'
4665 $contact_get = $this->callAPISuccess('Contact', 'get', [
4666 'tag' => $tags[0]['id'] . ', ' . $tags[1]['id'],
4669 $this->assertEquals(1, $contact_get['count']);
4670 $this->assertEquals($contact['id'], $contact_get['id']);
4671 $this->assertEquals('Tag B', $contact_get['values'][$contact['id']]['tags']);
4673 foreach ($tags as $tag) {
4674 $this->callAPISuccess('Tag', 'delete', ['id' => $tag['id']]);
4676 $this->callAPISuccess('Contact', 'delete', [
4677 'id' => $contact['id'],
4678 'skip_undelete' => TRUE,
4683 * Create pair of contacts with multiple conflicts.
4687 * @throws \CRM_Core_Exception
4689 protected function createDeeplyConflictedContacts(): array {
4690 $this->createCustomGroupWithFieldOfType();
4691 $contact1 = $this->individualCreate([
4692 'email' => 'bob@example.com',
4693 'api.address.create' => ['location_type_id' => 'work', 'street_address' => 'big office', 'city' => 'small city'],
4694 'api.address.create.2' => ['location_type_id' => 'home', 'street_address' => 'big house', 'city' => 'small city'],
4695 'external_identifier' => 'unique and special',
4696 $this->getCustomFieldName('text') => 'mummy loves me',
4698 $contact2 = $this->individualCreate([
4699 'first_name' => 'different',
4700 'api.address.create.1' => ['location_type_id' => 'home', 'street_address' => 'medium house', 'city' => 'small city'],
4701 'api.address.create.2' => ['location_type_id' => 'work', 'street_address' => 'medium office', 'city' => 'small city'],
4702 'external_identifier' => 'uniquer and specialler',
4703 'api.email.create' => ['location_type_id' => 'Other', 'email' => 'bob@example.com'],
4704 $this->getCustomFieldName('text') => 'mummy loves me more',
4706 return [$contact1, $contact2];