Merge branch '5.41' of github.com:civicrm/civicrm-core
[civicrm-core.git] / tests / phpunit / api / v4 / Action / BasicCustomFieldTest.php
1 <?php
2
3 /*
4 +--------------------------------------------------------------------+
5 | Copyright CiviCRM LLC. All rights reserved. |
6 | |
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 |
10 +--------------------------------------------------------------------+
11 */
12
13 /**
14 *
15 * @package CRM
16 * @copyright CiviCRM LLC https://civicrm.org/licensing
17 */
18
19
20 namespace api\v4\Action;
21
22 use Civi\Api4\Contact;
23 use Civi\Api4\CustomField;
24 use Civi\Api4\CustomGroup;
25 use Civi\Api4\OptionGroup;
26 use Civi\Api4\Relationship;
27 use Civi\Api4\RelationshipCache;
28
29 /**
30 * @group headless
31 */
32 class BasicCustomFieldTest extends BaseCustomValueTest {
33
34 /**
35 * @throws \API_Exception
36 */
37 public function testWithSingleField(): void {
38 $customGroup = CustomGroup::create(FALSE)
39 ->addValue('name', 'MyIndividualFields')
40 ->addValue('extends', 'Individual')
41 ->execute()
42 ->first();
43
44 CustomField::create(FALSE)
45 ->addValue('label', 'FavColor')
46 ->addValue('custom_group_id', $customGroup['id'])
47 ->addValue('html_type', 'Text')
48 ->addValue('data_type', 'String')
49 ->execute();
50
51 // Individual fields should show up when contact_type = null|Individual but not other contact types
52 $getFields = Contact::getFields(FALSE);
53 $this->assertEquals('Custom', $getFields->execute()->indexBy('name')['MyIndividualFields.FavColor']['type']);
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
57 $contactId = Contact::create(FALSE)
58 ->addValue('first_name', 'Johann')
59 ->addValue('last_name', 'Tester')
60 ->addValue('contact_type', 'Individual')
61 ->addValue('MyIndividualFields.FavColor', 'Red')
62 ->execute()
63 ->first()['id'];
64
65 $contact = Contact::get(FALSE)
66 ->addSelect('first_name')
67 ->addSelect('MyIndividualFields.FavColor')
68 ->addWhere('id', '=', $contactId)
69 ->addWhere('MyIndividualFields.FavColor', '=', 'Red')
70 ->execute()
71 ->first();
72
73 $this->assertEquals('Red', $contact['MyIndividualFields.FavColor']);
74
75 Contact::update()
76 ->addWhere('id', '=', $contactId)
77 ->addValue('MyIndividualFields.FavColor', 'Blue')
78 ->execute();
79
80 $contact = Contact::get(FALSE)
81 ->addSelect('MyIndividualFields.FavColor')
82 ->addWhere('id', '=', $contactId)
83 ->execute()
84 ->first();
85
86 $this->assertEquals('Blue', $contact['MyIndividualFields.FavColor']);
87 }
88
89 public function testWithTwoFields() {
90 $optionGroupCount = OptionGroup::get(FALSE)->selectRowCount()->execute()->count();
91
92 // First custom set
93 CustomGroup::create(FALSE)
94 ->addValue('name', 'MyContactFields')
95 ->addValue('extends', 'Contact')
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'))
106 ->execute();
107
108 // Second custom set
109 CustomGroup::create(FALSE)
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'))
122 ->execute();
123
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
127 $contactId1 = Contact::create(FALSE)
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
135 $contactId2 = Contact::create(FALSE)
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
143 $contact = Contact::get(FALSE)
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();
152 $this->assertArrayHasKey('MyContactFields.FavColor', $contact);
153 $this->assertEquals('Red', $contact['MyContactFields.FavColor']);
154
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
164 // Update 2nd set and ensure 1st hasn't changed
165 Contact::update()
166 ->addWhere('id', '=', $contactId1)
167 ->addValue('MyContactFields2.FavColor', 'Orange')
168 ->addValue('MyContactFields2.FavFood', 'Tangerine')
169 ->execute();
170 $contact = Contact::get(FALSE)
171 ->addSelect('MyContactFields.FavColor', 'MyContactFields2.FavColor', 'MyContactFields.FavFood', 'MyContactFields2.FavFood')
172 ->addWhere('id', '=', $contactId1)
173 ->execute()
174 ->first();
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']);
179
180 // Update 1st set and ensure 2st hasn't changed
181 Contact::update()
182 ->addWhere('id', '=', $contactId1)
183 ->addValue('MyContactFields.FavColor', 'Blue')
184 ->execute();
185 $contact = Contact::get(FALSE)
186 ->addSelect('custom.*')
187 ->addWhere('id', '=', $contactId1)
188 ->execute()
189 ->first();
190 $this->assertEquals('Blue', $contact['MyContactFields.FavColor']);
191 $this->assertEquals('Orange', $contact['MyContactFields2.FavColor']);
192 $this->assertEquals('Cherry', $contact['MyContactFields.FavFood']);
193 $this->assertEquals('Tangerine', $contact['MyContactFields2.FavFood']);
194
195 $search = Contact::get(FALSE)
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
204 $search = Contact::get(FALSE)
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
213 $search = Contact::get(FALSE)
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
223 $search = Contact::get(FALSE)
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
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
250 $parent = Contact::create(FALSE)
251 ->addValue('first_name', 'Parent')
252 ->addValue('last_name', 'Tester')
253 ->addValue('contact_type', 'Individual')
254 ->execute()
255 ->first()['id'];
256
257 $child = Contact::create(FALSE)
258 ->addValue('first_name', 'Child')
259 ->addValue('last_name', 'Tester')
260 ->addValue('contact_type', 'Individual')
261 ->execute()
262 ->first()['id'];
263
264 $relationship = Relationship::create(FALSE)
265 ->addValue('contact_id_a', $parent)
266 ->addValue('contact_id_b', $child)
267 ->addValue('relationship_type_id', 1)
268 ->addValue("$cgName.PetName", 'Buddy')
269 ->execute();
270
271 $results = RelationshipCache::get(FALSE)
272 ->addSelect("$cgName.PetName")
273 ->addWhere("$cgName.PetName", '=', 'Buddy')
274 ->execute();
275
276 $this->assertCount(2, $results);
277 $this->assertEquals('Buddy', $results[0]["$cgName.PetName"]);
278 }
279
280 public function testMultipleJoinsToCustomTable() {
281 $cgName = uniqid('My');
282
283 CustomGroup::create(FALSE)
284 ->addValue('name', $cgName)
285 ->addValue('extends', 'Contact')
286 ->addChain('field1', CustomField::create()
287 ->addValue('label', 'FavColor')
288 ->addValue('custom_group_id', '$id')
289 ->addValue('html_type', 'Text')
290 ->addValue('data_type', 'String'))
291 ->execute();
292
293 $parent = Contact::create(FALSE)
294 ->addValue('first_name', 'Parent')
295 ->addValue('last_name', 'Tester')
296 ->addValue("$cgName.FavColor", 'Purple')
297 ->execute()
298 ->first()['id'];
299
300 $child = Contact::create(FALSE)
301 ->addValue('first_name', 'Child')
302 ->addValue('last_name', 'Tester')
303 ->addValue("$cgName.FavColor", 'Cyan')
304 ->execute()
305 ->first()['id'];
306
307 Relationship::create(FALSE)
308 ->addValue('contact_id_a', $parent)
309 ->addValue('contact_id_b', $child)
310 ->addValue('relationship_type_id', 1)
311 ->execute();
312
313 $results = Contact::get(FALSE)
314 ->addSelect('first_name', 'child.first_name', "$cgName.FavColor", "child.$cgName.FavColor")
315 ->addWhere('id', '=', $parent)
316 ->addJoin('Contact AS child', 'INNER', 'RelationshipCache', ['id', '=', 'child.far_contact_id'])
317 ->execute();
318
319 $this->assertCount(1, $results);
320 $this->assertEquals('Parent', $results[0]['first_name']);
321 $this->assertEquals('Child', $results[0]['child.first_name']);
322 $this->assertEquals('Purple', $results[0]["$cgName.FavColor"]);
323 $this->assertEquals('Cyan', $results[0]["child.$cgName.FavColor"]);
324 }
325
326 /**
327 * Some types are creating a dummy option group even if we don't have
328 * any option values.
329 * @throws \API_Exception
330 */
331 public function testUndesiredOptionGroupCreation(): void {
332 $optionGroupCount = OptionGroup::get(FALSE)->selectRowCount()->execute()->count();
333
334 $customGroup = CustomGroup::create(FALSE)
335 ->addValue('name', 'MyIndividualFields')
336 ->addValue('extends', 'Individual')
337 ->execute()
338 ->first();
339
340 // This one doesn't make sense to have an option group.
341 CustomField::create(FALSE)
342 ->addValue('label', 'FavColor')
343 ->addValue('custom_group_id', $customGroup['id'])
344 ->addValue('html_type', 'Number')
345 ->addValue('data_type', 'Money')
346 ->execute();
347
348 // This one might be ok if we planned to then use the autocreated option
349 // group, but if we go on to create our own after then we have an extra
350 // unused group.
351 CustomField::create(FALSE)
352 ->addValue('label', 'FavMovie')
353 ->addValue('custom_group_id', $customGroup['id'])
354 ->addValue('html_type', 'Select')
355 ->addValue('data_type', 'String')
356 ->execute();
357
358 $this->assertEquals($optionGroupCount, OptionGroup::get(FALSE)->selectRowCount()->execute()->count());
359 }
360
361 }