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
686 * @throws \CRM_Core_Exception
688 public function testCreateWithCustom($version) {
689 $this->_apiversion
= $version;
690 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
692 $params = $this->_params
;
693 $params['custom_' . $ids['custom_field_id']] = "custom string";
694 $description = 'This demonstrates setting a custom field through the API.';
695 $result = $this->callAPIAndDocument($this->_entity
, 'create', $params, __FUNCTION__
, __FILE__
, $description);
697 $check = $this->callAPISuccess($this->_entity
, 'get', [
698 'return.custom_' . $ids['custom_field_id'] => 1,
699 'id' => $result['id'],
701 $this->assertEquals("custom string", $check['values'][$check['id']]['custom_' . $ids['custom_field_id']]);
703 $this->customFieldDelete($ids['custom_field_id']);
704 $this->customGroupDelete($ids['custom_group_id']);
708 * CRM-12773 - expectation is that civicrm quietly ignores fields without values.
710 * @throws \CRM_Core_Exception
712 public function testCreateWithNULLCustomCRM12773() {
713 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
714 $params = $this->_params
;
715 $params['custom_' . $ids['custom_field_id']] = NULL;
716 $this->callAPISuccess('contact', 'create', $params);
717 $this->customFieldDelete($ids['custom_field_id']);
718 $this->customGroupDelete($ids['custom_group_id']);
722 * CRM-14232 test preferred language set to site default if not passed.
724 * @param int $version
726 * @dataProvider versionThreeAndFour
728 * @throws \CRM_Core_Exception
730 public function testCreatePreferredLanguageUnset($version) {
731 $this->_apiversion
= $version;
732 $this->callAPISuccess('Contact', 'create', [
733 'first_name' => 'Snoop',
734 'last_name' => 'Dog',
735 'contact_type' => 'Individual',
737 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Dog']);
738 $this->assertEquals('en_US', $result['preferred_language']);
742 * CRM-14232 test preferred language returns setting if not passed.
744 * @param int $version
746 * @dataProvider versionThreeAndFour
748 * @throws \CRM_Core_Exception
750 public function testCreatePreferredLanguageSet($version) {
751 $this->_apiversion
= $version;
752 $this->callAPISuccess('Setting', 'create', ['contact_default_language' => 'fr_FR']);
753 $this->callAPISuccess('Contact', 'create', [
754 'first_name' => 'Snoop',
755 'last_name' => 'Dog',
756 'contact_type' => 'Individual',
758 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Dog']);
759 $this->assertEquals('fr_FR', $result['preferred_language']);
763 * CRM-14232 test preferred language returns setting if not passed where setting is NULL.
766 * @throws \CRM_Core_Exception
768 public function testCreatePreferredLanguageNull() {
769 $this->callAPISuccess('Setting', 'create', ['contact_default_language' => 'null']);
770 $this->callAPISuccess('Contact', 'create', [
771 'first_name' => 'Snoop',
772 'last_name' => 'Dog',
773 'contact_type' => 'Individual',
775 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Dog']);
776 $this->assertEquals(NULL, $result['preferred_language']);
780 * CRM-14232 test preferred language returns setting if not passed where setting is NULL.
782 * @param int $version
784 * @dataProvider versionThreeAndFour
785 * @throws \CRM_Core_Exception
787 public function testCreatePreferredLanguagePassed($version) {
788 $this->_apiversion
= $version;
789 $this->callAPISuccess('Setting', 'create', ['contact_default_language' => 'null']);
790 $this->callAPISuccess('Contact', 'create', [
791 'first_name' => 'Snoop',
792 'last_name' => 'Dog',
793 'contact_type' => 'Individual',
794 'preferred_language' => 'en_AU',
796 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Dog']);
797 $this->assertEquals('en_AU', $result['preferred_language']);
801 * CRM-15792 - create/update datetime field for contact.
803 * @throws \CRM_Core_Exception
805 public function testCreateContactCustomFldDateTime() {
806 $customGroup = $this->customGroupCreate(['extends' => 'Individual', 'title' => 'datetime_test_group']);
807 $dateTime = CRM_Utils_Date
::currentDBDate();
808 //check date custom field is saved along with time when time_format is set
810 'first_name' => 'abc3',
811 'last_name' => 'xyz3',
812 'contact_type' => 'Individual',
813 'email' => 'man3@yahoo.com',
814 'api.CustomField.create' => [
815 'custom_group_id' => $customGroup['id'],
816 'name' => 'test_datetime',
817 'label' => 'Demo Date',
818 'html_type' => 'Select Date',
819 'data_type' => 'Date',
823 'is_searchable' => 0,
828 $result = $this->callAPISuccess('Contact', 'create', $params);
829 $customFldId = $result['values'][$result['id']]['api.CustomField.create']['id'];
830 $this->assertNotNull($result['id']);
831 $this->assertNotNull($customFldId);
834 'id' => $result['id'],
835 "custom_{$customFldId}" => $dateTime,
836 'api.CustomValue.get' => 1,
839 $result = $this->callAPISuccess('Contact', 'create', $params);
840 $this->assertNotNull($result['id']);
841 $customFldDate = date("YmdHis", strtotime($result['values'][$result['id']]['api.CustomValue.get']['values'][0]['latest']));
842 $this->assertNotNull($customFldDate);
843 $this->assertEquals($dateTime, $customFldDate);
844 $customValueId = $result['values'][$result['id']]['api.CustomValue.get']['values'][0]['id'];
845 $dateTime = date('Ymd');
846 //date custom field should not contain time part when time_format is null
848 'id' => $result['id'],
849 'api.CustomField.create' => [
850 'id' => $customFldId,
851 'html_type' => 'Select Date',
852 'data_type' => 'Date',
855 'api.CustomValue.create' => [
856 'id' => $customValueId,
857 'entity_id' => $result['id'],
858 "custom_{$customFldId}" => $dateTime,
860 'api.CustomValue.get' => 1,
862 $result = $this->callAPISuccess('Contact', 'create', $params);
863 $this->assertNotNull($result['id']);
864 $customFldDate = date("Ymd", strtotime($result['values'][$result['id']]['api.CustomValue.get']['values'][0]['latest']));
865 $customFldTime = date("His", strtotime($result['values'][$result['id']]['api.CustomValue.get']['values'][0]['latest']));
866 $this->assertNotNull($customFldDate);
867 $this->assertEquals($dateTime, $customFldDate);
868 $this->assertEquals(000000, $customFldTime);
869 $this->callAPISuccess('Contact', 'create', $params);
873 * Test creating a current employer through API.
875 public function testContactCreateCurrentEmployer() {
876 // Here we will just do the get for set-up purposes.
877 $count = $this->callAPISuccess('contact', 'getcount', [
878 'organization_name' => 'new employer org',
879 'contact_type' => 'Organization',
881 $this->assertEquals(0, $count);
882 $employerResult = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, [
883 'current_employer' => 'new employer org',
885 // do it again as an update to check it doesn't cause an error
886 $employerResult = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, [
887 'current_employer' => 'new employer org',
888 'id' => $employerResult['id'],
891 $this->callAPISuccess('contact', 'getcount', [
892 'organization_name' => 'new employer org',
893 'contact_type' => 'Organization',
896 $result = $this->callAPISuccess('contact', 'getsingle', [
897 'id' => $employerResult['id'],
900 $this->assertEquals('new employer org', $result['current_employer']);
905 * Test creating a current employer through API.
907 * Check it will re-activate a de-activated employer
909 public function testContactCreateDuplicateCurrentEmployerEnables() {
910 // Set up - create employer relationship.
911 $employerResult = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, ['current_employer' => 'new employer org']));
912 $relationship = $this->callAPISuccess('relationship', 'get', [
913 'contact_id_a' => $employerResult['id'],
916 //disable & check it is disabled
917 $this->callAPISuccess('relationship', 'create', ['id' => $relationship['id'], 'is_active' => 0]);
918 $this->callAPISuccess('relationship', 'getvalue', [
919 'id' => $relationship['id'],
920 'return' => 'is_active',
923 // Re-set the current employer - thus enabling the relationship.
924 $this->callAPISuccess('contact', 'create', array_merge($this->_params
, [
925 'current_employer' => 'new employer org',
926 'id' => $employerResult['id'],
928 //check is_active is now 1
929 $relationship = $this->callAPISuccess('relationship', 'getsingle', ['return' => 'is_active']);
930 $this->assertEquals(1, $relationship['is_active']);
934 * Check deceased contacts are not retrieved.
936 * Note at time of writing the default is to return default. This should possibly be changed & test added.
938 * @param int $version
940 * @dataProvider versionThreeAndFour
942 * @throws \CRM_Core_Exception
944 public function testGetDeceasedRetrieved($version) {
945 $this->_apiversion
= $version;
946 $this->callAPISuccess($this->_entity
, 'create', $this->_params
);
947 $c2 = $this->callAPISuccess($this->_entity
, 'create', [
948 'first_name' => 'bb',
949 'last_name' => 'ccc',
950 'contact_type' => 'Individual',
953 $result = $this->callAPISuccess($this->_entity
, 'get', ['is_deceased' => 0]);
954 $this->assertFalse(array_key_exists($c2['id'], $result['values']));
958 * Test that sort works - old syntax.
960 * @throws \CRM_Core_Exception
962 public function testGetSort() {
963 $c1 = $this->callAPISuccess($this->_entity
, 'create', $this->_params
);
964 $c2 = $this->callAPISuccess($this->_entity
, 'create', [
965 'first_name' => 'bb',
966 'last_name' => 'ccc',
967 'contact_type' => 'Individual',
969 $result = $this->callAPISuccess($this->_entity
, 'get', [
970 'sort' => 'first_name ASC',
971 'return.first_name' => 1,
974 'contact_type' => 'Individual',
977 $this->assertEquals('abc1', $result['values'][0]['first_name']);
978 $result = $this->callAPISuccess($this->_entity
, 'get', [
979 'sort' => 'first_name DESC',
980 'return.first_name' => 1,
984 $this->assertEquals('bb', $result['values'][0]['first_name']);
986 $this->callAPISuccess($this->_entity
, 'delete', ['id' => $c1['id']]);
987 $this->callAPISuccess($this->_entity
, 'delete', ['id' => $c2['id']]);
991 * Test the like operator works for Contact.get
993 * @throws \CRM_Core_Exception
995 public function testGetEmailLike() {
996 $this->individualCreate();
997 $this->callAPISuccessGetCount('Contact', ['email' => ['LIKE' => 'an%']], 1);
998 $this->callAPISuccessGetCount('Contact', ['email' => ['LIKE' => 'ab%']], 0);
1002 * Test that we can retrieve contacts using array syntax.
1004 * I.e 'id' => array('IN' => array('3,4')).
1006 * @param int $version
1008 * @dataProvider versionThreeAndFour
1010 * @throws \CRM_Core_Exception
1012 public function testGetINIDArray($version) {
1013 $this->_apiversion
= $version;
1014 $c1 = $this->callAPISuccess($this->_entity
, 'create', $this->_params
);
1015 $c2 = $this->callAPISuccess($this->_entity
, 'create', [
1016 'first_name' => 'bb',
1017 'last_name' => 'ccc',
1018 'contact_type' => 'Individual',
1020 $c3 = $this->callAPISuccess($this->_entity
, 'create', [
1021 'first_name' => 'hh',
1022 'last_name' => 'll',
1023 'contact_type' => 'Individual',
1025 $result = $this->callAPISuccess($this->_entity
, 'get', ['id' => ['IN' => [$c1['id'], $c3['id']]]]);
1026 $this->assertEquals(2, $result['count']);
1027 $this->assertEquals([$c1['id'], $c3['id']], array_keys($result['values']));
1028 $this->callAPISuccess($this->_entity
, 'delete', ['id' => $c1['id']]);
1029 $this->callAPISuccess($this->_entity
, 'delete', ['id' => $c2['id']]);
1030 $this->callAPISuccess($this->_entity
, 'delete', ['id' => $c3['id']]);
1034 * Test variants on deleted behaviour.
1036 * @throws \CRM_Core_Exception
1038 public function testGetDeleted() {
1039 $params = $this->_params
;
1040 $contact1 = $this->callAPISuccess('contact', 'create', $params);
1041 $params['is_deleted'] = 1;
1042 $params['last_name'] = 'bcd';
1043 $contact2 = $this->callAPISuccess('contact', 'create', $params);
1044 $countActive = $this->callAPISuccess('contact', 'getcount', [
1045 'showAll' => 'active',
1046 'contact_type' => 'Individual',
1048 $countAll = $this->callAPISuccess('contact', 'getcount', ['showAll' => 'all', 'contact_type' => 'Individual']);
1049 $countTrash = $this->callAPISuccess('contact', 'getcount', ['showAll' => 'trash', 'contact_type' => 'Individual']);
1050 $countDefault = $this->callAPISuccess('contact', 'getcount', ['contact_type' => 'Individual']);
1051 $countDeleted = $this->callAPISuccess('contact', 'getcount', [
1052 'contact_type' => 'Individual',
1053 'contact_is_deleted' => 1,
1055 $countNotDeleted = $this->callAPISuccess('contact', 'getcount', [
1056 'contact_is_deleted' => 0,
1057 'contact_type' => 'Individual',
1059 $this->callAPISuccess('contact', 'delete', ['id' => $contact1['id']]);
1060 $this->callAPISuccess('contact', 'delete', ['id' => $contact2['id']]);
1061 $this->assertEquals(1, $countNotDeleted, 'contact_is_deleted => 0 is respected');
1062 $this->assertEquals(1, $countActive);
1063 $this->assertEquals(1, $countTrash);
1064 $this->assertEquals(2, $countAll);
1065 $this->assertEquals(1, $countDeleted);
1066 $this->assertEquals(1, $countDefault, 'Only active by default in line');
1070 * Test that sort works - new syntax.
1072 * @param int $version
1074 * @dataProvider versionThreeAndFour
1076 public function testGetSortNewSyntax($version) {
1077 $this->_apiversion
= $version;
1078 $c1 = $this->callAPISuccess($this->_entity
, 'create', $this->_params
);
1079 $c2 = $this->callAPISuccess($this->_entity
, 'create', [
1080 'first_name' => 'bb',
1081 'last_name' => 'ccc',
1082 'contact_type' => 'Individual',
1084 $result = $this->callAPISuccess($this->_entity
, 'getvalue', [
1085 'return' => 'first_name',
1086 'contact_type' => 'Individual',
1089 'sort' => 'first_name',
1092 $this->assertEquals('abc1', $result);
1094 $result = $this->callAPISuccess($this->_entity
, 'getvalue', [
1095 'return' => 'first_name',
1096 'contact_type' => 'Individual',
1099 'sort' => 'first_name DESC',
1102 $this->assertEquals('bb', $result);
1104 $this->callAPISuccess($this->_entity
, 'delete', ['id' => $c1['id']]);
1105 $this->callAPISuccess($this->_entity
, 'delete', ['id' => $c2['id']]);
1109 * Test sort and limit for chained relationship get.
1111 * https://issues.civicrm.org/jira/browse/CRM-15983
1112 * @param int $version
1114 * @dataProvider versionThreeAndFour
1115 * @throws \CRM_Core_Exception
1117 public function testSortLimitChainedRelationshipGetCRM15983($version) {
1118 $this->_apiversion
= $version;
1120 $create_result_1 = $this->callAPISuccess('contact', 'create', [
1121 'first_name' => 'Jules',
1122 'last_name' => 'Smos',
1123 'contact_type' => 'Individual',
1126 // Create another contact with two relationships.
1128 'first_name' => 'Jos',
1129 'last_name' => 'Smos',
1130 'contact_type' => 'Individual',
1131 'api.relationship.create' => [
1133 'contact_id_a' => '$value.id',
1134 'contact_id_b' => $create_result_1['id'],
1136 'relationship_type_id' => 2,
1137 'start_date' => '2005-01-12',
1138 'end_date' => '2006-01-11',
1139 'description' => 'old',
1142 'contact_id_a' => '$value.id',
1143 'contact_id_b' => $create_result_1['id'],
1144 // spouse of (was married twice :))
1145 'relationship_type_id' => 2,
1146 'start_date' => '2006-07-01',
1147 'end_date' => '2010-07-01',
1148 'description' => 'new',
1152 $create_result = $this->callAPISuccess('contact', 'create', $create_params);
1154 // Try to retrieve the contact and the most recent relationship.
1157 'id' => $create_result['id'],
1158 'api.relationship.get' => [
1159 'contact_id_a' => '$value.id',
1162 'sort' => 'start_date DESC',
1166 $get_result = $this->callAPISuccess('contact', 'getsingle', $get_params);
1169 $this->callAPISuccess('contact', 'delete', [
1170 'id' => $create_result['id'],
1174 $this->assertEquals(1, $get_result['api.relationship.get']['count']);
1175 $this->assertEquals('new', $get_result['api.relationship.get']['values'][0]['description']);
1179 * Test apostrophe works in get & create.
1181 * @param int $version
1183 * @dataProvider versionThreeAndFour
1185 public function testGetApostropheCRM10857($version) {
1186 $this->_apiversion
= $version;
1187 $params = array_merge($this->_params
, ['last_name' => "O'Connor"]);
1188 $this->callAPISuccess($this->_entity
, 'create', $params);
1189 $result = $this->callAPISuccess($this->_entity
, 'getsingle', [
1190 'last_name' => "O'Connor",
1193 $this->assertEquals("O'Connor", $result['last_name']);
1197 * Test between accepts zero.
1199 * In the past it incorrectly required !empty.
1201 * @param int $version
1203 * @dataProvider versionThreeAndFour
1205 public function testGetBetweenZeroWorks($version) {
1206 $this->_apiversion
= $version;
1207 $this->callAPISuccess($this->_entity
, 'get', [
1208 'contact_id' => ['BETWEEN' => [0, 9]],
1210 $this->callAPISuccess($this->_entity
, 'get', [
1221 * Test retrieval by addressee id.
1222 * V3 only - the "skip_greeting_processing" param is not currently in v4
1224 public function testGetByAddresseeID() {
1225 $individual1ID = $this->individualCreate([
1226 'skip_greeting_processing' => 1,
1227 'addressee_id' => 'null',
1228 'email_greeting_id' => 'null',
1229 'postal_greeting_id' => 'null',
1231 $individual2ID = $this->individualCreate();
1233 $this->assertEquals($individual1ID,
1234 $this->callAPISuccessGetValue('Contact', ['contact_type' => 'Individual', 'addressee_id' => ['IS NULL' => 1], 'return' => 'id'])
1236 $this->assertEquals($individual1ID,
1237 $this->callAPISuccessGetValue('Contact', ['contact_type' => 'Individual', 'email_greeting_id' => ['IS NULL' => 1], 'return' => 'id'])
1239 $this->assertEquals($individual1ID,
1240 $this->callAPISuccessGetValue('Contact', ['contact_type' => 'Individual', 'postal_greeting_id' => ['IS NULL' => 1], 'return' => 'id'])
1243 $this->assertEquals($individual2ID,
1244 $this->callAPISuccessGetValue('Contact', ['contact_type' => 'Individual', 'addressee_id' => ['NOT NULL' => 1], 'return' => 'id'])
1249 * Check with complete array + custom field.
1251 * Note that the test is written on purpose without any
1252 * variables specific to participant so it can be replicated into other entities
1253 * and / or moved to the automated test suite
1255 public function testGetWithCustom() {
1256 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
1258 $params = $this->_params
;
1259 $params['custom_' . $ids['custom_field_id']] = "custom string";
1260 $description = "This demonstrates setting a custom field through the API.";
1261 $subfile = "CustomFieldGet";
1262 $result = $this->callAPISuccess($this->_entity
, 'create', $params);
1264 $check = $this->callAPIAndDocument($this->_entity
, 'get', [
1265 'return.custom_' . $ids['custom_field_id'] => 1,
1266 'id' => $result['id'],
1267 ], __FUNCTION__
, __FILE__
, $description, $subfile);
1269 $this->assertEquals("custom string", $check['values'][$check['id']]['custom_' . $ids['custom_field_id']]);
1270 $fields = ($this->callAPISuccess('contact', 'getfields', $params));
1271 $this->assertTrue(is_array($fields['values']['custom_' . $ids['custom_field_id']]));
1272 $this->customFieldDelete($ids['custom_field_id']);
1273 $this->customGroupDelete($ids['custom_group_id']);
1277 * Tests that using 'return' with a custom field not of type contact does not inappropriately filter.
1279 * https://lab.civicrm.org/dev/core/issues/1025
1281 * @throws \CRM_Core_Exception
1283 public function testGetWithCustomOfActivityType() {
1284 $this->createCustomGroupWithFieldOfType(['extends' => 'Activity']);
1285 $this->createCustomGroupWithFieldOfType(['extends' => 'Contact'], 'text', 'contact_');
1286 $contactID = $this->individualCreate();
1287 $this->callAPISuccessGetSingle('Contact', ['id' => $contactID, 'return' => ['external_identifier', $this->getCustomFieldName('contact_text')]]);
1291 * Check with complete array + custom field.
1293 * Note that the test is written on purpose without any
1294 * variables specific to participant so it can be replicated into other entities
1295 * and / or moved to the automated test suite
1297 public function testGetWithCustomReturnSyntax() {
1298 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
1300 $params = $this->_params
;
1301 $params['custom_' . $ids['custom_field_id']] = "custom string";
1302 $description = "This demonstrates setting a custom field through the API.";
1303 $subfile = "CustomFieldGetReturnSyntaxVariation";
1304 $result = $this->callAPISuccess($this->_entity
, 'create', $params);
1305 $params = ['return' => 'custom_' . $ids['custom_field_id'], 'id' => $result['id']];
1306 $check = $this->callAPIAndDocument($this->_entity
, 'get', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
1308 $this->assertEquals("custom string", $check['values'][$check['id']]['custom_' . $ids['custom_field_id']]);
1309 $this->customFieldDelete($ids['custom_field_id']);
1310 $this->customGroupDelete($ids['custom_group_id']);
1314 * Check that address name, ID is returned if required.
1316 public function testGetReturnAddress() {
1317 $contactID = $this->individualCreate();
1318 $result = $this->callAPISuccess('address', 'create', [
1319 'contact_id' => $contactID,
1320 'address_name' => 'My house',
1321 'location_type_id' => 'Home',
1322 'street_address' => '1 my road',
1324 $addressID = $result['id'];
1326 $result = $this->callAPISuccessGetSingle('contact', [
1327 'return' => 'address_name, street_address, address_id',
1330 $this->assertEquals($addressID, $result['address_id']);
1331 $this->assertEquals('1 my road', $result['street_address']);
1332 $this->assertEquals('My house', $result['address_name']);
1337 * Test group filter syntaxes.
1339 public function testGetGroupIDFromContact() {
1340 $groupId = $this->groupCreate();
1342 'email' => 'man2@yahoo.com',
1343 'contact_type' => 'Individual',
1344 'location_type_id' => 1,
1345 'api.group_contact.create' => ['group_id' => $groupId],
1348 $this->callAPISuccess('contact', 'create', $params);
1349 // testing as integer
1351 'filter.group_id' => $groupId,
1352 'contact_type' => 'Individual',
1354 $result = $this->callAPISuccess('contact', 'get', $params);
1355 $this->assertEquals(1, $result['count']);
1356 // group 26 doesn't exist, but we can still search contacts in it.
1358 'filter.group_id' => 26,
1359 'contact_type' => 'Individual',
1361 $this->callAPISuccess('contact', 'get', $params);
1362 // testing as string
1364 'filter.group_id' => "$groupId, 26",
1365 'contact_type' => 'Individual',
1367 $result = $this->callAPISuccess('contact', 'get', $params);
1368 $this->assertEquals(1, $result['count']);
1370 'filter.group_id' => "26,27",
1371 'contact_type' => 'Individual',
1373 $this->callAPISuccess('contact', 'get', $params);
1375 // testing as string
1377 'filter.group_id' => [$groupId, 26],
1378 'contact_type' => 'Individual',
1380 $result = $this->callAPISuccess('contact', 'get', $params);
1381 $this->assertEquals(1, $result['count']);
1383 //test in conjunction with other criteria
1385 'filter.group_id' => [$groupId, 26],
1386 'contact_type' => 'Organization',
1388 $this->callAPISuccess('contact', 'get', $params);
1390 'filter.group_id' => [26, 27],
1391 'contact_type' => 'Individual',
1393 $result = $this->callAPISuccess('contact', 'get', $params);
1394 $this->assertEquals(0, $result['count']);
1398 * Verify that attempt to create individual contact with two chained websites succeeds.
1400 public function testCreateIndividualWithContributionDottedSyntax() {
1401 $description = "This demonstrates the syntax to create 2 chained entities.";
1402 $subFile = "ChainTwoWebsites";
1404 'first_name' => 'abc3',
1405 'last_name' => 'xyz3',
1406 'contact_type' => 'Individual',
1407 'email' => 'man3@yahoo.com',
1408 'api.contribution.create' => [
1409 'receive_date' => '2010-01-01',
1410 'total_amount' => 100.00,
1411 'financial_type_id' => $this->_financialTypeId
,
1412 'payment_instrument_id' => 1,
1413 'non_deductible_amount' => 10.00,
1414 'fee_amount' => 50.00,
1415 'net_amount' => 90.00,
1417 'invoice_id' => 67990,
1419 'contribution_status_id' => 1,
1420 'skipCleanMoney' => 1,
1422 'api.website.create' => [
1423 'url' => "http://civicrm.org",
1425 'api.website.create.2' => [
1426 'url' => "http://chained.org",
1430 $result = $this->callAPIAndDocument('Contact', 'create', $params, __FUNCTION__
, __FILE__
, $description, $subFile);
1432 // checking child function result not covered in callAPIAndDocument
1433 $this->assertAPISuccess($result['values'][$result['id']]['api.website.create']);
1434 $this->assertEquals("http://chained.org", $result['values'][$result['id']]['api.website.create.2']['values'][0]['url']);
1435 $this->assertEquals("http://civicrm.org", $result['values'][$result['id']]['api.website.create']['values'][0]['url']);
1437 // delete the contact
1438 $this->callAPISuccess('contact', 'delete', $result);
1442 * Verify that attempt to create individual contact with chained contribution and website succeeds.
1444 public function testCreateIndividualWithContributionChainedArrays() {
1446 'first_name' => 'abc3',
1447 'last_name' => 'xyz3',
1448 'contact_type' => 'Individual',
1449 'email' => 'man3@yahoo.com',
1450 'api.contribution.create' => [
1451 'receive_date' => '2010-01-01',
1452 'total_amount' => 100.00,
1453 'financial_type_id' => $this->_financialTypeId
,
1454 'payment_instrument_id' => 1,
1455 'non_deductible_amount' => 10.00,
1456 'fee_amount' => 50.00,
1457 'net_amount' => 90.00,
1459 'invoice_id' => 67890,
1461 'contribution_status_id' => 1,
1462 'skipCleanMoney' => 1,
1464 'api.website.create' => [
1466 'url' => "http://civicrm.org",
1469 'url' => "http://chained.org",
1470 'website_type_id' => 2,
1475 $description = "Demonstrates creating two websites as an array.";
1476 $subfile = "ChainTwoWebsitesSyntax2";
1477 $result = $this->callAPIAndDocument('Contact', 'create', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
1479 // the callAndDocument doesn't check the chained call
1480 $this->assertEquals(0, $result['values'][$result['id']]['api.website.create'][0]['is_error']);
1481 $this->assertEquals("http://chained.org", $result['values'][$result['id']]['api.website.create'][1]['values'][0]['url']);
1482 $this->assertEquals("http://civicrm.org", $result['values'][$result['id']]['api.website.create'][0]['values'][0]['url']);
1484 $this->callAPISuccess('contact', 'delete', $result);
1488 * Test for direction when chaining relationships.
1490 * https://issues.civicrm.org/jira/browse/CRM-16084
1491 * @param int $version
1493 * @dataProvider versionThreeAndFour
1495 public function testDirectionChainingRelationshipsCRM16084($version) {
1496 $this->_apiversion
= $version;
1497 // Some contact, called Jules.
1498 $create_result_1 = $this->callAPISuccess('contact', 'create', [
1499 'first_name' => 'Jules',
1500 'last_name' => 'Smos',
1501 'contact_type' => 'Individual',
1504 // Another contact: Jos, child of Jules.
1506 'first_name' => 'Jos',
1507 'last_name' => 'Smos',
1508 'contact_type' => 'Individual',
1509 'api.relationship.create' => [
1511 'contact_id_a' => '$value.id',
1512 'contact_id_b' => $create_result_1['id'],
1514 'relationship_type_id' => 1,
1518 $create_result_2 = $this->callAPISuccess('contact', 'create', $create_params);
1520 // Mia is the child of Jos.
1522 'first_name' => 'Mia',
1523 'last_name' => 'Smos',
1524 'contact_type' => 'Individual',
1525 'api.relationship.create' => [
1527 'contact_id_a' => '$value.id',
1528 'contact_id_b' => $create_result_2['id'],
1530 'relationship_type_id' => 1,
1534 $create_result_3 = $this->callAPISuccess('contact', 'create', $create_params);
1536 // Get Jos and his children.
1539 'id' => $create_result_2['id'],
1540 'api.relationship.get' => [
1541 'contact_id_b' => '$value.id',
1542 'relationship_type_id' => 1,
1545 $get_result = $this->callAPISuccess('contact', 'getsingle', $get_params);
1548 $this->callAPISuccess('contact', 'delete', [
1549 'id' => $create_result_1['id'],
1551 $this->callAPISuccess('contact', 'delete', [
1552 'id' => $create_result_2['id'],
1556 $this->assertEquals(1, $get_result['api.relationship.get']['count']);
1557 $this->assertEquals($create_result_3['id'], $get_result['api.relationship.get']['values'][0]['contact_id_a']);
1561 * Verify that attempt to create individual contact with first, and last names and email succeeds.
1563 public function testCreateIndividualWithNameEmail() {
1565 'first_name' => 'abc3',
1566 'last_name' => 'xyz3',
1567 'contact_type' => 'Individual',
1568 'email' => 'man3@yahoo.com',
1571 $contact = $this->callAPISuccess('contact', 'create', $params);
1573 $this->callAPISuccess('contact', 'delete', $contact);
1577 * Verify that attempt to create individual contact with no data fails.
1579 public function testCreateIndividualWithOutNameEmail() {
1581 'contact_type' => 'Individual',
1583 $this->callAPIFailure('contact', 'create', $params);
1587 * Test create individual contact with first &last names, email and location type succeeds.
1589 public function testCreateIndividualWithNameEmailLocationType() {
1591 'first_name' => 'abc4',
1592 'last_name' => 'xyz4',
1593 'email' => 'man4@yahoo.com',
1594 'contact_type' => 'Individual',
1595 'location_type_id' => 1,
1597 $result = $this->callAPISuccess('contact', 'create', $params);
1599 $this->callAPISuccess('contact', 'delete', ['id' => $result['id']]);
1603 * Verify that when changing employers the old employer relationship becomes inactive.
1605 public function testCreateIndividualWithEmployer() {
1606 $employer = $this->organizationCreate();
1607 $employer2 = $this->organizationCreate();
1610 'email' => 'man4@yahoo.com',
1611 'contact_type' => 'Individual',
1612 'employer_id' => $employer,
1615 $result = $this->callAPISuccess('contact', 'create', $params);
1616 $relationships = $this->callAPISuccess('relationship', 'get', [
1617 'contact_id_a' => $result['id'],
1621 $this->assertEquals($employer, $relationships['values'][0]['contact_id_b']);
1623 // Add more random relationships to make the test more realistic
1624 foreach (['Employee of', 'Volunteer for'] as $relationshipType) {
1625 $relTypeId = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_RelationshipType', $relationshipType, 'id', 'name_a_b');
1626 $this->callAPISuccess('relationship', 'create', [
1627 'contact_id_a' => $result['id'],
1628 'contact_id_b' => $this->organizationCreate(),
1630 'relationship_type_id' => $relTypeId,
1634 // Add second employer
1635 $params['employer_id'] = $employer2;
1636 $params['id'] = $result['id'];
1637 $result = $this->callAPISuccess('contact', 'create', $params);
1639 $relationships = $this->callAPISuccess('relationship', 'get', [
1640 'contact_id_a' => $result['id'],
1645 $this->assertEquals($employer, $relationships['values'][0]['contact_id_b']);
1649 * Verify that attempt to create household contact with details succeeds.
1651 public function testCreateHouseholdDetails() {
1653 'household_name' => 'abc8\'s House',
1654 'nick_name' => 'x House',
1655 'email' => 'man8@yahoo.com',
1656 'contact_type' => 'Household',
1659 $contact = $this->callAPISuccess('contact', 'create', $params);
1661 $this->callAPISuccess('contact', 'delete', $contact);
1665 * Verify that attempt to create household contact with inadequate details fails.
1667 public function testCreateHouseholdInadequateDetails() {
1669 'nick_name' => 'x House',
1670 'email' => 'man8@yahoo.com',
1671 'contact_type' => 'Household',
1673 $this->callAPIFailure('contact', 'create', $params);
1677 * Verify successful update of individual contact.
1679 public function testUpdateIndividualWithAll() {
1680 $contactID = $this->individualCreate();
1684 'first_name' => 'abcd',
1685 'contact_type' => 'Individual',
1686 'nick_name' => 'This is nickname first',
1687 'do_not_email' => '1',
1688 'do_not_phone' => '1',
1689 'do_not_mail' => '1',
1690 'do_not_trade' => '1',
1691 'legal_identifier' => 'ABC23853ZZ2235',
1692 'external_identifier' => '1928837465',
1693 'image_URL' => 'http://some.url.com/image.jpg',
1694 'home_url' => 'http://www.example.org',
1697 $this->callAPISuccess('Contact', 'Update', $params);
1698 $getResult = $this->callAPISuccess('Contact', 'Get', $params);
1699 unset($params['contact_id']);
1700 //Todo - neither API v2 or V3 are testing for home_url - not sure if it is being set.
1701 //reducing this test partially back to api v2 level to get it through
1702 unset($params['home_url']);
1703 foreach ($params as $key => $value) {
1704 $this->assertEquals($value, $getResult['values'][$contactID][$key]);
1709 * Verify successful update of organization contact.
1711 * @throws \Exception
1713 public function testUpdateOrganizationWithAll() {
1714 $contactID = $this->organizationCreate();
1718 'organization_name' => 'WebAccess India Pvt Ltd',
1719 'legal_name' => 'WebAccess',
1720 'sic_code' => 'ABC12DEF',
1721 'contact_type' => 'Organization',
1724 $this->callAPISuccess('Contact', 'Update', $params);
1725 $this->getAndCheck($params, $contactID, 'Contact');
1729 * Test merging 2 organizations.
1731 * CRM-20421: This test make sure that inherited memberships are deleted upon merging organization.
1733 * @throws \API_Exception
1734 * @throws \CRM_Core_Exception
1735 * @throws \CiviCRM_API3_Exception
1736 * @throws \Civi\API\Exception\UnauthorizedException
1738 public function testMergeOrganizations() {
1739 $organizationID1 = $this->organizationCreate([], 0);
1740 $organizationID2 = $this->organizationCreate([], 1);
1741 $contact = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, [
1742 'employer_id' => $organizationID1,
1744 $contact = $contact["values"][$contact["id"]];
1746 $membershipType = $this->createEmployerOfMembership();
1747 $membershipParams = [
1748 'membership_type_id' => $membershipType["id"],
1749 'contact_id' => $organizationID1,
1750 'start_date' => '01/01/2015',
1751 'join_date' => '01/01/2010',
1752 'end_date' => '12/31/2015',
1754 $ownerMembershipID = $this->contactMembershipCreate($membershipParams);
1756 $contactMembership = $this->callAPISuccessGetSingle('membership', ['contact_id' => $contact['id']]);
1758 $this->assertEquals($ownerMembershipID, $contactMembership['owner_membership_id'], "Contact membership must be inherited from Organization");
1760 CRM_Dedupe_Merger
::moveAllBelongings($organizationID2, $organizationID1, [
1761 'move_rel_table_memberships' => "0",
1762 "move_rel_table_relationships" => "1",
1764 "contact_id" => $organizationID2,
1765 "contact_type" => "Organization",
1767 "other_details" => [
1768 "contact_id" => $organizationID1,
1769 "contact_type" => "Organization",
1773 $contactMembership = $this->callAPISuccess("membership", "get", [
1774 "contact_id" => $contact["id"],
1777 $this->assertEquals(0, $contactMembership["count"], "Contact membership must be deleted after merging organization without memberships.");
1781 * Test the function that determines if 2 contacts have conflicts.
1783 * @throws \CRM_Core_Exception
1785 public function testMergeGetConflicts() {
1786 list($contact1, $contact2) = $this->createDeeplyConflictedContacts();
1787 $conflicts = $this->callAPISuccess('Contact', 'get_merge_conflicts', ['to_keep_id' => $contact1, 'to_remove_id' => $contact2])['values'];
1788 $this->assertEquals([
1792 'first_name' => [$contact1 => 'Anthony', $contact2 => 'different', 'title' => 'First Name'],
1793 'external_identifier' => [$contact1 => 'unique and special', $contact2 => 'uniquer and specialler', 'title' => 'External Identifier'],
1794 $this->getCustomFieldName('text') => [$contact1 => 'mummy loves me', $contact2 => 'mummy loves me more', 'title' => 'Enter text here'],
1798 'location_type_id' => '1',
1799 'title' => 'Address 1 (Home)',
1800 'street_address' => [
1801 $contact1 => 'big house',
1802 $contact2 => 'medium house',
1805 $contact1 => "big house\nsmall city, \n",
1806 $contact2 => "medium house\nsmall city, \n",
1810 'location_type_id' => '2',
1811 'street_address' => [
1812 $contact1 => 'big office',
1813 $contact2 => 'medium office',
1815 'title' => 'Address 2 (Work)',
1817 $contact1 => "big office\nsmall city, \n",
1818 $contact2 => "medium office\nsmall city, \n",
1824 'location_type_id' => '1',
1826 $contact1 => 'bob@example.com',
1827 $contact2 => 'anthony_anderson@civicrm.org',
1829 'title' => 'Email 1 (Home)',
1831 $contact1 => 'bob@example.com',
1832 $contact2 => 'anthony_anderson@civicrm.org',
1841 $this->callAPISuccess('Job', 'process_batch_merge');
1842 $defaultRuleGroupID = $this->callAPISuccessGetValue('RuleGroup', [
1843 'contact_type' => 'Individual',
1844 'used' => 'Unsupervised',
1846 'options' => ['limit' => 1],
1849 $duplicates = $this->callAPISuccess('Dedupe', 'getduplicates', ['rule_group_id' => $defaultRuleGroupID]);
1850 $this->assertEquals($conflicts['safe']['conflicts'], $duplicates['values'][0]['safe']['conflicts']);
1855 * @throws \CRM_Core_Exception
1857 public function testGetConflictsAggressiveMode() {
1858 list($contact1, $contact2) = $this->createDeeplyConflictedContacts();
1859 $conflicts = $this->callAPISuccess('Contact', 'get_merge_conflicts', ['to_keep_id' => $contact1, 'to_remove_id' => $contact2, 'mode' => ['safe', 'aggressive']])['values'];
1860 $this->assertEquals([
1862 'external_identifier' => 'uniquer and specialler',
1863 'first_name' => 'different',
1864 'custom_1' => 'mummy loves me more',
1866 ], $conflicts['aggressive']['resolved']);
1870 * Create inherited membership type for employer relationship.
1874 * @throws \CRM_Core_Exception
1876 private function createEmployerOfMembership() {
1878 'domain_id' => CRM_Core_Config
::domainID(),
1879 'name' => 'Organization Membership',
1880 'member_of_contact_id' => 1,
1881 'financial_type_id' => 1,
1882 'minimum_fee' => 10,
1883 'duration_unit' => 'year',
1884 'duration_interval' => 1,
1885 'period_type' => 'rolling',
1886 'relationship_type_id' => 5,
1887 'relationship_direction' => 'b_a',
1888 'visibility' => 'Public',
1891 $membershipType = $this->callAPISuccess('membership_type', 'create', $params);
1892 return $membershipType['values'][$membershipType['id']];
1896 * Verify successful update of household contact.
1898 * @param int $version
1900 * @dataProvider versionThreeAndFour
1902 public function testUpdateHouseholdWithAll($version) {
1903 $this->_apiversion
= $version;
1904 $contactID = $this->householdCreate();
1908 'household_name' => 'ABC household',
1909 'nick_name' => 'ABC House',
1910 'contact_type' => 'Household',
1913 $result = $this->callAPISuccess('Contact', 'Update', $params);
1916 'contact_type' => 'Household',
1918 'sort_name' => 'ABC household',
1919 'display_name' => 'ABC household',
1920 'nick_name' => 'ABC House',
1922 $this->getAndCheck($expected, $result['id'], 'contact');
1926 * Test civicrm_update() without contact type.
1928 * Deliberately exclude contact_type as it should still cope using civicrm_api.
1932 * @param int $version
1934 * @dataProvider versionThreeAndFour
1936 public function testUpdateCreateWithID($version) {
1937 $this->_apiversion
= $version;
1938 $contactID = $this->individualCreate();
1939 $this->callAPISuccess('Contact', 'Update', [
1941 'first_name' => 'abcd',
1942 'last_name' => 'wxyz',
1947 * Test civicrm_contact_delete() with no contact ID.
1949 * @param int $version
1951 * @dataProvider versionThreeAndFour
1953 public function testContactDeleteNoID($version) {
1954 $this->_apiversion
= $version;
1958 $this->callAPIFailure('contact', 'delete', $params);
1962 * Test civicrm_contact_delete() with error.
1964 * @param int $version
1966 * @dataProvider versionThreeAndFour
1968 public function testContactDeleteError($version) {
1969 $this->_apiversion
= $version;
1970 $params = ['contact_id' => 999];
1971 $this->callAPIFailure('contact', 'delete', $params);
1975 * Test civicrm_contact_delete().
1977 * @param int $version
1979 * @dataProvider versionThreeAndFour
1981 public function testContactDelete($version) {
1982 $this->_apiversion
= $version;
1983 $contactID = $this->individualCreate();
1987 $this->callAPIAndDocument('contact', 'delete', $params, __FUNCTION__
, __FILE__
);
1991 * Test civicrm_contact_get() return only first name.
1993 * @param int $version
1995 * @dataProvider versionThreeAndFour
1997 public function testContactGetRetFirst($version) {
1998 $this->_apiversion
= $version;
1999 $contact = $this->callAPISuccess('contact', 'create', $this->_params
);
2001 'contact_id' => $contact['id'],
2002 'return_first_name' => TRUE,
2003 'sort' => 'first_name',
2005 $result = $this->callAPISuccess('contact', 'get', $params);
2006 $this->assertEquals(1, $result['count']);
2007 $this->assertEquals($contact['id'], $result['id']);
2008 $this->assertEquals('abc1', $result['values'][$contact['id']]['first_name']);
2012 * Test civicrm_contact_get() return only first name & last name.
2014 * Use comma separated string return with a space.
2016 * @param int $version
2018 * @dataProvider versionThreeAndFour
2020 public function testContactGetReturnFirstLast($version) {
2021 $this->_apiversion
= $version;
2022 $contact = $this->callAPISuccess('contact', 'create', $this->_params
);
2024 'contact_id' => $contact['id'],
2025 'return' => 'first_name, last_name',
2027 $result = $this->callAPISuccess('contact', 'getsingle', $params);
2028 $this->assertEquals('abc1', $result['first_name']);
2029 $this->assertEquals('xyz1', $result['last_name']);
2030 //check that other defaults not returns
2031 $this->assertArrayNotHasKey('sort_name', $result);
2033 'contact_id' => $contact['id'],
2034 'return' => 'first_name,last_name',
2036 $result = $this->callAPISuccess('contact', 'getsingle', $params);
2037 $this->assertEquals('abc1', $result['first_name']);
2038 $this->assertEquals('xyz1', $result['last_name']);
2039 //check that other defaults not returns
2040 $this->assertArrayNotHasKey('sort_name', $result);
2044 * Test civicrm_contact_get() return only first name & last name.
2046 * Use comma separated string return without a space
2048 * @param int $version
2050 * @dataProvider versionThreeAndFour
2052 public function testContactGetReturnFirstLastNoComma($version) {
2053 $this->_apiversion
= $version;
2054 $contact = $this->callAPISuccess('contact', 'create', $this->_params
);
2056 'contact_id' => $contact['id'],
2057 'return' => 'first_name,last_name',
2059 $result = $this->callAPISuccess('contact', 'getsingle', $params);
2060 $this->assertEquals('abc1', $result['first_name']);
2061 $this->assertEquals('xyz1', $result['last_name']);
2062 //check that other defaults not returns
2063 $this->assertArrayNotHasKey('sort_name', $result);
2067 * Test civicrm_contact_get() with default return properties.
2069 public function testContactGetRetDefault() {
2070 $contactID = $this->individualCreate();
2072 'contact_id' => $contactID,
2073 'sort' => 'first_name',
2075 $result = $this->callAPISuccess('contact', 'get', $params);
2076 $this->assertEquals($contactID, $result['values'][$contactID]['contact_id']);
2077 $this->assertEquals('Anthony', $result['values'][$contactID]['first_name']);
2081 * Test civicrm_contact_getquick() with empty name param.
2083 public function testContactGetQuick() {
2084 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact', 'email' => 'TestContact@example.com']);
2086 $result = $this->callAPISuccess('contact', 'getquick', ['name' => 'T']);
2087 $this->assertEquals($contactID, $result['values'][0]['id']);
2089 'name' => "TestContact@example.com",
2090 'field_name' => 'sort_name',
2092 $result = $this->callAPISuccess('contact', 'getquick', $params);
2093 $this->assertEquals($contactID, $result['values'][0]['id']);
2097 * Test civicrm_contact_get) with empty params.
2099 * @param int $version
2101 * @dataProvider versionThreeAndFour
2103 public function testContactGetEmptyParams($version) {
2104 $this->_apiversion
= $version;
2105 $this->callAPISuccess('contact', 'get', []);
2109 * Test civicrm_contact_get(,true) with no matches.
2111 * @param int $version
2113 * @dataProvider versionThreeAndFour
2115 public function testContactGetOldParamsNoMatches($version) {
2116 $this->_apiversion
= $version;
2117 $this->individualCreate();
2118 $result = $this->callAPISuccess('contact', 'get', ['first_name' => 'Fred']);
2119 $this->assertEquals(0, $result['count']);
2123 * Test civicrm_contact_get(,true) with one match.
2125 public function testContactGetOldParamsOneMatch() {
2126 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2128 $result = $this->callAPISuccess('contact', 'get', ['first_name' => 'Test']);
2129 $this->assertEquals($contactID, $result['values'][$contactID]['contact_id']);
2130 $this->assertEquals($contactID, $result['id']);
2134 * Test civicrm_contact_get(,true) with space in sort_name.
2136 public function testContactGetSpaceMatches() {
2137 $contactParams_1 = [
2138 'first_name' => 'Sanford',
2139 'last_name' => 'Blackwell',
2140 'sort_name' => 'Blackwell, Sanford',
2141 'contact_type' => 'Individual',
2143 $this->individualCreate($contactParams_1);
2145 $contactParams_2 = [
2146 'household_name' => 'Blackwell family',
2147 'sort_name' => 'Blackwell family',
2148 'contact_type' => 'Household',
2150 $this->individualCreate($contactParams_2);
2152 $result = $this->callAPISuccess('contact', 'get', ['sort_name' => 'Blackwell F']);
2153 $this->assertEquals(1, $result['count']);
2157 * Test civicrm_contact_search_count().
2159 public function testContactGetEmail() {
2161 'email' => 'man2@yahoo.com',
2162 'contact_type' => 'Individual',
2163 'location_type_id' => 1,
2166 $contact = $this->callAPISuccess('contact', 'create', $params);
2169 'email' => 'man2@yahoo.com',
2171 $result = $this->callAPIAndDocument('contact', 'get', $params, __FUNCTION__
, __FILE__
);
2172 $this->assertEquals('man2@yahoo.com', $result['values'][$result['id']]['email']);
2174 $this->callAPISuccess('contact', 'delete', $contact);
2178 * Ensure consistent return format for option group fields.
2180 * @param int $version
2182 * @dataProvider versionThreeAndFour
2184 public function testSetPreferredCommunicationNull($version) {
2185 $this->_apiversion
= $version;
2186 $contact = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, [
2187 'preferred_communication_method' => ['Phone', 'SMS'],
2189 $preferredCommunicationMethod = $this->callAPISuccessGetValue('Contact', [
2190 'id' => $contact['id'],
2191 'return' => 'preferred_communication_method',
2193 $this->assertNotEmpty($preferredCommunicationMethod);
2194 $contact = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, [
2195 'preferred_communication_method' => 'null',
2196 'id' => $contact['id'],
2198 $preferredCommunicationMethod = $this->callAPISuccessGetValue('Contact', [
2199 'id' => $contact['id'],
2200 'return' => 'preferred_communication_method',
2202 $this->assertEmpty($preferredCommunicationMethod);
2206 * Ensure consistent return format for option group fields.
2208 * @throws \CRM_Core_Exception
2210 public function testPseudoFields() {
2212 'preferred_communication_method' => ['Phone', 'SMS'],
2213 'preferred_language' => 'en_US',
2214 'gender_id' => 'Female',
2215 'prefix_id' => 'Mrs.',
2216 'suffix_id' => 'II',
2217 'communication_style_id' => 'Formal',
2220 $contact = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, $params));
2222 $result = $this->callAPISuccess('contact', 'getsingle', ['id' => $contact['id']]);
2223 $this->assertEquals('Both', $result['preferred_mail_format']);
2225 $this->assertEquals('en_US', $result['preferred_language']);
2226 $this->assertEquals(1, $result['communication_style_id']);
2227 $this->assertEquals(1, $result['gender_id']);
2228 $this->assertEquals('Female', $result['gender']);
2229 $this->assertEquals('Mrs.', $result['individual_prefix']);
2230 $this->assertEquals(1, $result['prefix_id']);
2231 $this->assertEquals('II', $result['individual_suffix']);
2232 $this->assertEquals(CRM_Core_PseudoConstant
::getKey("CRM_Contact_BAO_Contact", 'suffix_id', 'II'), $result['suffix_id']);
2233 $this->callAPISuccess('contact', 'delete', $contact);
2234 $this->assertEquals([
2235 CRM_Core_PseudoConstant
::getKey("CRM_Contact_BAO_Contact", 'preferred_communication_method', 'Phone'),
2236 CRM_Core_PseudoConstant
::getKey("CRM_Contact_BAO_Contact", 'preferred_communication_method', 'SMS'),
2237 ], $result['preferred_communication_method']);
2241 * Test birth date parameters.
2243 * These include value, array & birth_date_high, birth_date_low
2246 * @throws \CRM_Core_Exception
2248 public function testContactGetBirthDate() {
2249 $contact1 = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, ['birth_date' => 'first day of next month - 2 years']));
2250 $contact2 = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, ['birth_date' => 'first day of next month - 5 years']));
2251 $contact3 = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, ['birth_date' => 'first day of next month -20 years']));
2253 $result = $this->callAPISuccess('contact', 'get', []);
2254 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -2 years')), $result['values'][$contact1['id']]['birth_date']);
2255 $result = $this->callAPISuccess('contact', 'get', ['birth_date' => 'first day of next month -5 years']);
2256 $this->assertEquals(1, $result['count']);
2257 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['birth_date']);
2258 $result = $this->callAPISuccess('contact', 'get', ['birth_date_high' => date('Y-m-d', strtotime('-6 years'))]);
2259 $this->assertEquals(1, $result['count']);
2260 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -20 years')), $result['values'][$contact3['id']]['birth_date']);
2261 $result = $this->callAPISuccess('contact', 'get', [
2262 'birth_date_low' => date('Y-m-d', strtotime('-6 years')),
2263 'birth_date_high' => date('Y-m-d', strtotime('- 3 years')),
2265 $this->assertEquals(1, $result['count']);
2266 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['birth_date']);
2267 $result = $this->callAPISuccess('contact', 'get', [
2268 'birth_date_low' => '-6 years',
2269 'birth_date_high' => '- 3 years',
2271 $this->assertEquals(1, $result['count']);
2272 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['birth_date']);
2276 * Test Address parameters
2278 * This include state_province, state_province_name, country
2280 public function testContactGetWithAddressFields() {
2283 'first_name' => 'abc1',
2284 'contact_type' => 'Individual',
2285 'last_name' => 'xyz1',
2286 'api.address.create' => [
2287 'country' => 'United States',
2288 'state_province_id' => 'Michigan',
2289 'location_type_id' => 1,
2293 'first_name' => 'abc2',
2294 'contact_type' => 'Individual',
2295 'last_name' => 'xyz2',
2296 'api.address.create' => [
2297 'country' => 'United States',
2298 'state_province_id' => 'Alabama',
2299 'location_type_id' => 1,
2303 foreach ($individuals as $params) {
2304 $contact = $this->callAPISuccess('contact', 'create', $params);
2307 // Check whether Contact get API return successfully with below Address params.
2309 'state_province_name' => 'Michigan',
2310 'state_province' => 'Michigan',
2311 'country' => 'United States',
2312 'state_province_name' => ['IN' => ['Michigan', 'Alabama']],
2313 'state_province' => ['IN' => ['Michigan', 'Alabama']],
2315 foreach ($fieldsToTest as $field => $value) {
2317 'id' => $contact['id'],
2320 $result = $this->callAPISuccess('Contact', 'get', $getParams);
2321 $this->assertEquals(1, $result['count']);
2326 * Test Deceased date parameters.
2328 * These include value, array & Deceased_date_high, Deceased date_low
2331 public function testContactGetDeceasedDate() {
2332 $contact1 = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, ['deceased_date' => 'first day of next month - 2 years']));
2333 $contact2 = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, ['deceased_date' => 'first day of next month - 5 years']));
2334 $contact3 = $this->callAPISuccess('contact', 'create', array_merge($this->_params
, ['deceased_date' => 'first day of next month -20 years']));
2336 $result = $this->callAPISuccess('contact', 'get', []);
2337 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -2 years')), $result['values'][$contact1['id']]['deceased_date']);
2338 $result = $this->callAPISuccess('contact', 'get', ['deceased_date' => 'first day of next month -5 years']);
2339 $this->assertEquals(1, $result['count']);
2340 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['deceased_date']);
2341 $result = $this->callAPISuccess('contact', 'get', ['deceased_date_high' => date('Y-m-d', strtotime('-6 years'))]);
2342 $this->assertEquals(1, $result['count']);
2343 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -20 years')), $result['values'][$contact3['id']]['deceased_date']);
2344 $result = $this->callAPISuccess('contact', 'get', [
2345 'deceased_date_low' => '-6 years',
2346 'deceased_date_high' => date('Y-m-d', strtotime('- 3 years')),
2348 $this->assertEquals(1, $result['count']);
2349 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['deceased_date']);
2353 * Test for Contact.get id=@user:username.
2355 public function testContactGetByUsername() {
2356 // Setup - create contact with a uf-match.
2357 $cid = $this->individualCreate([
2358 'contact_type' => 'Individual',
2359 'first_name' => 'testGetByUsername',
2360 'last_name' => 'testGetByUsername',
2364 'domain_id' => CRM_Core_Config
::domainID(),
2366 'uf_name' => 'the-email-matching-key-is-not-really-the-username',
2367 'contact_id' => $cid,
2369 $ufMatch = CRM_Core_BAO_UFMatch
::create($ufMatchParams);
2370 $this->assertTrue(is_numeric($ufMatch->id
));
2372 // setup - mock the calls to CRM_Utils_System_*::getUfId
2373 $userSystem = $this->getMockBuilder('CRM_Utils_System_UnitTests')->setMethods(['getUfId'])->getMock();
2374 $userSystem->expects($this->once())
2376 ->with($this->equalTo('exampleUser'))
2377 ->will($this->returnValue(99));
2378 CRM_Core_Config
::singleton()->userSystem
= $userSystem;
2381 $result = $this->callAPISuccess('Contact', 'get', [
2382 'id' => '@user:exampleUser',
2384 $this->assertEquals('testGetByUsername', $result['values'][$cid]['first_name']);
2386 // Check search of contacts with & without uf records
2387 $result = $this->callAPISuccess('Contact', 'get', ['uf_user' => 1]);
2388 $this->assertArrayHasKey($cid, $result['values']);
2390 $result = $this->callAPISuccess('Contact', 'get', ['uf_user' => 0]);
2391 $this->assertArrayNotHasKey($cid, $result['values']);
2395 * Test to check return works OK.
2397 public function testContactGetReturnValues() {
2399 'nick_name' => 'Bob',
2401 'email' => 'e@mail.com',
2403 $contactID = $this->individualCreate($extraParams);
2404 //actually it turns out the above doesn't create a phone
2405 $this->callAPISuccess('phone', 'create', ['contact_id' => $contactID, 'phone' => '456']);
2406 $result = $this->callAPISuccess('contact', 'getsingle', ['id' => $contactID]);
2407 foreach ($extraParams as $key => $value) {
2408 $this->assertEquals($result[$key], $value);
2410 //now we check they are still returned with 'return' key
2411 $result = $this->callAPISuccess('contact', 'getsingle', [
2413 'return' => array_keys($extraParams),
2415 foreach ($extraParams as $key => $value) {
2416 $this->assertEquals($result[$key], $value);
2421 * Test creating multiple phones using chaining.
2423 * @param int $version
2425 * @dataProvider versionThreeAndFour
2426 * @throws \Exception
2428 public function testCRM13252MultipleChainedPhones($version) {
2429 $this->_apiversion
= $version;
2430 $contactID = $this->householdCreate();
2431 $this->callAPISuccessGetCount('phone', ['contact_id' => $contactID], 0);
2433 'contact_id' => $contactID,
2434 'household_name' => 'Household 1',
2435 'contact_type' => 'Household',
2436 'api.phone.create' => [
2438 'phone' => '111-111-1111',
2439 'location_type_id' => 1,
2440 'phone_type_id' => 1,
2443 'phone' => '222-222-2222',
2444 'location_type_id' => 1,
2445 'phone_type_id' => 2,
2449 $this->callAPISuccess('contact', 'create', $params);
2450 $this->callAPISuccessGetCount('phone', ['contact_id' => $contactID], 2);
2455 * Test for Contact.get id=@user:username (with an invalid username).
2457 public function testContactGetByUnknownUsername() {
2458 // setup - mock the calls to CRM_Utils_System_*::getUfId
2459 $userSystem = $this->getMockBuilder('CRM_Utils_System_UnitTests')->setMethods(['getUfId'])->getMock();
2460 $userSystem->expects($this->once())
2462 ->with($this->equalTo('exampleUser'))
2463 ->will($this->returnValue(NULL));
2464 CRM_Core_Config
::singleton()->userSystem
= $userSystem;
2467 $result = $this->callAPIFailure('Contact', 'get', [
2468 'id' => '@user:exampleUser',
2470 $this->assertRegExp('/cannot be resolved to a contact ID/', $result['error_message']);
2474 * Verify attempt to create individual with chained arrays and sequential.
2476 * @param int $version
2478 * @dataProvider versionThreeAndFour
2480 public function testGetIndividualWithChainedArraysAndSequential($version) {
2481 $this->_apiversion
= $version;
2482 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
2483 $params['custom_' . $ids['custom_field_id']] = "custom string";
2485 $moreIDs = $this->CustomGroupMultipleCreateWithFields();
2488 'first_name' => 'abc3',
2489 'last_name' => 'xyz3',
2490 'contact_type' => 'Individual',
2491 'email' => 'man3@yahoo.com',
2492 'api.website.create' => [
2494 'url' => "http://civicrm.org",
2497 'url' => "https://civicrm.org",
2502 $result = $this->callAPISuccess('Contact', 'create', $params);
2504 // delete the contact and custom groups
2505 $this->callAPISuccess('contact', 'delete', ['id' => $result['id']]);
2506 $this->customGroupDelete($ids['custom_group_id']);
2507 $this->customGroupDelete($moreIDs['custom_group_id']);
2509 $this->assertEquals($result['id'], $result['values'][0]['id']);
2510 $this->assertArrayKeyExists('api.website.create', $result['values'][0]);
2514 * Verify attempt to create individual with chained arrays.
2516 public function testGetIndividualWithChainedArrays() {
2517 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
2518 $params['custom_' . $ids['custom_field_id']] = "custom string";
2520 $moreIDs = $this->CustomGroupMultipleCreateWithFields();
2521 $description = "This demonstrates the usage of chained api functions.\nIn this case no notes or custom fields have been created.";
2522 $subfile = "APIChainedArray";
2524 'first_name' => 'abc3',
2525 'last_name' => 'xyz3',
2526 'contact_type' => 'Individual',
2527 'email' => 'man3@yahoo.com',
2528 'api.contribution.create' => [
2529 'receive_date' => '2010-01-01',
2530 'total_amount' => 100.00,
2531 'financial_type_id' => 1,
2532 'payment_instrument_id' => 1,
2533 'non_deductible_amount' => 10.00,
2534 'fee_amount' => 50.00,
2535 'net_amount' => 90.00,
2537 'invoice_id' => 67890,
2539 'contribution_status_id' => 1,
2541 'api.contribution.create.1' => [
2542 'receive_date' => '2011-01-01',
2543 'total_amount' => 120.00,
2544 'financial_type_id' => $this->_financialTypeId
= 1,
2545 'payment_instrument_id' => 1,
2546 'non_deductible_amount' => 10.00,
2547 'fee_amount' => 50.00,
2548 'net_amount' => 90.00,
2550 'invoice_id' => 67830,
2552 'contribution_status_id' => 1,
2554 'api.website.create' => [
2556 'url' => "http://civicrm.org",
2561 $result = $this->callAPISuccess('Contact', 'create', $params);
2563 'id' => $result['id'],
2564 'api.website.get' => [],
2565 'api.Contribution.get' => [
2566 'total_amount' => '120.00',
2568 'api.CustomValue.get' => 1,
2569 'api.Note.get' => 1,
2571 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
2572 // delete the contact
2573 $this->callAPISuccess('contact', 'delete', $result);
2574 $this->customGroupDelete($ids['custom_group_id']);
2575 $this->customGroupDelete($moreIDs['custom_group_id']);
2576 $this->assertEquals(0, $result['values'][$result['id']]['api.website.get']['is_error']);
2577 $this->assertEquals("http://civicrm.org", $result['values'][$result['id']]['api.website.get']['values'][0]['url']);
2581 * Verify attempt to create individual with chained arrays and sequential.
2583 * @see https://issues.civicrm.org/jira/browse/CRM-15815
2585 * @param int $version
2587 * @dataProvider versionThreeAndFour
2589 public function testCreateIndividualWithChainedArrayAndSequential($version) {
2590 $this->_apiversion
= $version;
2593 'first_name' => 'abc5',
2594 'last_name' => 'xyz5',
2595 'contact_type' => 'Individual',
2596 'email' => 'woman5@yahoo.com',
2597 'api.phone.create' => [
2598 ['phone' => '03-231 07 95'],
2599 ['phone' => '03-232 51 62'],
2601 'api.website.create' => [
2602 'url' => 'http://civicrm.org',
2605 $result = $this->callAPISuccess('Contact', 'create', $params);
2607 // I could try to parse the result to see whether the two phone numbers
2608 // and the website are there, but I am not sure about the correct format.
2609 // So I will just fetch it again before checking.
2610 // See also http://forum.civicrm.org/index.php/topic,35393.0.html
2613 'id' => $result['id'],
2614 'api.website.get' => [],
2615 'api.phone.get' => [],
2617 $result = $this->callAPISuccess('Contact', 'get', $params);
2619 // delete the contact
2620 $this->callAPISuccess('contact', 'delete', $result);
2622 $this->assertEquals(2, $result['values'][0]['api.phone.get']['count']);
2623 $this->assertEquals(1, $result['values'][0]['api.website.get']['count']);
2627 * Test retrieving an individual with chained array syntax.
2629 public function testGetIndividualWithChainedArraysFormats() {
2630 $description = "This demonstrates the usage of chained api functions.\nIn this case no notes or custom fields have been created.";
2631 $subfile = "APIChainedArrayFormats";
2632 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
2633 $params['custom_' . $ids['custom_field_id']] = "custom string";
2635 $moreIDs = $this->CustomGroupMultipleCreateWithFields();
2637 'first_name' => 'abc3',
2638 'last_name' => 'xyz3',
2639 'contact_type' => 'Individual',
2640 'email' => 'man3@yahoo.com',
2641 'api.contribution.create' => [
2642 'receive_date' => '2010-01-01',
2643 'total_amount' => 100.00,
2644 'financial_type_id' => $this->_financialTypeId
,
2645 'payment_instrument_id' => 1,
2646 'non_deductible_amount' => 10.00,
2647 'fee_amount' => 50.00,
2648 'net_amount' => 90.00,
2650 'contribution_status_id' => 1,
2651 'skipCleanMoney' => 1,
2653 'api.contribution.create.1' => [
2654 'receive_date' => '2011-01-01',
2655 'total_amount' => 120.00,
2656 'financial_type_id' => $this->_financialTypeId
,
2657 'payment_instrument_id' => 1,
2658 'non_deductible_amount' => 10.00,
2659 'fee_amount' => 50.00,
2660 'net_amount' => 90.00,
2662 'contribution_status_id' => 1,
2663 'skipCleanMoney' => 1,
2665 'api.website.create' => [
2667 'url' => "http://civicrm.org",
2672 $result = $this->callAPISuccess('Contact', 'create', $params);
2674 'id' => $result['id'],
2675 'api.website.getValue' => ['return' => 'url'],
2676 'api.Contribution.getCount' => [],
2677 'api.CustomValue.get' => 1,
2678 'api.Note.get' => 1,
2679 'api.Membership.getCount' => [],
2681 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
2682 $this->assertEquals(2, $result['values'][$result['id']]['api.Contribution.getCount']);
2683 $this->assertEquals(0, $result['values'][$result['id']]['api.Note.get']['is_error']);
2684 $this->assertEquals("http://civicrm.org", $result['values'][$result['id']]['api.website.getValue']);
2686 $this->callAPISuccess('contact', 'delete', $result);
2687 $this->customGroupDelete($ids['custom_group_id']);
2688 $this->customGroupDelete($moreIDs['custom_group_id']);
2692 * Test complex chaining.
2694 public function testGetIndividualWithChainedArraysAndMultipleCustom() {
2695 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
2696 $params['custom_' . $ids['custom_field_id']] = "custom string";
2697 $moreIDs = $this->CustomGroupMultipleCreateWithFields();
2698 $andMoreIDs = $this->CustomGroupMultipleCreateWithFields([
2699 'title' => "another group",
2700 'name' => 'another name',
2702 $description = "This demonstrates the usage of chained api functions with multiple custom fields.";
2703 $subfile = "APIChainedArrayMultipleCustom";
2705 'first_name' => 'abc3',
2706 'last_name' => 'xyz3',
2707 'contact_type' => 'Individual',
2708 'email' => 'man3@yahoo.com',
2709 'api.contribution.create' => [
2710 'receive_date' => '2010-01-01',
2711 'total_amount' => 100.00,
2712 'financial_type_id' => 1,
2713 'payment_instrument_id' => 1,
2714 'non_deductible_amount' => 10.00,
2715 'fee_amount' => 50.00,
2716 'net_amount' => 90.00,
2718 'invoice_id' => 67890,
2720 'contribution_status_id' => 1,
2721 'skipCleanMoney' => 1,
2723 'api.contribution.create.1' => [
2724 'receive_date' => '2011-01-01',
2725 'total_amount' => 120.00,
2726 'financial_type_id' => 1,
2727 'payment_instrument_id' => 1,
2728 'non_deductible_amount' => 10.00,
2729 'fee_amount' => 50.00,
2730 'net_amount' => 90.00,
2732 'invoice_id' => 67830,
2734 'contribution_status_id' => 1,
2735 'skipCleanMoney' => 1,
2737 'api.website.create' => [
2739 'url' => "http://civicrm.org",
2742 'custom_' . $ids['custom_field_id'] => "value 1",
2743 'custom_' . $moreIDs['custom_field_id'][0] => "value 2",
2744 'custom_' . $moreIDs['custom_field_id'][1] => "warm beer",
2745 'custom_' . $andMoreIDs['custom_field_id'][1] => "vegemite",
2748 $result = $this->callAPISuccess('Contact', 'create', $params);
2749 $result = $this->callAPISuccess('Contact', 'create', [
2750 'contact_type' => 'Individual',
2751 'id' => $result['id'],
2753 $moreIDs['custom_field_id'][0] => "value 3",
2755 $ids['custom_field_id'] => "value 4",
2759 'id' => $result['id'],
2760 'api.website.getValue' => ['return' => 'url'],
2761 'api.Contribution.getCount' => [],
2762 'api.CustomValue.get' => 1,
2764 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
2766 $this->customGroupDelete($ids['custom_group_id']);
2767 $this->customGroupDelete($moreIDs['custom_group_id']);
2768 $this->customGroupDelete($andMoreIDs['custom_group_id']);
2769 $this->assertEquals(0, $result['values'][$result['id']]['api.CustomValue.get']['is_error']);
2770 $this->assertEquals('http://civicrm.org', $result['values'][$result['id']]['api.website.getValue']);
2774 * Test checks usage of $values to pick & choose inputs.
2776 * Api3 Only - chaining syntax is too funky for v4 (assuming entityTag "entity_id" field will be filled by magic)
2778 public function testChainingValuesCreate() {
2779 $description = "This demonstrates the usage of chained api functions. Specifically it has one 'parent function' &
2780 2 child functions - one receives values from the parent (Contact) and the other child (Tag).";
2781 $subfile = "APIChainedArrayValuesFromSiblingFunction";
2783 'display_name' => 'batman',
2784 'contact_type' => 'Individual',
2785 'api.tag.create' => [
2786 'name' => '$value.id',
2787 'description' => '$value.display_name',
2788 'format.only_id' => 1,
2790 'api.entity_tag.create' => ['tag_id' => '$value.api.tag.create'],
2792 $result = $this->callAPIAndDocument('Contact', 'Create', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
2793 $this->assertEquals(0, $result['values'][$result['id']]['api.entity_tag.create']['is_error']);
2795 $tablesToTruncate = [
2798 'civicrm_entity_tag',
2801 $this->quickCleanup($tablesToTruncate, TRUE);
2805 * Test TrueFalse format - I couldn't come up with an easy way to get an error on Get.
2807 * @param int $version
2809 * @dataProvider versionThreeAndFour
2811 public function testContactGetFormatIsSuccessTrue($version) {
2812 $this->_apiversion
= $version;
2813 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2814 $description = "This demonstrates use of the 'format.is_success' param.
2815 This param causes only the success or otherwise of the function to be returned as BOOLEAN";
2816 $subfile = "FormatIsSuccess_True";
2817 $params = ['id' => $contactID, 'format.is_success' => 1];
2818 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
2819 $this->assertEquals(1, $result);
2820 $this->callAPISuccess('Contact', 'Delete', $params);
2824 * Test TrueFalse format.
2826 * @param int $version
2828 * @dataProvider versionThreeAndFour
2830 public function testContactCreateFormatIsSuccessFalse($version) {
2831 $this->_apiversion
= $version;
2833 $description = "This demonstrates use of the 'format.is_success' param.
2834 This param causes only the success or otherwise of the function to be returned as BOOLEAN";
2835 $subfile = "FormatIsSuccess_Fail";
2836 $params = ['id' => 500, 'format.is_success' => 1];
2837 $result = $this->callAPIAndDocument('Contact', 'Create', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
2838 $this->assertEquals(0, $result);
2842 * Test long display names.
2844 * @see https://issues.civicrm.org/jira/browse/CRM-21258
2846 * @param int $version
2848 * @dataProvider versionThreeAndFour
2850 public function testContactCreateLongDisplayName($version) {
2851 $this->_apiversion
= $version;
2852 $result = $this->callAPISuccess('Contact', 'Create', [
2853 'first_name' => str_pad('a', 64, 'a'),
2854 'last_name' => str_pad('a', 64, 'a'),
2855 'contact_type' => 'Individual',
2857 $this->assertEquals('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', $result['values'][$result['id']]['display_name']);
2858 $this->assertEquals('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', $result['values'][$result['id']]['sort_name']);
2862 * Test that we can set the sort name via the api or alter it via a hook.
2864 * As of writing this is being fixed for Organization & Household but it makes sense to do for individuals too.
2866 * @param int $version
2868 * @dataProvider versionThreeAndFour
2870 public function testCreateAlterSortName($version) {
2871 $this->_apiversion
= $version;
2872 $organizationID = $this->organizationCreate(['organization_name' => 'The Justice League', 'sort_name' => 'Justice League, The']);
2873 $organization = $this->callAPISuccessGetSingle('Contact', ['return' => ['sort_name', 'display_name'], 'id' => $organizationID]);
2874 $this->assertEquals('Justice League, The', $organization['sort_name']);
2875 $this->assertEquals('The Justice League', $organization['display_name']);
2876 $this->hookClass
->setHook('civicrm_pre', [$this, 'killTheJusticeLeague']);
2877 $this->organizationCreate(['id' => $organizationID, 'sort_name' => 'Justice League, The']);
2878 $organization = $this->callAPISuccessGetSingle('Contact', ['return' => ['sort_name', 'display_name', 'is_deceased'], 'id' => $organizationID]);
2879 $this->assertEquals('Steppenwolf wuz here', $organization['display_name']);
2880 $this->assertEquals('Steppenwolf wuz here', $organization['sort_name']);
2881 $this->assertEquals(1, $organization['is_deceased']);
2883 $householdID = $this->householdCreate();
2884 $household = $this->callAPISuccessGetSingle('Contact', ['return' => ['sort_name', 'display_name'], 'id' => $householdID]);
2885 $this->assertEquals('Steppenwolf wuz here', $household['display_name']);
2886 $this->assertEquals('Steppenwolf wuz here', $household['sort_name']);
2890 * Implements hook_pre().
2892 public function killTheJusticeLeague($op, $entity, $id, &$params) {
2893 $params['sort_name'] = 'Steppenwolf wuz here';
2894 $params['display_name'] = 'Steppenwolf wuz here';
2895 $params['is_deceased'] = 1;
2899 * Test Single Entity format.
2901 * @param int $version
2903 * @dataProvider versionThreeAndFour
2905 public function testContactGetSingleEntityArray($version) {
2906 $this->_apiversion
= $version;
2907 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2908 $description = "This demonstrates use of the 'format.single_entity_array' param.
2909 This param causes the only contact to be returned as an array without the other levels.
2910 It will be ignored if there is not exactly 1 result";
2911 $subfile = "GetSingleContact";
2912 $result = $this->callAPIAndDocument('Contact', 'GetSingle', ['id' => $contactID], __FUNCTION__
, __FILE__
, $description, $subfile);
2913 $this->assertEquals('Mr. Test Contact II', $result['display_name']);
2914 $this->callAPISuccess('Contact', 'Delete', ['id' => $contactID]);
2918 * Test Single Entity format.
2920 * @param int $version
2922 * @dataProvider versionThreeAndFour
2924 public function testContactGetFormatCountOnly($version) {
2925 $this->_apiversion
= $version;
2926 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2927 $description = "This demonstrates use of the 'getCount' action.
2928 This param causes the count of the only function to be returned as an integer.";
2929 $params = ['id' => $contactID];
2930 $result = $this->callAPIAndDocument('Contact', 'GetCount', $params, __FUNCTION__
, __FILE__
, $description,
2932 $this->assertEquals('1', $result);
2933 $this->callAPISuccess('Contact', 'Delete', $params);
2937 * Test id only format.
2939 * @param int $version
2941 * @dataProvider versionThreeAndFour
2943 public function testContactGetFormatIDOnly($version) {
2944 $this->_apiversion
= $version;
2945 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2946 $description = "This demonstrates use of the 'format.id_only' param.
2947 This param causes the id of the only entity to be returned as an integer.
2948 It will be ignored if there is not exactly 1 result";
2949 $subfile = "FormatOnlyID";
2950 $params = ['id' => $contactID, 'format.only_id' => 1];
2951 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__
, __FILE__
, $description, $subfile);
2952 $this->assertEquals($contactID, $result);
2953 $this->callAPISuccess('Contact', 'Delete', $params);
2957 * Test id only format.
2959 * @param int $version
2961 * @dataProvider versionThreeAndFour
2963 public function testContactGetFormatSingleValue($version) {
2964 $this->_apiversion
= $version;
2965 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2966 $description = "This demonstrates use of the 'format.single_value' param.
2967 This param causes only a single value of the only entity to be returned as an string.
2968 It will be ignored if there is not exactly 1 result";
2969 $subFile = "FormatSingleValue";
2970 $params = ['id' => $contactID, 'return' => 'display_name'];
2971 $result = $this->callAPIAndDocument('Contact', 'getvalue', $params, __FUNCTION__
, __FILE__
, $description, $subFile);
2972 $this->assertEquals('Mr. Test Contact II', $result);
2973 $this->callAPISuccess('Contact', 'Delete', $params);
2977 * Test that permissions are respected when creating contacts.
2979 * @param int $version
2981 * @dataProvider versionThreeAndFour
2983 public function testContactCreationPermissions($version) {
2984 $this->_apiversion
= $version;
2986 'contact_type' => 'Individual',
2987 'first_name' => 'Foo',
2988 'last_name' => 'Bear',
2989 'check_permissions' => TRUE,
2991 $config = CRM_Core_Config
::singleton();
2992 $config->userPermissionClass
->permissions
= ['access CiviCRM'];
2993 $result = $this->callAPIFailure('contact', 'create', $params);
2994 $this->assertContains('failed', $result['error_message'], 'lacking permissions should not be enough to create a contact');
2996 $config->userPermissionClass
->permissions
= ['access CiviCRM', 'add contacts', 'import contacts'];
2997 $this->callAPISuccess('contact', 'create', $params);
3001 * Test that delete with skip undelete respects permissions.
3004 * @throws \CRM_Core_Exception
3006 public function testContactDeletePermissions() {
3007 $contactID = $this->individualCreate();
3008 $tag = $this->callAPISuccess('Tag', 'create', ['name' => 'to be deleted']);
3009 $this->callAPISuccess('EntityTag', 'create', ['entity_id' => $contactID, 'tag_id' => $tag['id']]);
3010 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access CiviCRM'];
3011 $this->callAPIFailure('Contact', 'delete', [
3013 'check_permissions' => 1,
3014 'skip_undelete' => 1,
3016 $this->callAPISuccess('Contact', 'delete', [
3018 'check_permissions' => 0,
3019 'skip_undelete' => 1,
3021 $this->callAPISuccessGetCount('EntityTag', ['entity_id' => $contactID], 0);
3025 * Test update with check permissions set.
3027 * @param int $version
3029 * @dataProvider versionThreeAndFour
3031 public function testContactUpdatePermissions($version) {
3032 $this->_apiversion
= $version;
3034 'contact_type' => 'Individual',
3035 'first_name' => 'Foo',
3036 'last_name' => 'Bear',
3037 'check_permissions' => TRUE,
3039 $result = $this->callAPISuccess('contact', 'create', $params);
3040 $config = CRM_Core_Config
::singleton();
3042 'id' => $result['id'],
3043 'contact_type' => 'Individual',
3044 'last_name' => 'Bar',
3045 'check_permissions' => TRUE,
3048 $config->userPermissionClass
->permissions
= ['access CiviCRM'];
3049 $result = $this->callAPIFailure('contact', 'update', $params);
3050 $this->assertEquals('Permission denied to modify contact record', $result['error_message']);
3052 $config->userPermissionClass
->permissions
= [
3055 'view all contacts',
3056 'edit all contacts',
3059 $this->callAPISuccess('contact', 'update', $params);
3063 * Test contact proximity api.
3065 public function testContactProximity() {
3066 // first create a contact with a SF location with a specific
3068 $contactID = $this->organizationCreate();
3070 // now create the address
3072 'street_address' => '123 Main Street',
3073 'city' => 'San Francisco',
3075 'country_id' => 1228,
3076 'state_province_id' => 1004,
3077 'geo_code_1' => '37.79',
3078 'geo_code_2' => '-122.40',
3079 'location_type_id' => 1,
3080 'contact_id' => $contactID,
3083 $result = $this->callAPISuccess('address', 'create', $params);
3084 $this->assertEquals(1, $result['count']);
3086 // now do a proximity search with a close enough geocode and hope to match
3087 // that specific contact only!
3090 'longitude' => -122.3,
3094 $result = $this->callAPISuccess('contact', 'proximity', $proxParams);
3095 $this->assertEquals(1, $result['count']);
3099 * Test that Ajax API permission is sufficient to access getquick api.
3101 * (note that getquick api is required for autocomplete & has ACL permissions applied)
3103 public function testGetquickPermissionCRM13744() {
3104 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access CiviEvent'];
3105 $this->callAPIFailure('contact', 'getquick', ['name' => 'b', 'check_permissions' => TRUE]);
3106 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access CiviCRM'];
3107 $this->callAPISuccess('contact', 'getquick', ['name' => 'b', 'check_permissions' => TRUE]);
3108 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access AJAX API'];
3109 $this->callAPISuccess('contact', 'getquick', ['name' => 'b', 'check_permissions' => TRUE]);
3113 * Test that getquick returns contacts with an exact first name match first.
3115 * The search string 'b' & 'bob' both return ordered by sort_name if includeOrderByClause
3116 * is true (default) but if it is false then matches are returned in ID order.
3118 * @dataProvider getSearchSortOptions
3120 public function testGetQuickExactFirst($searchParameters, $settings, $firstContact, $secondContact = NULL) {
3121 $this->getQuickSearchSampleData();
3122 $this->callAPISuccess('Setting', 'create', $settings);
3123 $result = $this->callAPISuccess('contact', 'getquick', $searchParameters);
3124 $this->assertEquals($firstContact, $result['values'][0]['sort_name']);
3125 $this->assertEquals($secondContact, $result['values'][1]['sort_name']);
3126 $this->callAPISuccess('Setting', 'create', ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE]);
3129 public function getSearchSortOptions() {
3130 $firstAlphabeticalContactBySortName = 'A Bobby, Bobby';
3131 $secondAlphabeticalContactBySortName = 'Aadvark, Bob';
3132 $secondAlphabeticalContactWithEmailBySortName = 'Bob, Bob';
3133 $firstAlphabeticalContactFirstNameBob = 'Aadvark, Bob';
3134 $secondAlphabeticalContactFirstNameBob = 'Bob, Bob';
3135 $firstByIDContactFirstNameBob = 'Bob, Bob';
3136 $secondByIDContactFirstNameBob = 'K Bobby, Bob';
3137 $firstContactByID = 'Bob, Bob';
3138 $secondContactByID = 'E Bobby, Bobby';
3139 $bobLikeEmail = 'A Bobby, Bobby';
3142 'empty_search_basic' => [
3143 'search_parameters' => ['name' => '%'],
3144 'settings' => ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE],
3145 'first_contact' => $firstAlphabeticalContactBySortName,
3146 'second_contact' => $secondAlphabeticalContactBySortName,
3148 'empty_search_basic_no_wildcard' => [
3149 'search_parameters' => ['name' => '%'],
3150 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3151 'first_contact' => $firstAlphabeticalContactBySortName,
3152 'second_contact' => $secondAlphabeticalContactBySortName,
3154 'single_letter_search_basic' => [
3155 'search_parameters' => ['name' => 'b'],
3156 'settings' => ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE],
3157 'first_contact' => $firstAlphabeticalContactBySortName,
3158 'second_contact' => $secondAlphabeticalContactBySortName,
3160 'bob_search_basic' => [
3161 'search_parameters' => ['name' => 'bob'],
3162 'settings' => ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE],
3163 'first_contact' => $firstAlphabeticalContactBySortName,
3164 'second_contact' => $secondAlphabeticalContactBySortName,
3166 // This test has been disabled as is proving to be problematic to reproduce due to MySQL sorting issues between different versions
3167 // 'bob_search_no_orderby' => array(
3168 // 'search_parameters' => array('name' => 'bob'),
3169 // 'settings' => array('includeWildCardInName' => TRUE, 'includeOrderByClause' => FALSE),
3170 // 'first_contact' => $firstContactByID,
3171 // 'second_contact' => $secondContactByID,
3173 'bob_search_no_wildcard' => [
3174 'search_parameters' => ['name' => 'bob'],
3175 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3176 'second_contact' => $bobLikeEmail,
3177 'first_contact' => $secondAlphabeticalContactFirstNameBob,
3179 // This should be the same as just no wildcard as if we had an exactMatch while searching by
3180 // sort name it would rise to the top CRM-19547
3181 'bob_search_no_wildcard_no_orderby' => [
3182 'search_parameters' => ['name' => 'bob'],
3183 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3184 'second_contact' => $bobLikeEmail,
3185 'first_contact' => $secondAlphabeticalContactFirstNameBob,
3187 'first_name_search_basic' => [
3188 'search_parameters' => ['name' => 'bob', 'field_name' => 'first_name'],
3189 'settings' => ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE],
3190 'first_contact' => $firstAlphabeticalContactFirstNameBob,
3191 'second_contact' => $secondAlphabeticalContactFirstNameBob,
3193 'first_name_search_no_wildcard' => [
3194 'search_parameters' => ['name' => 'bob', 'field_name' => 'first_name'],
3195 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3196 'first_contact' => $firstAlphabeticalContactFirstNameBob,
3197 'second_contact' => $secondAlphabeticalContactFirstNameBob,
3199 // This test has been disabled as is proving to be problematic to reproduce due to MySQL sorting issues between different versions
3200 //'first_name_search_no_orderby' => array(
3201 // 'search_parameters' => array('name' => 'bob', 'field_name' => 'first_name'),
3202 // 'settings' => array('includeWildCardInName' => TRUE, 'includeOrderByClause' => FALSE),
3203 // 'first_contact' => $firstByIDContactFirstNameBob,
3204 // 'second_contact' => $secondByIDContactFirstNameBob,
3206 'email_search_basic' => [
3207 'search_parameters' => ['name' => 'bob', 'field_name' => 'email', 'table_name' => 'eml'],
3208 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3209 'first_contact' => $firstAlphabeticalContactBySortName,
3210 'second_contact' => $secondAlphabeticalContactWithEmailBySortName,
3216 * Test that getquick returns contacts with an exact first name match first.
3218 public function testGetQuickEmail() {
3219 $this->getQuickSearchSampleData();
3220 $loggedInContactID = $this->createLoggedInUser();
3221 $result = $this->callAPISuccess('contact', 'getquick', [
3225 'A Bobby, Bobby :: bob@bobby.com',
3226 'Bob, Bob :: bob@bob.com',
3228 'H Bobby, Bobby :: bob@h.com',
3230 $this->callAPISuccessGetValue('Contact', ['id' => $loggedInContactID, 'return' => 'last_name']) . ', Logged In :: anthony_anderson@civicrm.org',
3232 $this->assertEquals(6, $result['count']);
3233 foreach ($expectedData as $index => $value) {
3234 $this->assertEquals($value, $result['values'][$index]['data']);
3236 $result = $this->callAPISuccess('contact', 'getquick', [
3240 'H Bobby, Bobby :: bob@h.com',
3242 foreach ($expectedData as $index => $value) {
3243 $this->assertEquals($value, $result['values'][$index]['data']);
3245 $this->callAPISuccess('Setting', 'create', ['includeWildCardInName' => FALSE]);
3246 $result = $this->callAPISuccess('contact', 'getquick', [
3249 $this->callAPISuccess('Setting', 'create', ['includeWildCardInName' => TRUE]);
3250 $this->assertEquals(0, $result['count']);
3254 * Test that getquick returns contacts with an exact first name match first.
3256 public function testGetQuickEmailACL() {
3257 $this->getQuickSearchSampleData();
3258 $loggedInContactID = $this->createLoggedInUser();
3259 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= [];
3260 $result = $this->callAPISuccess('contact', 'getquick', [
3263 $this->assertEquals(0, $result['count']);
3265 $this->hookClass
->setHook('civicrm_aclWhereClause', [$this, 'aclWhereNoBobH']);
3266 CRM_Contact_BAO_Contact_Permission
::cache($loggedInContactID, CRM_Core_Permission
::VIEW
, TRUE);
3267 $result = $this->callAPISuccess('contact', 'getquick', [
3271 // Without the acl it would be 6 like the previous email getquick test.
3272 $this->assertEquals(5, $result['count']);
3274 'A Bobby, Bobby :: bob@bobby.com',
3275 'Bob, Bob :: bob@bob.com',
3278 $this->callAPISuccessGetValue('Contact', ['id' => $loggedInContactID, 'return' => 'last_name']) . ', Logged In :: anthony_anderson@civicrm.org',
3280 foreach ($expectedData as $index => $value) {
3281 $this->assertEquals($value, $result['values'][$index]['data']);
3286 * Test that getquick returns contacts with an exact first name match first.
3288 public function testGetQuickExternalID() {
3289 $this->getQuickSearchSampleData();
3290 $result = $this->callAPISuccess('contact', 'getquick', [
3292 'field_name' => 'external_identifier',
3293 'table_name' => 'cc',
3295 $this->assertEquals(0, $result['count']);
3296 $result = $this->callAPISuccess('contact', 'getquick', [
3298 'field_name' => 'external_identifier',
3299 'table_name' => 'cc',
3301 $this->assertEquals(1, $result['count']);
3302 $this->assertEquals('Bob, Bob', $result['values'][0]['sort_name']);
3306 * Test that getquick returns contacts with an exact first name match first.
3308 public function testGetQuickID() {
3309 $max = CRM_Core_DAO
::singleValueQuery("SELECT max(id) FROM civicrm_contact");
3310 $this->getQuickSearchSampleData();
3311 $result = $this->callAPISuccess('contact', 'getquick', [
3313 'field_name' => 'id',
3314 'table_name' => 'cc',
3316 $this->assertEquals(1, $result['count']);
3317 $this->assertEquals('E Bobby, Bobby', $result['values'][0]['sort_name']);
3318 $result = $this->callAPISuccess('contact', 'getquick', [
3320 'field_name' => 'contact_id',
3321 'table_name' => 'cc',
3323 $this->assertEquals(1, $result['count']);
3324 $this->assertEquals('E Bobby, Bobby', $result['values'][0]['sort_name']);
3328 * Test that getquick returns contacts with different cases of phone substring.
3330 public function testGetQuickPhone() {
3331 $this->getQuickSearchSampleData();
3336 'field_name' => 'phone_numeric',
3347 'field_name' => 'phone_numeric',
3357 'field_name' => 'phone_numeric',
3366 'name' => '8a7abc6',
3367 'field_name' => 'phone_numeric',
3377 foreach ($criterias as $criteria) {
3378 $result = $this->callAPISuccess('contact', 'getquick', $criteria['criteria']);
3379 $this->assertEquals($result['count'], $criteria['count']);
3380 foreach ($criteria['sort_names'] as $key => $sortName) {
3381 $this->assertEquals($sortName, $result['values'][$key]['sort_name']);
3387 * Test that getquick returns contacts with an exact first name match first.
3389 * Depending on the setting the sort name sort might click in next or not - test!
3391 public function testGetQuickFirstName() {
3392 $this->getQuickSearchSampleData();
3393 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => TRUE]);
3394 $result = $this->callAPISuccess('contact', 'getquick', [
3396 'field_name' => 'first_name',
3397 'table_name' => 'cc',
3406 foreach ($expected as $index => $value) {
3407 $this->assertEquals($value, $result['values'][$index]['sort_name']);
3409 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => FALSE]);
3410 $result = $this->callAPISuccess('contact', 'getquick', ['name' => 'bob']);
3411 $this->assertEquals('Bob, Bob', $result['values'][0]['sort_name']);
3412 // This test has been disabled as is proving to be problematic to reproduce due to MySQL sorting issues between different versions
3413 //$this->assertEquals('E Bobby, Bobby', $result['values'][1]['sort_name']);
3417 * Test that getquick applies ACLs.
3419 public function testGetQuickFirstNameACLs() {
3420 $this->getQuickSearchSampleData();
3421 $userID = $this->createLoggedInUser();
3422 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => TRUE, 'search_autocomplete_count' => 15]);
3423 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= [];
3424 $result = $this->callAPISuccess('contact', 'getquick', [
3426 'field_name' => 'first_name',
3427 'table_name' => 'cc',
3429 $this->assertEquals(0, $result['count']);
3431 $this->hookClass
->setHook('civicrm_aclWhereClause', [$this, 'aclWhereNoBobH']);
3432 CRM_Contact_BAO_Contact_Permission
::cache($userID, CRM_Core_Permission
::VIEW
, TRUE);
3433 $result = $this->callAPISuccess('contact', 'getquick', [
3435 'field_name' => 'first_name',
3436 'table_name' => 'cc',
3438 $this->assertEquals('K Bobby, Bob', $result['values'][2]['sort_name']);
3439 // Without the ACL 9 would be bob@h.com.
3440 $this->assertEquals('I Bobby, Bobby', $result['values'][10]['sort_name']);
3444 * Full results returned.
3446 * @implements CRM_Utils_Hook::aclWhereClause
3448 * @param string $type
3449 * @param array $tables
3450 * @param array $whereTables
3451 * @param int $contactID
3452 * @param string $where
3454 public function aclWhereNoBobH($type, &$tables, &$whereTables, &$contactID, &$where) {
3455 $where = " (email <> 'bob@h.com' OR email IS NULL) ";
3456 $whereTables['civicrm_email'] = "LEFT JOIN civicrm_email e ON contact_a.id = e.contact_id";
3460 * Test that getquick returns contacts with an exact last name match first.
3462 public function testGetQuickLastName() {
3463 $this->getQuickSearchSampleData();
3464 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => TRUE]);
3465 $result = $this->callAPISuccess('contact', 'getquick', [
3467 'field_name' => 'last_name',
3468 'table_name' => 'cc',
3476 foreach ($expected as $index => $value) {
3477 $this->assertEquals($value, $result['values'][$index]['sort_name']);
3479 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => FALSE]);
3480 $result = $this->callAPISuccess('contact', 'getquick', ['name' => 'bob']);
3481 $this->assertEquals('Bob, Bob :: bob@bob.com', $result['values'][0]['data']);
3485 * Test that getquick returns contacts by city.
3487 public function testGetQuickCity() {
3488 $this->getQuickSearchSampleData();
3489 $result = $this->callAPISuccess('contact', 'getquick', [
3491 'field_name' => 'city',
3492 'table_name' => 'sts',
3494 $this->assertEquals('B Bobby, Bobby :: Toronto', $result['values'][0]['data']);
3495 $result = $this->callAPISuccess('contact', 'getquick', [
3497 'field_name' => 'city',
3498 'table_name' => 'sts',
3500 $this->assertEquals('B Bobby, Bobby :: Toronto', $result['values'][0]['data']);
3501 $this->assertEquals('C Bobby, Bobby :: Whanganui', $result['values'][1]['data']);
3505 * Test that getquick doesn't work with field_name=api_key
3507 * @throws \CRM_Core_Exception
3509 public function testGetQuickApiKey() {
3510 $this->callAPISuccess('Contact', 'create', [
3511 'contact_type' => 'Individual',
3512 'email' => 'apiuser@example.com',
3513 'api_key' => 'hunter2',
3515 $result = $this->callAPIFailure('Contact', 'getquick', [
3517 'field_name' => 'api_key',
3518 ], 'Illegal value "api_key" for parameter "field_name"');
3522 * Set up some sample data for testing quicksearch.
3524 public function getQuickSearchSampleData() {
3526 ['first_name' => 'Bob', 'last_name' => 'Bob', 'external_identifier' => 'abc', 'email' => 'bob@bob.com'],
3527 ['first_name' => 'Bobby', 'last_name' => 'E Bobby', 'external_identifier' => 'abcd'],
3529 'first_name' => 'Bobby',
3530 'last_name' => 'B Bobby',
3531 'external_identifier' => 'bcd',
3532 'api.address.create' => [
3533 'street_address' => 'Sesame Street',
3534 'city' => 'Toronto',
3535 'location_type_id' => 1,
3539 'first_name' => 'Bobby',
3540 'last_name' => 'C Bobby',
3541 'external_identifier' => 'bcde',
3542 'api.address.create' => [
3543 'street_address' => 'Te huarahi',
3544 'city' => 'Whanganui',
3545 'location_type_id' => 1,
3548 ['first_name' => 'Bobby', 'last_name' => 'D Bobby', 'external_identifier' => 'efg'],
3549 ['first_name' => 'Bobby', 'last_name' => 'A Bobby', 'external_identifier' => 'hij', 'email' => 'bob@bobby.com'],
3550 ['first_name' => 'Bobby', 'last_name' => 'F Bobby', 'external_identifier' => 'klm'],
3551 ['first_name' => 'Bobby', 'last_name' => 'G Bobby', 'external_identifier' => 'nop'],
3552 ['first_name' => 'Bobby', 'last_name' => 'H Bobby', 'external_identifier' => 'qrs', 'email' => 'bob@h.com'],
3554 'first_name' => 'Bobby',
3555 'last_name' => 'I Bobby',
3556 'api.phone.create' => [
3557 'phone' => '876-123',
3558 'phone_ext' => '444',
3559 "phone_type_id" => "Phone",
3560 'location_type_id' => 1,
3565 'first_name' => 'Bobby',
3566 'last_name' => 'J Bobby',
3567 'api.phone.create' => [
3568 'phone' => '87-6-234',
3569 'phone_ext' => '134',
3570 "phone_type_id" => "Phone",
3571 'location_type_id' => 1,
3575 ['first_name' => 'Bob', 'last_name' => 'K Bobby', 'external_identifier' => 'bcdef'],
3576 ['first_name' => 'Bob', 'last_name' => 'Aadvark'],
3578 foreach ($contacts as $type => $contact) {
3579 $contact['contact_type'] = 'Individual';
3580 $this->callAPISuccess('Contact', 'create', $contact);
3585 * Test get ref api - gets a list of references to an entity.
3587 public function testGetReferenceCounts() {
3588 $result = $this->callAPISuccess('Contact', 'create', [
3589 'first_name' => 'Testily',
3590 'last_name' => 'McHaste',
3591 'contact_type' => 'Individual',
3592 'api.Address.replace' => [
3595 'api.Email.replace' => [
3598 'email' => 'spam@dev.null',
3600 'location_type_id' => 1,
3604 'api.Phone.replace' => [
3607 'phone' => '234-567-0001',
3609 'location_type_id' => 1,
3612 'phone' => '234-567-0002',
3614 'location_type_id' => 1,
3620 //$dao = new CRM_Contact_BAO_Contact();
3621 //$dao->id = $result['id'];
3622 //$this->assertTrue((bool) $dao->find(TRUE));
3624 //$refCounts = $dao->getReferenceCounts();
3625 //$this->assertTrue(is_array($refCounts));
3626 //$refCountsIdx = CRM_Utils_Array::index(array('name'), $refCounts);
3628 $refCounts = $this->callAPISuccess('Contact', 'getrefcount', [
3629 'id' => $result['id'],
3631 $refCountsIdx = CRM_Utils_Array
::index(['name'], $refCounts['values']);
3633 $this->assertEquals(1, $refCountsIdx['sql:civicrm_email:contact_id']['count']);
3634 $this->assertEquals('civicrm_email', $refCountsIdx['sql:civicrm_email:contact_id']['table']);
3635 $this->assertEquals(2, $refCountsIdx['sql:civicrm_phone:contact_id']['count']);
3636 $this->assertEquals('civicrm_phone', $refCountsIdx['sql:civicrm_phone:contact_id']['table']);
3637 $this->assertTrue(!isset($refCountsIdx['sql:civicrm_address:contact_id']));
3641 * Test the use of sql operators.
3643 * @param int $version
3645 * @dataProvider versionThreeAndFour
3647 public function testSQLOperatorsOnContactAPI($version) {
3648 $this->_apiversion
= $version;
3649 $this->individualCreate();
3650 $this->organizationCreate();
3651 $this->householdCreate();
3652 $contacts = $this->callAPISuccess('contact', 'get', ['legal_name' => ['IS NOT NULL' => TRUE]]);
3653 $this->assertEquals($contacts['count'], CRM_Core_DAO
::singleValueQuery('select count(*) FROM civicrm_contact WHERE legal_name IS NOT NULL'));
3654 $contacts = $this->callAPISuccess('contact', 'get', ['legal_name' => ['IS NULL' => TRUE]]);
3655 $this->assertEquals($contacts['count'], CRM_Core_DAO
::singleValueQuery('select count(*) FROM civicrm_contact WHERE legal_name IS NULL'));
3659 * CRM-14743 - test api respects search operators.
3661 * @param int $version
3663 * @dataProvider versionThreeAndFour
3665 public function testGetModifiedDateByOperators($version) {
3666 $this->_apiversion
= $version;
3667 $preExistingContactCount = CRM_Core_DAO
::singleValueQuery('select count(*) FROM civicrm_contact');
3668 $contact1 = $this->individualCreate();
3669 $sql = "UPDATE civicrm_contact SET created_date = '2012-01-01', modified_date = '2013-01-01' WHERE id = " . $contact1;
3670 CRM_Core_DAO
::executeQuery($sql);
3671 $contact2 = $this->individualCreate();
3672 $sql = "UPDATE civicrm_contact SET created_date = '2012-02-01', modified_date = '2013-02-01' WHERE id = " . $contact2;
3673 CRM_Core_DAO
::executeQuery($sql);
3674 $contact3 = $this->householdCreate();
3675 $sql = "UPDATE civicrm_contact SET created_date = '2012-03-01', modified_date = '2013-03-01' WHERE id = " . $contact3;
3676 CRM_Core_DAO
::executeQuery($sql);
3677 $contacts = $this->callAPISuccess('contact', 'get', ['modified_date' => ['<' => '2014-01-01']]);
3678 $this->assertEquals($contacts['count'], 3);
3679 $contacts = $this->callAPISuccess('contact', 'get', ['modified_date' => ['>' => '2014-01-01']]);
3680 $this->assertEquals($contacts['count'], $preExistingContactCount);
3684 * CRM-14743 - test api respects search operators.
3686 * @param int $version
3688 * @dataProvider versionThreeAndFour
3689 * @throws \CRM_Core_Exception
3691 public function testGetCreatedDateByOperators($version) {
3692 $this->_apiversion
= $version;
3693 $preExistingContactCount = CRM_Core_DAO
::singleValueQuery('select count(*) FROM civicrm_contact');
3694 $contact1 = $this->individualCreate();
3695 $sql = "UPDATE civicrm_contact SET created_date = '2012-01-01' WHERE id = " . $contact1;
3696 CRM_Core_DAO
::executeQuery($sql);
3697 $contact2 = $this->individualCreate();
3698 $sql = "UPDATE civicrm_contact SET created_date = '2012-02-01' WHERE id = " . $contact2;
3699 CRM_Core_DAO
::executeQuery($sql);
3700 $contact3 = $this->householdCreate();
3701 $sql = "UPDATE civicrm_contact SET created_date = '2012-03-01' WHERE id = " . $contact3;
3702 CRM_Core_DAO
::executeQuery($sql);
3703 $contacts = $this->callAPISuccess('contact', 'get', ['created_date' => ['<' => '2014-01-01']]);
3704 $this->assertEquals($contacts['count'], 3);
3705 $contacts = $this->callAPISuccess('contact', 'get', ['created_date' => ['>' => '2014-01-01']]);
3706 $this->assertEquals($contacts['count'], $preExistingContactCount);
3710 * CRM-14263 check that API is not affected by search profile related bug.
3712 * @throws \CRM_Core_Exception
3714 public function testReturnCityProfile() {
3715 $contactID = $this->individualCreate();
3716 Civi
::settings()->set('defaultSearchProfileID', 1);
3717 $this->callAPISuccess('address', 'create', [
3718 'contact_id' => $contactID,
3719 'city' => 'Cool City',
3720 'location_type_id' => 1,
3722 $result = $this->callAPISuccess('contact', 'get', ['city' => 'Cool City', 'return' => 'contact_type']);
3723 $this->assertEquals(1, $result['count']);
3727 * CRM-15443 - ensure getlist api does not return deleted contacts.
3729 * @throws \CRM_Core_Exception
3731 public function testGetlistExcludeConditions() {
3733 $contact = $this->individualCreate(['last_name' => $name]);
3734 $this->individualCreate(['last_name' => $name, 'is_deceased' => 1]);
3735 $this->individualCreate(['last_name' => $name, 'is_deleted' => 1]);
3736 // We should get all but the deleted contact.
3737 $result = $this->callAPISuccess('contact', 'getlist', ['input' => $name]);
3738 $this->assertEquals(2, $result['count']);
3739 // Force-exclude the deceased contact.
3740 $result = $this->callAPISuccess('contact', 'getlist', [
3742 'params' => ['is_deceased' => 0],
3744 $this->assertEquals(1, $result['count']);
3745 $this->assertEquals($contact, $result['values'][0]['id']);
3749 * Test contact getactions.
3751 public function testGetActions() {
3752 $description = "Getting the available actions for an entity.";
3753 $result = $this->callAPIAndDocument($this->_entity
, 'getactions', [], __FUNCTION__
, __FILE__
, $description);
3777 foreach ($expected as $action) {
3778 $this->assertTrue(in_array($action, $result['values']), "Expected action $action");
3780 foreach ($deprecated as $action) {
3781 $this->assertArrayKeyExists($action, $result['deprecated']);
3786 * Test the duplicate check function.
3788 public function testDuplicateCheck() {
3790 'first_name' => 'Harry',
3791 'last_name' => 'Potter',
3792 'email' => 'harry@hogwarts.edu',
3793 'contact_type' => 'Individual',
3795 $this->callAPISuccess('Contact', 'create', $harry);
3796 $result = $this->callAPISuccess('Contact', 'duplicatecheck', [
3800 $this->assertEquals(1, $result['count']);
3801 $result = $this->callAPISuccess('Contact', 'duplicatecheck', [
3803 'first_name' => 'Harry',
3804 'last_name' => 'Potter',
3805 'email' => 'no5@privet.drive',
3806 'contact_type' => 'Individual',
3809 $this->assertEquals(0, $result['count']);
3810 $this->callAPIFailure('Contact', 'create', array_merge($harry, ['dupe_check' => 1]));
3814 * Test that duplicates can be found even when phone type is specified.
3816 * @param string $phoneKey
3818 * @dataProvider getPhoneStrings
3820 * @throws \CRM_Core_Exception
3822 public function testGetMatchesPhoneWithType($phoneKey) {
3823 $ruleGroup = $this->createRuleGroup();
3824 $this->callAPISuccess('Rule', 'create', [
3825 'dedupe_rule_group_id' => $ruleGroup['id'],
3826 'rule_table' => 'civicrm_phone',
3827 'rule_field' => 'phone_numeric',
3830 $contact1 = $this->individualCreate(['api.Phone.create' => ['phone' => 123]]);
3833 'contact_type' => 'Individual',
3835 $dupes = $this->callAPISuccess('Contact', 'duplicatecheck', [
3836 'dedupe_rule_id' => $ruleGroup['id'],
3837 'match' => $dedupeParams,
3839 $this->assertEquals([$contact1 => ['id' => $contact1]], $dupes);
3845 public function getPhoneStrings() {
3847 ['phone-Primary-1'],
3854 * Test the duplicate check function.
3856 public function testDuplicateCheckRuleNotReserved() {
3858 'first_name' => 'Harry',
3859 'last_name' => 'Potter',
3860 'email' => 'harry@hogwarts.edu',
3861 'contact_type' => 'Individual',
3863 $defaultRule = $this->callAPISuccess('RuleGroup', 'getsingle', ['used' => 'Unsupervised', 'is_reserved' => 1]);
3864 $this->callAPISuccess('RuleGroup', 'create', ['id' => $defaultRule['id'], 'is_reserved' => 0]);
3865 $this->callAPISuccess('Contact', 'create', $harry);
3866 $result = $this->callAPISuccess('Contact', 'duplicatecheck', [
3870 $this->assertEquals(1, $result['count']);
3871 $this->callAPISuccess('RuleGroup', 'create', ['id' => $defaultRule['id'], 'is_reserved' => 1]);
3875 * Test variants on retrieving contact by type.
3877 public function testGetByContactType() {
3878 $individual = $this->callAPISuccess('Contact', 'create', [
3879 'email' => 'individual@test.com',
3880 'contact_type' => 'Individual',
3882 $household = $this->callAPISuccess('Contact', 'create', [
3883 'household_name' => 'household@test.com',
3884 'contact_type' => 'Household',
3886 $organization = $this->callAPISuccess('Contact', 'create', [
3887 'organization_name' => 'organization@test.com',
3888 'contact_type' => 'Organization',
3890 // Test with id - getsingle will throw an exception if not found
3891 $this->callAPISuccess('Contact', 'getsingle', [
3892 'id' => $individual['id'],
3893 'contact_type' => 'Individual',
3895 $this->callAPISuccess('Contact', 'getsingle', [
3896 'id' => $individual['id'],
3897 'contact_type' => ['IN' => ['Individual']],
3900 $this->callAPISuccess('Contact', 'getsingle', [
3901 'id' => $organization['id'],
3902 'contact_type' => ['IN' => ['Individual', 'Organization']],
3905 $result = $this->callAPISuccess('Contact', 'get', [
3906 'contact_type' => ['IN' => ['Individual', 'Organization']],
3907 'options' => ['limit' => 0],
3910 $this->assertContains($organization['id'], array_keys($result['values']));
3911 $this->assertContains($individual['id'], array_keys($result['values']));
3912 $this->assertNotContains($household['id'], array_keys($result['values']));
3914 $result = $this->callAPISuccess('Contact', 'get', [
3915 'contact_type' => 'Household',
3916 'options' => ['limit' => 0],
3919 $this->assertNotContains($organization['id'], array_keys($result['values']));
3920 $this->assertNotContains($individual['id'], array_keys($result['values']));
3921 $this->assertContains($household['id'], array_keys($result['values']));
3925 * Test merging 2 contacts.
3927 * Someone kindly bequethed us the legacy of mixed up use of main_id & other_id
3928 * in the params for contact.merge api.
3930 * This test protects that legacy.
3932 public function testMergeBizzareOldParams() {
3933 $this->createLoggedInUser();
3934 $otherContact = $this->callAPISuccess('contact', 'create', $this->_params
);
3935 $mainContact = $this->callAPISuccess('contact', 'create', $this->_params
);
3936 $this->callAPISuccess('contact', 'merge', [
3937 'main_id' => $mainContact['id'],
3938 'other_id' => $otherContact['id'],
3940 $contacts = $this->callAPISuccess('contact', 'get', $this->_params
);
3941 $this->assertEquals($otherContact['id'], $contacts['id']);
3945 * Test merging 2 contacts.
3947 public function testMerge() {
3948 $this->createLoggedInUser();
3949 $otherContact = $this->callAPISuccess('contact', 'create', $this->_params
);
3950 $retainedContact = $this->callAPISuccess('contact', 'create', $this->_params
);
3951 $this->callAPISuccess('contact', 'merge', [
3952 'to_keep_id' => $retainedContact['id'],
3953 'to_remove_id' => $otherContact['id'],
3954 'auto_flip' => FALSE,
3957 $contacts = $this->callAPISuccess('contact', 'get', $this->_params
);
3958 $this->assertEquals($retainedContact['id'], $contacts['id']);
3959 $activity = $this->callAPISuccess('Activity', 'getsingle', [
3960 'target_contact_id' => $retainedContact['id'],
3961 'activity_type_id' => 'Contact Merged',
3963 $this->assertEquals(date('Y-m-d'), date('Y-m-d', strtotime($activity['activity_date_time'])));
3964 $activity2 = $this->callAPISuccess('Activity', 'getsingle', [
3965 'target_contact_id' => $otherContact['id'],
3966 'activity_type_id' => 'Contact Deleted by Merge',
3968 $this->assertEquals($activity['id'], $activity2['parent_id']);
3969 $this->assertEquals('Normal', civicrm_api3('option_value', 'getvalue', [
3970 'value' => $activity['priority_id'],
3971 'return' => 'label',
3972 'option_group_id' => 'priority',
3978 * Test merging 2 contacts with custom fields.
3980 * @throws \API_Exception
3981 * @throws \CRM_Core_Exception
3982 * @throws \Civi\API\Exception\UnauthorizedException
3984 public function testMergeCustomFields() {
3985 $contact1 = $this->individualCreate();
3986 // Not sure this is quite right but it does get it into the file table
3987 $file = $this->callAPISuccess('Attachment', 'create', [
3988 'name' => 'header.txt',
3989 'mime_type' => 'text/plain',
3990 'description' => 'My test description',
3991 'content' => 'My test content',
3992 'entity_table' => 'civicrm_contact',
3993 'entity_id' => $contact1,
3996 $this->createCustomGroupWithFieldsOfAllTypes();
3997 $fileField = $this->getCustomFieldName('file');
3998 $linkField = $this->getCustomFieldName('link');
3999 $dateField = $this->getCustomFieldName('select_date');
4000 $selectField = $this->getCustomFieldName('select_string');
4001 $countryField = $this->getCustomFieldName('country');
4002 $multiCountryField = $this->getCustomFieldName('multi_country');
4003 $referenceField = $this->getCustomFieldName('contact_reference');
4004 $stateField = $this->getCustomFieldName('state');
4005 $multiStateField = $this->getCustomFieldName('multi_state');
4006 $booleanStateField = $this->getCustomFieldName('boolean');
4008 $countriesByName = array_flip(CRM_Core_PseudoConstant
::country(FALSE, FALSE));
4009 $statesByName = array_flip(CRM_Core_PseudoConstant
::stateProvince(FALSE, FALSE));
4010 $customFieldValues = [
4011 $fileField => $file['id'],
4012 $linkField => 'http://example.org',
4013 $dateField => '2018-01-01 17:10:56',
4014 $selectField => 'G',
4015 $countryField => $countriesByName['New Zealand'],
4016 $multiCountryField => [$countriesByName['New Zealand'], $countriesByName['Australia']],
4017 $referenceField => $this->householdCreate(),
4018 $stateField => $statesByName['Victoria'],
4019 $multiStateField => [$statesByName['Victoria'], $statesByName['Tasmania']],
4020 $booleanStateField => 1,
4022 $this->callAPISuccess('Contact', 'create', array_merge([
4024 ], $customFieldValues));
4026 $contact2 = $this->individualCreate();
4027 $this->callAPISuccess('contact', 'merge', [
4028 'to_keep_id' => $contact2,
4029 'to_remove_id' => $contact1,
4030 'auto_flip' => FALSE,
4032 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact2, 'return' => array_keys($customFieldValues)]);
4033 $this->assertEquals($contact2, CRM_Core_DAO
::singleValueQuery('SELECT entity_id FROM civicrm_entity_file WHERE file_id = ' . $file['id']));
4034 foreach ($customFieldValues as $key => $value) {
4035 $this->assertEquals($value, $contact[$key]);
4040 * Test merging a contact that is the target of a contact reference field on another contact.
4042 * @throws \API_Exception
4043 * @throws \CRM_Core_Exception
4044 * @throws \Civi\API\Exception\UnauthorizedException
4046 public function testMergeContactReferenceCustomFieldTarget() {
4047 $this->createCustomGroupWithFieldOfType([], 'contact_reference');
4048 $contact1 = $this->individualCreate();
4049 $contact2 = $this->individualCreate();
4050 $contact3 = $this->individualCreate([$this->getCustomFieldName('contact_reference') => $contact2]);
4051 $this->callAPISuccess('contact', 'merge', [
4052 'to_keep_id' => $contact1,
4053 'to_remove_id' => $contact2,
4054 'auto_flip' => FALSE,
4056 $this->assertEquals($contact1, $this->callAPISuccessGetValue('Contact', ['id' => $contact3, 'return' => $this->getCustomFieldName('contact_reference')]));
4060 * Test merging when a multiple record set is in use.
4062 * @throws \API_Exception
4063 * @throws \CRM_Core_Exception
4064 * @throws \Civi\API\Exception\UnauthorizedException
4066 public function testMergeMultipleCustomValues() {
4067 $customGroupID = $this->createCustomGroup(['is_multiple' => TRUE]);
4068 $this->ids
['CustomField']['text'] = (int) $this->createTextCustomField(['custom_group_id' => $customGroupID])['id'];
4069 $contact1 = $this->individualCreate([$this->getCustomFieldName('text') => 'blah']);
4070 $contact2 = $this->individualCreate([$this->getCustomFieldName('text') => 'de blah']);
4071 $this->callAPISuccess('contact', 'merge', [
4072 'to_keep_id' => $contact1,
4073 'to_remove_id' => $contact2,
4074 'auto_flip' => FALSE,
4076 $column = $this->getCustomFieldColumnName('text');
4077 $table = $this->getCustomGroupTable();
4078 $this->assertEquals('blah,de blah', CRM_Core_DAO
::singleValueQuery(
4079 "SELECT GROUP_CONCAT({$column}) FROM $table WHERE entity_id = $contact1"
4084 * Test retrieving merged contacts.
4086 * The goal here is to start with a contact deleted by merged and find out the contact that is the current version of them.
4088 * @throws \CRM_Core_Exception
4090 public function testMergedGet() {
4091 $this->contactIDs
[] = $this->individualCreate();
4092 $this->contactIDs
[] = $this->individualCreate();
4093 $this->contactIDs
[] = $this->individualCreate();
4094 $this->contactIDs
[] = $this->individualCreate();
4096 // First do an 'unnatural merge' - they 'like to merge into the lowest but this will mean that contact 0 merged to contact [3].
4097 // When the batch merge runs.... the new lowest contact is contact[1]. All contacts will merge into that contact,
4098 // including contact[3], resulting in only 3 existing at the end. For each contact the correct answer to 'who did I eventually
4099 // wind up being should be [1]
4100 $this->callAPISuccess('Contact', 'merge', ['to_remove_id' => $this->contactIDs
[0], 'to_keep_id' => $this->contactIDs
[3]]);
4102 $this->callAPISuccess('Job', 'process_batch_merge', []);
4103 foreach ($this->contactIDs
as $contactID) {
4104 if ($contactID === $this->contactIDs
[1]) {
4107 $result = $this->callAPIAndDocument('Contact', 'getmergedto', ['sequential' => 1, 'contact_id' => $contactID], __FUNCTION__
, __FILE__
);
4108 $this->assertEquals(1, $result['count']);
4109 $this->assertEquals($this->contactIDs
[1], $result['values'][0]['id']);
4112 $result = $this->callAPIAndDocument('Contact', 'getmergedfrom', ['contact_id' => $this->contactIDs
[1]], __FUNCTION__
, __FILE__
)['values'];
4113 $mergedContactIds = array_merge(array_diff($this->contactIDs
, [$this->contactIDs
[1]]));
4114 $this->assertEquals($mergedContactIds, array_keys($result));
4118 * Test merging 2 contacts with delete to trash off.
4120 * We are checking that there is no error due to attempting to add an activity for the
4123 * @see https://issues.civicrm.org/jira/browse/CRM-18307
4125 public function testMergeNoTrash() {
4126 $this->createLoggedInUser();
4127 $this->callAPISuccess('Setting', 'create', ['contact_undelete' => FALSE]);
4128 $otherContact = $this->callAPISuccess('contact', 'create', $this->_params
);
4129 $retainedContact = $this->callAPISuccess('contact', 'create', $this->_params
);
4130 $this->callAPISuccess('contact', 'merge', [
4131 'to_keep_id' => $retainedContact['id'],
4132 'to_remove_id' => $otherContact['id'],
4133 'auto_flip' => FALSE,
4135 $this->callAPISuccess('Setting', 'create', ['contact_undelete' => TRUE]);
4139 * Ensure format with return=group shows comma-separated group IDs.
4141 * @see https://issues.civicrm.org/jira/browse/CRM-19426
4143 * @throws \CRM_Core_Exception
4145 public function testContactGetReturnGroup() {
4146 // Set up a contact, asser that they were created.
4148 'contact_type' => 'Individual',
4149 'first_name' => 'Test',
4150 'last_name' => 'Groupmember',
4151 'email' => 'test@example.org',
4153 $create_contact = $this->callApiSuccess('Contact', 'create', $contact_params);
4154 $this->assertEquals(0, $create_contact['is_error']);
4155 $this->assertInternalType('int', $create_contact['id']);
4157 $created_contact_id = $create_contact['id'];
4159 // Set up multiple groups, add the contact to the groups.
4160 $test_groups = ['Test group A', 'Test group B'];
4161 foreach ($test_groups as $title) {
4162 // Use this contact as group owner, since we know they exist.
4165 'created_id' => $created_contact_id,
4167 $create_group = $this->callApiSuccess('Group', 'create', $group_params);
4168 $this->assertEquals(0, $create_group['is_error']);
4169 $this->assertInternalType('int', $create_group['id']);
4171 $created_group_ids[] = $create_group['id'];
4173 // Add contact to the new group.
4174 $group_contact_params = [
4175 'contact_id' => $created_contact_id,
4176 'group_id' => $create_group['id'],
4178 $create_group_contact = $this->callApiSuccess('GroupContact', 'create', $group_contact_params);
4179 $this->assertEquals(0, $create_group_contact['is_error']);
4180 $this->assertInternalType('int', $create_group_contact['added']);
4183 // Use the Contact,get API to retrieve the contact
4184 $contact_get_params = [
4185 'id' => $created_contact_id,
4186 'return' => 'group',
4188 $contact_get = $this->callApiSuccess('Contact', 'get', $contact_get_params);
4189 $this->assertInternalType('array', $contact_get['values'][$created_contact_id]);
4190 $this->assertInternalType('string', $contact_get['values'][$created_contact_id]['groups']);
4192 // Ensure they are shown as being in each created group.
4193 $contact_group_ids = explode(',', $contact_get['values'][$created_contact_id]['groups']);
4194 foreach ($created_group_ids as $created_group_id) {
4195 $this->assertContains($created_group_id, $contact_group_ids);
4200 * CRM-20144 Verify that passing title of group works as well as id
4201 * Tests the following formats
4202 * contact.get group='title1'
4203 * contact.get group=id1
4205 public function testContactGetWithGroupTitle() {
4206 // Set up a contact, asser that they were created.
4208 'contact_type' => 'Individual',
4209 'first_name' => 'Test2',
4210 'last_name' => 'Groupmember',
4211 'email' => 'test@example.org',
4213 $create_contact = $this->callApiSuccess('Contact', 'create', $contact_params);
4214 $created_contact_id = $create_contact['id'];
4215 // Set up multiple groups, add the contact to the groups.
4216 $test_groups = ['Test group C', 'Test group D'];
4217 foreach ($test_groups as $title) {
4220 'created_id' => $created_contact_id,
4222 $create_group = $this->callApiSuccess('Group', 'create', $group_params);
4223 $created_group_id = $create_group['id'];
4225 // Add contact to the new group.
4226 $group_contact_params = [
4227 'contact_id' => $created_contact_id,
4228 'group_id' => $create_group['id'],
4230 $this->callApiSuccess('GroupContact', 'create', $group_contact_params);
4231 $contact_get = $this->callAPISuccess('contact', 'get', ['group' => $title, 'return' => 'group']);
4232 $this->assertEquals(1, $contact_get['count']);
4233 $this->assertEquals($created_contact_id, $contact_get['id']);
4234 $contact_groups = explode(',', $contact_get['values'][$created_contact_id]['groups']);
4235 $this->assertContains((string) $create_group['id'], $contact_groups);
4236 $contact_get2 = $this->callAPISuccess('contact', 'get', ['group' => $created_group_id, 'return' => 'group']);
4237 $this->assertEquals($created_contact_id, $contact_get2['id']);
4238 $contact_groups2 = explode(',', $contact_get2['values'][$created_contact_id]['groups']);
4239 $this->assertContains((string) $create_group['id'], $contact_groups2);
4240 $this->callAPISuccess('group', 'delete', ['id' => $created_group_id]);
4242 $this->callAPISuccess('contact', 'delete', ['id' => $created_contact_id, 'skip_undelete' => TRUE]);
4246 * CRM-20144 Verify that passing title of group works as well as id
4247 * Tests the following formats
4248 * contact.get group=array('title1', title1)
4249 * contact.get group=array('IN' => array('title1', 'title2)
4251 * @throws \CRM_Core_Exception
4253 public function testContactGetWithGroupTitleMultipleGroups() {
4254 $description = 'Get all from group and display contacts.';
4255 $subFile = 'GroupFilterUsingContactAPI';
4256 // Set up a contact, asser that they were created.
4258 'contact_type' => 'Individual',
4259 'first_name' => 'Test2',
4260 'last_name' => 'Groupmember',
4261 'email' => 'test@example.org',
4263 $create_contact = $this->callAPISuccess('Contact', 'create', $contact_params);
4264 $created_contact_id = $create_contact['id'];
4265 $createdGroupsIds = [];
4266 // Set up multiple groups, add the contact to the groups.
4267 $test_groups = ['Test group C', 'Test group D'];
4268 foreach ($test_groups as $title) {
4271 'created_id' => $created_contact_id,
4273 $create_group = $this->callAPISuccess('Group', 'create', $group_params);
4274 $createdGroupsIds[] = $create_group['id'];
4275 $createdGroupTitles[] = $title;
4276 // Add contact to the new group.
4277 $group_contact_params = [
4278 'contact_id' => $created_contact_id,
4279 'group_id' => $create_group['id'],
4281 $this->callAPISuccess('GroupContact', 'create', $group_contact_params);
4283 $contact_get = $this->callAPISuccess('contact', 'get', ['group' => $createdGroupTitles, 'return' => 'group']);
4284 $this->assertEquals(1, $contact_get['count']);
4285 $this->assertEquals($created_contact_id, $contact_get['id']);
4286 $contact_groups = explode(',', $contact_get['values'][$created_contact_id]['groups']);
4287 foreach ($createdGroupsIds as $id) {
4288 $this->assertContains((string) $id, $contact_groups);
4290 $this->callAPIAndDocument('contact', 'get', ['group' => ['IN' => $createdGroupTitles]], __FUNCTION__
, __FILE__
, $description, $subFile);
4291 $contact_get2 = $this->callAPISuccess('contact', 'get', ['group' => ['IN' => $createdGroupTitles], 'return' => 'group']);
4292 $this->assertEquals($created_contact_id, $contact_get2['id']);
4293 $contact_groups2 = explode(',', $contact_get2['values'][$created_contact_id]['groups']);
4294 foreach ($createdGroupsIds as $id) {
4295 $this->assertContains((string) $id, $contact_groups2);
4297 foreach ($createdGroupsIds as $id) {
4298 $this->callAPISuccess('group', 'delete', ['id' => $id]);
4300 $this->callAPISuccess('contact', 'delete', ['id' => $created_contact_id, 'skip_undelete' => TRUE]);
4304 * CRM-20144 Verify that passing title of group works as well as id
4305 * Tests the following formats
4306 * contact.get group=array('title1' => 1)
4307 * contact.get group=array('titke1' => 1, 'title2' => 1)
4308 * contact.get group=array('id1' => 1)
4309 * contact.get group=array('id1' => 1, id2 => 1)
4311 * @throws \CRM_Core_Exception
4313 public function testContactGetWithGroupTitleMultipleGroupsLegacyFormat() {
4314 // Set up a contact, asser that they were created.
4316 'contact_type' => 'Individual',
4317 'first_name' => 'Test2',
4318 'last_name' => 'Groupmember',
4319 'email' => 'test@example.org',
4321 $create_contact = $this->callApiSuccess('Contact', 'create', $contact_params);
4322 $created_contact_id = $create_contact['id'];
4323 $createdGroupsTitles = $createdGroupsIds = [];
4324 // Set up multiple groups, add the contact to the groups.
4325 $test_groups = ['Test group C', 'Test group D'];
4326 foreach ($test_groups as $title) {
4329 'created_id' => $created_contact_id,
4331 $create_group = $this->callApiSuccess('Group', 'create', $group_params);
4332 $createdGroupsIds[] = $create_group['id'];
4333 $createdGroupTitles[] = $title;
4334 // Add contact to the new group.
4335 $group_contact_params = [
4336 'contact_id' => $created_contact_id,
4337 'group_id' => $create_group['id'],
4339 $this->callApiSuccess('GroupContact', 'create', $group_contact_params);
4341 $contact_get = $this->callAPISuccess('contact', 'get', ['group' => [$createdGroupTitles[0] => 1], 'return' => 'group']);
4342 $this->assertEquals(1, $contact_get['count']);
4343 $this->assertEquals($created_contact_id, $contact_get['id']);
4344 $contact_groups = explode(',', $contact_get['values'][$created_contact_id]['groups']);
4345 foreach ($createdGroupsIds as $id) {
4346 $this->assertContains((string) $id, $contact_groups);
4348 $contact_get2 = $this->callAPISuccess('contact', 'get', ['group' => [$createdGroupTitles[0] => 1, $createdGroupTitles[1] => 1], 'return' => 'group']);
4349 $this->assertEquals(1, $contact_get2['count']);
4350 $this->assertEquals($created_contact_id, $contact_get2['id']);
4351 $contact_groups2 = explode(',', $contact_get2['values'][$created_contact_id]['groups']);
4352 foreach ($createdGroupsIds as $id) {
4353 $this->assertContains((string) $id, $contact_groups2);
4355 $contact_get3 = $this->callAPISuccess('contact', 'get', ['group' => [$createdGroupsIds[0] => 1], 'return' => 'group']);
4356 $this->assertEquals($created_contact_id, $contact_get3['id']);
4357 $contact_groups3 = explode(',', $contact_get3['values'][$created_contact_id]['groups']);
4358 foreach ($createdGroupsIds as $id) {
4359 $this->assertContains((string) $id, $contact_groups3);
4361 $contact_get4 = $this->callAPISuccess('contact', 'get', ['group' => [$createdGroupsIds[0] => 1, $createdGroupsIds[1] => 1], 'return' => 'group']);
4362 $this->assertEquals($created_contact_id, $contact_get4['id']);
4363 $contact_groups4 = explode(',', $contact_get4['values'][$created_contact_id]['groups']);
4364 foreach ($createdGroupsIds as $id) {
4365 $this->assertContains((string) $id, $contact_groups4);
4367 foreach ($createdGroupsIds as $id) {
4368 $this->callAPISuccess('group', 'delete', ['id' => $id]);
4370 $this->callAPISuccess('contact', 'delete', ['id' => $created_contact_id, 'skip_undelete' => TRUE]);
4374 * Test the prox_distance functionality works.
4376 * This is primarily testing functionality in the BAO_Query object that 'happens to be'
4377 * accessible via the api.
4379 * @throws \CRM_Core_Exception
4381 public function testContactGetProximity() {
4382 CRM_Core_Config
::singleton()->geocodeMethod
= 'CRM_Utils_MockGeocoder';
4383 $this->individualCreate();
4384 $contactID = $this->individualCreate();
4385 $this->callAPISuccess('Address', 'create', [
4386 'contact_id' => $contactID,
4388 'city' => 'Whangarei',
4389 'street_address' => 'Dent St',
4390 'geo_code_1' => '-35.8743325',
4391 'geo_code_2' => '174.4567136',
4392 'location_type_id' => 'Home',
4394 $contact = $this->callAPISuccess('Contact', 'get', [
4395 'prox_distance' => 100,
4396 'prox_geo_code_1' => '-35.72192',
4397 'prox_geo_code_2' => '174.32034',
4399 $this->assertEquals(1, $contact['count']);
4400 $this->assertEquals($contactID, $contact['id']);
4403 public function testLoggedInUserAPISupportToken() {
4404 $description = 'Get contact id of the current logged in user';
4405 $subFile = 'ContactIDOfLoggedInUserContactAPI';
4406 $cid = $this->createLoggedInUser();
4407 $contact = $this->callAPIAndDocument('contact', 'get', ['id' => 'user_contact_id'], __FUNCTION__
, __FILE__
, $description, $subFile);
4408 $this->assertEquals($cid, $contact['id']);
4415 protected function putGroupContactCacheInClearableState($groupID, $contact) {
4416 // We need to force the situation where there is invalid data in the cache and it
4417 // is due to be cleared.
4418 CRM_Core_DAO
::executeQuery("
4419 INSERT INTO civicrm_group_contact_cache (group_id, contact_id)
4420 VALUES ({$groupID}, {$contact['id']})
4422 CRM_Core_DAO
::executeQuery("UPDATE civicrm_group SET cache_date = '2017-01-01'");
4423 // Reset so it does not skip.
4424 Civi
::$statics['CRM_Contact_BAO_GroupContactCache']['is_refresh_init'] = FALSE;
4428 * CRM-21041 Test if 'communication style' is set to site default if not passed.
4430 * @param int $version
4432 * @dataProvider versionThreeAndFour
4433 * @throws \CRM_Core_Exception
4435 public function testCreateCommunicationStyleUnset($version) {
4436 $this->_apiversion
= $version;
4437 $this->callAPISuccess('Contact', 'create', [
4438 'first_name' => 'John',
4439 'last_name' => 'Doe',
4440 'contact_type' => 'Individual',
4442 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Doe']);
4443 $this->assertEquals(1, $result['communication_style_id']);
4447 * CRM-21041 Test if 'communication style' is set if value is passed.
4449 * @throws \CRM_Core_Exception
4450 * @throws \CiviCRM_API3_Exception
4452 public function testCreateCommunicationStylePassed() {
4453 $this->callAPISuccess('Contact', 'create', [
4454 'first_name' => 'John',
4455 'last_name' => 'Doe',
4456 'contact_type' => 'Individual',
4457 'communication_style_id' => 'Familiar',
4459 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Doe']);
4461 'option_group_id' => 'communication_style',
4462 'label' => 'Familiar',
4463 'return' => 'value',
4465 $optionResult = civicrm_api3('OptionValue', 'get', $params);
4466 $communicationStyle = reset($optionResult['values']);
4467 $this->assertEquals($communicationStyle['value'], $result['communication_style_id']);
4471 * Test that creating a contact with various contact greetings works.
4473 * @param int $version
4475 * @dataProvider versionThreeAndFour
4476 * @throws \CRM_Core_Exception
4478 public function testContactGreetingsCreate($version) {
4479 $this->_apiversion
= $version;
4480 // Api v4 takes a return parameter like postal_greeting_display which matches the field.
4481 // v3 has a customised parameter 'postal_greeting'. The v4 parameter is more correct so
4482 // we will not change it to match v3. The keyString value allows the test to support both.
4483 $keyString = $version === 4 ?
'_display' : '';
4485 $contact = $this->callAPISuccess('Contact', 'create', ['first_name' => 'Alan', 'last_name' => 'MouseMouse', 'contact_type' => 'Individual']);
4486 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => 'postal_greeting' . $keyString]);
4487 $this->assertEquals('Dear Alan', $contact['postal_greeting_display']);
4489 $contact = $this->callAPISuccess('Contact', 'create', ['id' => $contact['id'], 'postal_greeting_id' => 2]);
4490 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => 'postal_greeting' . $keyString]);
4491 $this->assertEquals('Dear Alan MouseMouse', $contact['postal_greeting_display']);
4493 $contact = $this->callAPISuccess('Contact', 'create', ['organization_name' => 'Alan\'s Show', 'contact_type' => 'Organization']);
4494 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => "postal_greeting{$keyString}, addressee{$keyString}, email_greeting{$keyString}"]);
4495 $this->assertEquals('', $contact['postal_greeting_display']);
4496 $this->assertEquals('', $contact['email_greeting_display']);
4497 $this->assertEquals('Alan\'s Show', $contact['addressee_display']);
4501 * Test that creating a contact with various contact greetings works.
4503 * @param int $version
4505 * @dataProvider versionThreeAndFour
4507 * @throws \CRM_Core_Exception
4509 public function testContactGreetingsCreateWithCustomField($version) {
4510 $this->_apiversion
= $version;
4511 // Api v4 takes a return parameter like postal_greeting_display which matches the field.
4512 // v3 has a customised parameter 'postal_greeting'. The v4 parameter is more correct so
4513 // we will not change it to match v3. The keyString value allows the test to support both.
4514 $keyString = $version === 4 ?
'_display' : '';
4516 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__
, __FILE__
);
4517 $contact = $this->callAPISuccess('Contact', 'create', ['first_name' => 'Alan', 'contact_type' => 'Individual', 'custom_' . $ids['custom_field_id'] => 'Mice']);
4519 // Change postal greeting to involve a custom field.
4520 $postalOption = $this->callAPISuccessGetSingle('OptionValue', ['option_group_id' => 'postal_greeting', 'filter' => 1, 'is_default' => 1]);
4521 $this->callAPISuccess('OptionValue', 'create', [
4522 'id' => $postalOption['id'],
4523 'name' => 'Dear {contact.first_name} {contact.custom_' . $ids['custom_field_id'] . '}',
4524 'label' => 'Dear {contact.first_name} {contact.custom_' . $ids['custom_field_id'] . '}',
4527 // Update contact & see if postal greeting now reflects the new string.
4528 $this->callAPISuccess('Contact', 'create', ['id' => $contact['id'], 'last_name' => 'MouseyMousey']);
4529 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => 'postal_greeting' . $keyString]);
4530 $this->assertEquals('Dear Alan Mice', $contact['postal_greeting_display']);
4532 // Set contact to have no postal greeting & check it is correct.
4533 $this->callAPISuccess('Contact', 'create', ['id' => $contact['id'], 'postal_greeting_id' => 'null']);
4534 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => 'postal_greeting' . $keyString]);
4535 $this->assertEquals('', $contact['postal_greeting_display']);
4538 $this->callAPISuccess('OptionValue', 'create', ['id' => $postalOption['id'], 'name' => 'Dear {contact.first_name}']);
4539 $this->customFieldDelete($ids['custom_field_id']);
4540 $this->customGroupDelete($ids['custom_group_id']);
4544 * Test that smarty variables are parsed if they exist in the greeting template.
4546 * In this test we have both a Civi token & a Smarty token and we check both are processed.
4548 * @param int $version
4550 * @dataProvider versionThreeAndFour
4551 * @throws \CRM_Core_Exception
4553 public function testGreetingParseSmarty($version) {
4554 $this->_apiversion
= $version;
4555 // Api v4 takes a return parameter like postal_greeting_display which matches the field.
4556 // v3 has a customised parameter 'postal_greeting'. The v4 parameter is more correct so
4557 // we will not change it to match v3. The keyString value allows the test to support both.
4558 $keyString = $version === 4 ?
'_display' : '';
4559 $postalOption = $this->callAPISuccessGetSingle('OptionValue', ['option_group_id' => 'postal_greeting', 'filter' => 1, 'is_default' => 1]);
4560 $this->callAPISuccess('OptionValue', 'create', [
4561 'id' => $postalOption['id'],
4562 'name' => "Dear {contact.first_name} {if \'{contact.first_name}\' === \'Tim\'}The Wise{/if}",
4563 'label' => "Dear {contact.first_name} {if '{contact.first_name}' === 'Tim'} The Wise{/if}",
4565 $contactID = $this->individualCreate();
4566 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contactID, 'return' => 'postal_greeting' . $keyString]);
4567 $this->assertEquals('Dear Anthony', $contact['postal_greeting_display']);
4569 $this->callAPISuccess('Contact', 'create', ['id' => $contactID, 'first_name' => 'Tim']);
4570 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contactID, 'return' => 'postal_greeting' . $keyString]);
4571 $this->assertEquals('Dear Tim The Wise', $contact['postal_greeting_display']);
4575 * Test getunique api call for Contact entity
4577 public function testContactGetUnique() {
4578 $result = $this->callAPIAndDocument($this->_entity
, 'getunique', [], __FUNCTION__
, __FILE__
);
4579 $this->assertEquals(1, $result['count']);
4580 $this->assertEquals(['external_identifier'], $result['values']['UI_external_identifier']);
4584 * API test to retrieve contact from group having different group title and name.
4586 * @throws \CRM_Core_Exception
4588 public function testContactGetFromGroup() {
4589 $groupId = $this->groupCreate([
4590 'name' => 'Test_Group',
4592 'title' => 'New Test Group Created',
4593 'description' => 'New Test Group Created',
4595 'visibility' => 'User and User Admin Only',
4597 $contact = $this->callAPISuccess('contact', 'create', $this->_params
);
4598 $groupContactCreateParams = [
4599 'contact_id' => $contact['id'],
4600 'group_id' => $groupId,
4601 'status' => 'Pending',
4603 $this->callAPISuccess('groupContact', 'create', $groupContactCreateParams);
4604 $this->callAPISuccess('groupContact', 'get', $groupContactCreateParams);
4605 $this->callAPISuccess('Contact', 'getcount', ['group' => 'Test_Group']);
4609 * Test the related contacts filter.
4611 * @throws \CRM_Core_Exception
4613 public function testSmartGroupsForRelatedContacts() {
4614 $rtype1 = $this->callAPISuccess('relationship_type', 'create', [
4615 "name_a_b" => uniqid() . " Child of",
4616 "name_b_a" => uniqid() . " Parent of",
4618 $rtype2 = $this->callAPISuccess('relationship_type', 'create', [
4619 "name_a_b" => uniqid() . " Household Member of",
4620 "name_b_a" => uniqid() . " Household Member is",
4622 $h1 = $this->householdCreate();
4623 $c1 = $this->individualCreate(['last_name' => 'Adams']);
4624 $c2 = $this->individualCreate(['last_name' => 'Adams']);
4625 $this->callAPISuccess('relationship', 'create', [
4626 'contact_id_a' => $c1,
4627 'contact_id_b' => $c2,
4630 'relationship_type_id' => $rtype1['id'],
4632 $this->callAPISuccess('relationship', 'create', [
4633 'contact_id_a' => $c1,
4634 'contact_id_b' => $h1,
4636 // Household Member of
4637 'relationship_type_id' => $rtype2['id'],
4639 $this->callAPISuccess('relationship', 'create', [
4640 'contact_id_a' => $c2,
4641 'contact_id_b' => $h1,
4643 // Household Member of
4644 'relationship_type_id' => $rtype2['id'],
4650 'display_relationship_type' => $rtype1['id'] . '_a_b',
4651 'sort_name' => 'Adams',
4654 $g1ID = $this->smartGroupCreate($ssParams, ['name' => uniqid(), 'title' => uniqid()]);
4657 // Household Member of
4658 'display_relationship_type' => $rtype2['id'] . '_a_b',
4661 $g2ID = $this->smartGroupCreate($ssParams, ['name' => uniqid(), 'title' => uniqid()]);
4664 // Household Member is
4665 'display_relationship_type' => $rtype2['id'] . '_b_a',
4668 // the reverse of g2 which adds another layer for overlap at related contact filter
4669 $g3ID = $this->smartGroupCreate($ssParams, ['name' => uniqid(), 'title' => uniqid()]);
4670 CRM_Contact_BAO_GroupContactCache
::loadAll();
4671 $this->callAPISuccessGetCount('contact', ['group' => $g1ID], 1);
4672 $this->callAPISuccessGetCount('contact', ['group' => $g2ID], 2);
4673 $this->callAPISuccessGetCount('contact', ['group' => $g3ID], 1);
4677 * Test creating a note from the contact.create API call when only passing the note as a string.
4679 * @throws \CRM_Core_Exception
4681 public function testCreateNoteInCreate() {
4682 $loggedInContactID = $this->createLoggedInUser();
4683 $this->_params
['note'] = 'Test note created by API Call as a String';
4684 $contact = $this->callAPISuccess('Contact', 'create', $this->_params
);
4685 $note = $this->callAPISuccess('Note', 'get', ['contact_id' => $loggedInContactID]);
4686 $this->assertEquals($note['values'][$note['id']]['note'], "Test note created by API Call as a String");
4687 $note = $this->callAPISuccess('Note', 'get', ['entity_id' => $contact['id']]);
4688 $this->assertEquals($note['values'][$note['id']]['note'], "Test note created by API Call as a String");
4689 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id'], 'skip_undelete' => TRUE]);
4693 * Test Creating a note from the contact.create api call when passing the note params as an array.
4695 * @throws \CRM_Core_Exception
4697 public function testCreateNoteinCreateArrayFormat() {
4698 $contact1 = $this->callAPISuccess('Contact', 'create', ['first_name' => 'Alan', 'last_name' => 'MouseMouse', 'contact_type' => 'Individual']);
4699 $this->_params
['note'] = [['note' => "Test note created by API Call as array", 'contact_id' => $contact1['id']]];
4700 $contact2 = $this->callAPISuccess('Contact', 'create', $this->_params
);
4701 $note = $this->callAPISuccess('Note', 'get', ['contact_id' => $contact1['id']]);
4702 $this->assertEquals($note['values'][$note['id']]['note'], "Test note created by API Call as array");
4703 $note = $this->callAPISuccess('Note', 'get', ['entity_id' => $contact2['id']]);
4704 $this->assertEquals($note['values'][$note['id']]['note'], "Test note created by API Call as array");
4708 * Verify that passing tag IDs to Contact.get works
4710 * Tests the following formats
4711 * - Contact.get tag='id1'
4712 * - Contact.get tag='id1,id2'
4713 * - Contact.get tag='id1, id2'
4715 * @throws \CRM_Core_Exception
4717 public function testContactGetWithTag() {
4718 $contact = $this->callApiSuccess('Contact', 'create', [
4719 'contact_type' => 'Individual',
4720 'first_name' => 'Test',
4721 'last_name' => 'Tagged',
4722 'email' => 'test@example.org',
4725 foreach (['Tag A', 'Tag B'] as $name) {
4726 $tags[] = $this->callApiSuccess('Tag', 'create', [
4731 // assign contact to "Tag B"
4732 $this->callApiSuccess('EntityTag', 'create', [
4733 'entity_table' => 'civicrm_contact',
4734 'entity_id' => $contact['id'],
4735 'tag_id' => $tags[1]['id'],
4738 // test format Contact.get tag='id1'
4739 $contact_get = $this->callAPISuccess('Contact', 'get', [
4740 'tag' => $tags[1]['id'],
4743 $this->assertEquals(1, $contact_get['count']);
4744 $this->assertEquals($contact['id'], $contact_get['id']);
4745 $this->assertEquals('Tag B', $contact_get['values'][$contact['id']]['tags']);
4747 // test format Contact.get tag='id1,id2'
4748 $contact_get = $this->callAPISuccess('Contact', 'get', [
4749 'tag' => $tags[0]['id'] . ',' . $tags[1]['id'],
4752 $this->assertEquals(1, $contact_get['count']);
4753 $this->assertEquals($contact['id'], $contact_get['id']);
4754 $this->assertEquals('Tag B', $contact_get['values'][$contact['id']]['tags']);
4756 // test format Contact.get tag='id1, id2'
4757 $contact_get = $this->callAPISuccess('Contact', 'get', [
4758 'tag' => $tags[0]['id'] . ', ' . $tags[1]['id'],
4761 $this->assertEquals(1, $contact_get['count']);
4762 $this->assertEquals($contact['id'], $contact_get['id']);
4763 $this->assertEquals('Tag B', $contact_get['values'][$contact['id']]['tags']);
4765 foreach ($tags as $tag) {
4766 $this->callAPISuccess('Tag', 'delete', ['id' => $tag['id']]);
4768 $this->callAPISuccess('Contact', 'delete', [
4769 'id' => $contact['id'],
4770 'skip_undelete' => TRUE,
4775 * Create pair of contacts with multiple conflicts.
4779 * @throws \API_Exception
4780 * @throws \CRM_Core_Exception
4781 * @throws \Civi\API\Exception\UnauthorizedException
4783 protected function createDeeplyConflictedContacts(): array {
4784 $this->createCustomGroupWithFieldOfType();
4785 $contact1 = $this->individualCreate([
4786 'email' => 'bob@example.com',
4787 'api.address.create' => ['location_type_id' => 'work', 'street_address' => 'big office', 'city' => 'small city'],
4788 'api.address.create.2' => ['location_type_id' => 'home', 'street_address' => 'big house', 'city' => 'small city'],
4789 'external_identifier' => 'unique and special',
4790 $this->getCustomFieldName('text') => 'mummy loves me',
4792 $contact2 = $this->individualCreate([
4793 'first_name' => 'different',
4794 'api.address.create.1' => ['location_type_id' => 'home', 'street_address' => 'medium house', 'city' => 'small city'],
4795 'api.address.create.2' => ['location_type_id' => 'work', 'street_address' => 'medium office', 'city' => 'small city'],
4796 'external_identifier' => 'uniquer and specialler',
4797 'api.email.create' => ['location_type_id' => 'Other', 'email' => 'bob@example.com'],
4798 $this->getCustomFieldName('text') => 'mummy loves me more',
4800 return [$contact1, $contact2];
4804 * Combinations of versions and privacy choices.
4808 public function versionAndPrivacyOption() {
4810 $fields = ['do_not_mail', 'do_not_email', 'do_not_sms', 'is_opt_out', 'do_not_trade'];
4812 foreach ($fields as $field) {
4813 foreach ($version as $v) {
4814 $tests[] = [$v, 1, $field, 1];
4815 $tests[] = [$v, 0, $field, 3];
4816 $tests[] = [$v, ['!=' => 1], $field, 3];
4817 $tests[] = [$v, ['!=' => 0], $field, 1];
4824 * CRM-14743 - test api respects search operators.
4826 * @param int $version
4832 * @throws \CRM_Core_Exception
4834 * @dataProvider versionAndPrivacyOption
4836 public function testGetContactsByPrivacyFlag($version, $query, $field, $expected) {
4837 $this->_apiversion
= $version;
4838 $contact1 = $this->individualCreate();
4839 $contact2 = $this->individualCreate([$field => 1]);
4840 $contact = $this->callAPISuccess('Contact', 'get', [$field => $query]);
4841 $this->assertEquals($expected, $contact['count']);
4842 $this->callAPISuccess('Contact', 'delete', ['id' => $contact1, 'skip_undelete' => 1]);
4843 $this->callAPISuccess('Contact', 'delete', ['id' => $contact2, 'skip_undelete' => 1]);