Merge pull request #21460 from civicrm/5.42
[civicrm-core.git] / tests / phpunit / api / v4 / Action / BasicCustomFieldTest.php
CommitLineData
19b53e5b
C
1<?php
2
380f3545
TO
3/*
4 +--------------------------------------------------------------------+
7d61e75f 5 | Copyright CiviCRM LLC. All rights reserved. |
380f3545 6 | |
7d61e75f
TO
7 | This work is published under the GNU AGPLv3 license with some |
8 | permitted exceptions and without any warranty. For full license |
9 | and copyright information, see https://civicrm.org/licensing |
380f3545
TO
10 +--------------------------------------------------------------------+
11 */
12
13/**
14 *
15 * @package CRM
ca5cec67 16 * @copyright CiviCRM LLC https://civicrm.org/licensing
380f3545
TO
17 */
18
19
19b53e5b
C
20namespace api\v4\Action;
21
22use Civi\Api4\Contact;
23use Civi\Api4\CustomField;
24use Civi\Api4\CustomGroup;
ab8c864e 25use Civi\Api4\OptionGroup;
cbda9790
CW
26use Civi\Api4\Relationship;
27use Civi\Api4\RelationshipCache;
19b53e5b
C
28
29/**
30 * @group headless
31 */
32class BasicCustomFieldTest extends BaseCustomValueTest {
33
06b84fc8
EM
34 /**
35 * @throws \API_Exception
36 */
37 public function testWithSingleField(): void {
fe806431 38 $customGroup = CustomGroup::create(FALSE)
c752d94b
CW
39 ->addValue('name', 'MyIndividualFields')
40 ->addValue('extends', 'Individual')
19b53e5b
C
41 ->execute()
42 ->first();
43
fe806431 44 CustomField::create(FALSE)
19b53e5b
C
45 ->addValue('label', 'FavColor')
46 ->addValue('custom_group_id', $customGroup['id'])
47 ->addValue('html_type', 'Text')
48 ->addValue('data_type', 'String')
49 ->execute();
50
c752d94b 51 // Individual fields should show up when contact_type = null|Individual but not other contact types
fe806431 52 $getFields = Contact::getFields(FALSE);
a1415a02 53 $this->assertEquals('Custom', $getFields->execute()->indexBy('name')['MyIndividualFields.FavColor']['type']);
c752d94b
CW
54 $this->assertContains('MyIndividualFields.FavColor', $getFields->setValues(['contact_type' => 'Individual'])->execute()->column('name'));
55 $this->assertNotContains('MyIndividualFields.FavColor', $getFields->setValues(['contact_type' => 'Household'])->execute()->column('name'));
56
fe806431 57 $contactId = Contact::create(FALSE)
19b53e5b
C
58 ->addValue('first_name', 'Johann')
59 ->addValue('last_name', 'Tester')
60 ->addValue('contact_type', 'Individual')
c752d94b 61 ->addValue('MyIndividualFields.FavColor', 'Red')
19b53e5b
C
62 ->execute()
63 ->first()['id'];
64
fe806431 65 $contact = Contact::get(FALSE)
19b53e5b 66 ->addSelect('first_name')
c752d94b 67 ->addSelect('MyIndividualFields.FavColor')
19b53e5b 68 ->addWhere('id', '=', $contactId)
c752d94b 69 ->addWhere('MyIndividualFields.FavColor', '=', 'Red')
19b53e5b
C
70 ->execute()
71 ->first();
72
c752d94b 73 $this->assertEquals('Red', $contact['MyIndividualFields.FavColor']);
19b53e5b
C
74
75 Contact::update()
76 ->addWhere('id', '=', $contactId)
c752d94b 77 ->addValue('MyIndividualFields.FavColor', 'Blue')
19b53e5b
C
78 ->execute();
79
fe806431 80 $contact = Contact::get(FALSE)
c752d94b 81 ->addSelect('MyIndividualFields.FavColor')
19b53e5b
C
82 ->addWhere('id', '=', $contactId)
83 ->execute()
84 ->first();
85
c752d94b 86 $this->assertEquals('Blue', $contact['MyIndividualFields.FavColor']);
19b53e5b
C
87 }
88
89 public function testWithTwoFields() {
ab8c864e 90 $optionGroupCount = OptionGroup::get(FALSE)->selectRowCount()->execute()->count();
19b53e5b 91
c9e3994d 92 // First custom set
fe806431 93 CustomGroup::create(FALSE)
19b53e5b
C
94 ->addValue('name', 'MyContactFields')
95 ->addValue('extends', 'Contact')
c9e3994d
CW
96 ->addChain('field1', CustomField::create()
97 ->addValue('label', 'FavColor')
98 ->addValue('custom_group_id', '$id')
99 ->addValue('html_type', 'Text')
100 ->addValue('data_type', 'String'))
101 ->addChain('field2', CustomField::create()
102 ->addValue('label', 'FavFood')
103 ->addValue('custom_group_id', '$id')
104 ->addValue('html_type', 'Text')
105 ->addValue('data_type', 'String'))
19b53e5b
C
106 ->execute();
107
c9e3994d 108 // Second custom set
fe806431 109 CustomGroup::create(FALSE)
c9e3994d
CW
110 ->addValue('name', 'MyContactFields2')
111 ->addValue('extends', 'Contact')
112 ->addChain('field1', CustomField::create()
113 ->addValue('label', 'FavColor')
114 ->addValue('custom_group_id', '$id')
115 ->addValue('html_type', 'Text')
116 ->addValue('data_type', 'String'))
117 ->addChain('field2', CustomField::create()
118 ->addValue('label', 'FavFood')
119 ->addValue('custom_group_id', '$id')
120 ->addValue('html_type', 'Text')
121 ->addValue('data_type', 'String'))
19b53e5b
C
122 ->execute();
123
ab8c864e
CW
124 // Test that no new option groups have been created (these are text fields with no options)
125 $this->assertEquals($optionGroupCount, OptionGroup::get(FALSE)->selectRowCount()->execute()->count());
126
fe806431 127 $contactId1 = Contact::create(FALSE)
19b53e5b
C
128 ->addValue('first_name', 'Johann')
129 ->addValue('last_name', 'Tester')
130 ->addValue('MyContactFields.FavColor', 'Red')
131 ->addValue('MyContactFields.FavFood', 'Cherry')
132 ->execute()
133 ->first()['id'];
134
fe806431 135 $contactId2 = Contact::create(FALSE)
19b53e5b
C
136 ->addValue('first_name', 'MaryLou')
137 ->addValue('last_name', 'Tester')
138 ->addValue('MyContactFields.FavColor', 'Purple')
139 ->addValue('MyContactFields.FavFood', 'Grapes')
140 ->execute()
141 ->first()['id'];
142
fe806431 143 $contact = Contact::get(FALSE)
19b53e5b
C
144 ->addSelect('first_name')
145 ->addSelect('MyContactFields.FavColor')
146 ->addSelect('MyContactFields.FavFood')
147 ->addWhere('id', '=', $contactId1)
148 ->addWhere('MyContactFields.FavColor', '=', 'Red')
149 ->addWhere('MyContactFields.FavFood', '=', 'Cherry')
150 ->execute()
151 ->first();
19b53e5b
C
152 $this->assertArrayHasKey('MyContactFields.FavColor', $contact);
153 $this->assertEquals('Red', $contact['MyContactFields.FavColor']);
154
2f69b203
CW
155 // By default custom fields are not returned
156 $contact = Contact::get(FALSE)
157 ->addWhere('id', '=', $contactId1)
158 ->addWhere('MyContactFields.FavColor', '=', 'Red')
159 ->addWhere('MyContactFields.FavFood', '=', 'Cherry')
160 ->execute()
161 ->first();
162 $this->assertArrayNotHasKey('MyContactFields.FavColor', $contact);
163
c9e3994d 164 // Update 2nd set and ensure 1st hasn't changed
19b53e5b
C
165 Contact::update()
166 ->addWhere('id', '=', $contactId1)
c9e3994d
CW
167 ->addValue('MyContactFields2.FavColor', 'Orange')
168 ->addValue('MyContactFields2.FavFood', 'Tangerine')
19b53e5b 169 ->execute();
fe806431 170 $contact = Contact::get(FALSE)
c9e3994d 171 ->addSelect('MyContactFields.FavColor', 'MyContactFields2.FavColor', 'MyContactFields.FavFood', 'MyContactFields2.FavFood')
19b53e5b
C
172 ->addWhere('id', '=', $contactId1)
173 ->execute()
174 ->first();
c9e3994d
CW
175 $this->assertEquals('Red', $contact['MyContactFields.FavColor']);
176 $this->assertEquals('Orange', $contact['MyContactFields2.FavColor']);
177 $this->assertEquals('Cherry', $contact['MyContactFields.FavFood']);
178 $this->assertEquals('Tangerine', $contact['MyContactFields2.FavFood']);
19b53e5b 179
c9e3994d
CW
180 // Update 1st set and ensure 2st hasn't changed
181 Contact::update()
182 ->addWhere('id', '=', $contactId1)
183 ->addValue('MyContactFields.FavColor', 'Blue')
184 ->execute();
fe806431 185 $contact = Contact::get(FALSE)
2f69b203 186 ->addSelect('custom.*')
c9e3994d
CW
187 ->addWhere('id', '=', $contactId1)
188 ->execute()
189 ->first();
19b53e5b 190 $this->assertEquals('Blue', $contact['MyContactFields.FavColor']);
c9e3994d
CW
191 $this->assertEquals('Orange', $contact['MyContactFields2.FavColor']);
192 $this->assertEquals('Cherry', $contact['MyContactFields.FavFood']);
193 $this->assertEquals('Tangerine', $contact['MyContactFields2.FavFood']);
19b53e5b 194
fe806431 195 $search = Contact::get(FALSE)
19b53e5b
C
196 ->addClause('OR', ['MyContactFields.FavColor', '=', 'Blue'], ['MyContactFields.FavFood', '=', 'Grapes'])
197 ->addSelect('id')
198 ->addOrderBy('id')
199 ->execute()
200 ->indexBy('id');
201
202 $this->assertEquals([$contactId1, $contactId2], array_keys((array) $search));
203
fe806431 204 $search = Contact::get(FALSE)
19b53e5b
C
205 ->addClause('NOT', ['MyContactFields.FavColor', '=', 'Purple'], ['MyContactFields.FavFood', '=', 'Grapes'])
206 ->addSelect('id')
207 ->addOrderBy('id')
208 ->execute()
209 ->indexBy('id');
210
211 $this->assertNotContains($contactId2, array_keys((array) $search));
212
fe806431 213 $search = Contact::get(FALSE)
19b53e5b
C
214 ->addClause('NOT', ['MyContactFields.FavColor', '=', 'Purple'], ['MyContactFields.FavFood', '=', 'Grapes'])
215 ->addSelect('id')
216 ->addOrderBy('id')
217 ->execute()
218 ->indexBy('id');
219
220 $this->assertContains($contactId1, array_keys((array) $search));
221 $this->assertNotContains($contactId2, array_keys((array) $search));
222
fe806431 223 $search = Contact::get(FALSE)
19b53e5b
C
224 ->setWhere([['NOT', ['OR', [['MyContactFields.FavColor', '=', 'Blue'], ['MyContactFields.FavFood', '=', 'Grapes']]]]])
225 ->addSelect('id')
226 ->addOrderBy('id')
227 ->execute()
228 ->indexBy('id');
229
230 $this->assertNotContains($contactId1, array_keys((array) $search));
231 $this->assertNotContains($contactId2, array_keys((array) $search));
232 }
233
cbda9790
CW
234 public function testRelationshipCacheCustomFields() {
235 $cgName = uniqid('RelFields');
236
237 $customGroup = CustomGroup::create(FALSE)
238 ->addValue('name', $cgName)
239 ->addValue('extends', 'Relationship')
240 ->execute()
241 ->first();
242
243 CustomField::create(FALSE)
244 ->addValue('label', 'PetName')
245 ->addValue('custom_group_id', $customGroup['id'])
246 ->addValue('html_type', 'Text')
247 ->addValue('data_type', 'String')
248 ->execute();
249
07e7a46b
CW
250 // Adding custom field to Relationship entity also adds it to RelationshipCache entity
251 $this->assertCount(1, RelationshipCache::getFields(FALSE)
252 ->addWhere('name', '=', "$cgName.PetName")
253 ->execute()
254 );
255
cbda9790
CW
256 $parent = Contact::create(FALSE)
257 ->addValue('first_name', 'Parent')
258 ->addValue('last_name', 'Tester')
259 ->addValue('contact_type', 'Individual')
260 ->execute()
261 ->first()['id'];
262
263 $child = Contact::create(FALSE)
264 ->addValue('first_name', 'Child')
265 ->addValue('last_name', 'Tester')
266 ->addValue('contact_type', 'Individual')
267 ->execute()
268 ->first()['id'];
269
07e7a46b 270 Relationship::create(FALSE)
cbda9790
CW
271 ->addValue('contact_id_a', $parent)
272 ->addValue('contact_id_b', $child)
273 ->addValue('relationship_type_id', 1)
274 ->addValue("$cgName.PetName", 'Buddy')
275 ->execute();
276
07e7a46b 277 // Test get directly from relationshipCache entity
cbda9790
CW
278 $results = RelationshipCache::get(FALSE)
279 ->addSelect("$cgName.PetName")
280 ->addWhere("$cgName.PetName", '=', 'Buddy')
281 ->execute();
282
283 $this->assertCount(2, $results);
284 $this->assertEquals('Buddy', $results[0]["$cgName.PetName"]);
07e7a46b
CW
285
286 // Test get via bridge INNER join
287 $result = Contact::get(FALSE)
288 ->addSelect('relative.display_name', "relative.$cgName.PetName")
289 ->addJoin('Contact AS relative', 'INNER', 'RelationshipCache')
290 ->addWhere('id', '=', $parent)
291 ->addWhere('relative.relationship_type_id', '=', 1)
292 ->execute()->single();
293 $this->assertEquals('Child Tester', $result['relative.display_name']);
294 $this->assertEquals('Buddy', $result["relative.$cgName.PetName"]);
295
296 // Test get via bridge LEFT join
297 $result = Contact::get(FALSE)
298 ->addSelect('relative.display_name', "relative.$cgName.PetName")
299 ->addJoin('Contact AS relative', 'LEFT', 'RelationshipCache')
300 ->addWhere('id', '=', $parent)
301 ->addWhere('relative.relationship_type_id', '=', 1)
302 ->execute()->single();
303 $this->assertEquals('Child Tester', $result['relative.display_name']);
304 $this->assertEquals('Buddy', $result["relative.$cgName.PetName"]);
cbda9790
CW
305 }
306
b60c8243
CW
307 public function testMultipleJoinsToCustomTable() {
308 $cgName = uniqid('My');
309
310 CustomGroup::create(FALSE)
311 ->addValue('name', $cgName)
312 ->addValue('extends', 'Contact')
313 ->addChain('field1', CustomField::create()
314 ->addValue('label', 'FavColor')
315 ->addValue('custom_group_id', '$id')
316 ->addValue('html_type', 'Text')
317 ->addValue('data_type', 'String'))
318 ->execute();
319
320 $parent = Contact::create(FALSE)
321 ->addValue('first_name', 'Parent')
322 ->addValue('last_name', 'Tester')
323 ->addValue("$cgName.FavColor", 'Purple')
324 ->execute()
325 ->first()['id'];
326
327 $child = Contact::create(FALSE)
328 ->addValue('first_name', 'Child')
329 ->addValue('last_name', 'Tester')
330 ->addValue("$cgName.FavColor", 'Cyan')
331 ->execute()
332 ->first()['id'];
333
334 Relationship::create(FALSE)
335 ->addValue('contact_id_a', $parent)
336 ->addValue('contact_id_b', $child)
337 ->addValue('relationship_type_id', 1)
338 ->execute();
339
340 $results = Contact::get(FALSE)
341 ->addSelect('first_name', 'child.first_name', "$cgName.FavColor", "child.$cgName.FavColor")
342 ->addWhere('id', '=', $parent)
343 ->addJoin('Contact AS child', 'INNER', 'RelationshipCache', ['id', '=', 'child.far_contact_id'])
344 ->execute();
345
346 $this->assertCount(1, $results);
347 $this->assertEquals('Parent', $results[0]['first_name']);
348 $this->assertEquals('Child', $results[0]['child.first_name']);
349 $this->assertEquals('Purple', $results[0]["$cgName.FavColor"]);
350 $this->assertEquals('Cyan', $results[0]["child.$cgName.FavColor"]);
351 }
352
2986a716 353 /**
354 * Some types are creating a dummy option group even if we don't have
355 * any option values.
356 * @throws \API_Exception
357 */
358 public function testUndesiredOptionGroupCreation(): void {
359 $optionGroupCount = OptionGroup::get(FALSE)->selectRowCount()->execute()->count();
360
361 $customGroup = CustomGroup::create(FALSE)
362 ->addValue('name', 'MyIndividualFields')
363 ->addValue('extends', 'Individual')
364 ->execute()
365 ->first();
366
367 // This one doesn't make sense to have an option group.
368 CustomField::create(FALSE)
369 ->addValue('label', 'FavColor')
370 ->addValue('custom_group_id', $customGroup['id'])
371 ->addValue('html_type', 'Number')
372 ->addValue('data_type', 'Money')
373 ->execute();
374
375 // This one might be ok if we planned to then use the autocreated option
376 // group, but if we go on to create our own after then we have an extra
377 // unused group.
378 CustomField::create(FALSE)
379 ->addValue('label', 'FavMovie')
380 ->addValue('custom_group_id', $customGroup['id'])
381 ->addValue('html_type', 'Select')
382 ->addValue('data_type', 'String')
383 ->execute();
384
385 $this->assertEquals($optionGroupCount, OptionGroup::get(FALSE)->selectRowCount()->execute()->count());
386 }
387
19b53e5b 388}