Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
7d61e75f | 4 | | Copyright CiviCRM LLC. All rights reserved. | |
6a488035 | 5 | | | |
7d61e75f TO |
6 | | This work is published under the GNU AGPLv3 license with some | |
7 | | permitted exceptions and without any warranty. For full license | | |
8 | | and copyright information, see https://civicrm.org/licensing | | |
6a488035 | 9 | +--------------------------------------------------------------------+ |
d25dd0ee | 10 | */ |
6a488035 | 11 | |
6a488035 TO |
12 | /** |
13 | * Test class for CRM_Contact_BAO_GroupContact BAO | |
14 | * | |
6c6e6187 | 15 | * @package CiviCRM |
acb109b7 | 16 | * @group headless |
6a488035 TO |
17 | */ |
18 | class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase { | |
19 | ||
20 | /** | |
eceb18cc | 21 | * Manually add and remove contacts from a smart group. |
6a488035 | 22 | */ |
00be9182 | 23 | public function testManualAddRemove() { |
0a0e1aab | 24 | list($group, $living, $deceased) = $this->setupSmartGroup(); |
6a488035 TO |
25 | |
26 | // Add $n1 to $g | |
9099cab3 | 27 | $this->callAPISuccess('group_contact', 'create', [ |
6a488035 TO |
28 | 'contact_id' => $living[0]->id, |
29 | 'group_id' => $group->id, | |
9099cab3 | 30 | ]); |
2828c36d | 31 | |
6a488035 TO |
32 | CRM_Contact_BAO_GroupContactCache::load($group, TRUE); |
33 | $this->assertCacheMatches( | |
9099cab3 | 34 | [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id, $living[0]->id], |
6a488035 TO |
35 | $group->id |
36 | ); | |
37 | ||
38 | // Remove $y1 from $g | |
9099cab3 | 39 | $this->callAPISuccess('group_contact', 'create', [ |
6a488035 TO |
40 | 'contact_id' => $deceased[0]->id, |
41 | 'group_id' => $group->id, | |
42 | 'status' => 'Removed', | |
9099cab3 | 43 | ]); |
2828c36d | 44 | |
6a488035 TO |
45 | CRM_Contact_BAO_GroupContactCache::load($group, TRUE); |
46 | $this->assertCacheMatches( | |
9099cab3 | 47 | [ |
92915c55 TO |
48 | $deceased[1]->id, |
49 | $deceased[2]->id, | |
28a04ea9 | 50 | $living[0]->id, |
9099cab3 | 51 | ], |
6a488035 TO |
52 | $group->id |
53 | ); | |
54 | } | |
b6708aeb | 55 | |
6a488035 | 56 | /** |
2828c36d | 57 | * Allow removing contact from a parent group even if contact is in a child group. (CRM-8858). |
6a488035 | 58 | */ |
00be9182 | 59 | public function testRemoveFromParentSmartGroup() { |
6a488035 | 60 | // Create smart group $parent |
9099cab3 | 61 | $params = [ |
6a488035 TO |
62 | 'name' => 'Deceased Contacts', |
63 | 'title' => 'Deceased Contacts', | |
64 | 'is_active' => 1, | |
9099cab3 CW |
65 | 'formValues' => ['is_deceased' => 1], |
66 | ]; | |
6a488035 | 67 | $parent = CRM_Contact_BAO_Group::createSmartGroup($params); |
9099cab3 | 68 | $this->registerTestObjects([$parent]); |
6a488035 TO |
69 | |
70 | // Create group $child in $parent | |
9099cab3 | 71 | $params = [ |
6a488035 TO |
72 | 'name' => 'Child Group', |
73 | 'title' => 'Child Group', | |
74 | 'is_active' => 1, | |
9099cab3 CW |
75 | 'parents' => [$parent->id => 1], |
76 | ]; | |
6a488035 | 77 | $child = CRM_Contact_BAO_Group::create($params); |
9099cab3 | 78 | $this->registerTestObjects([$child]); |
6a488035 TO |
79 | |
80 | // Create $c1, $c2, $c3 | |
9099cab3 | 81 | $deceased = $this->createTestObject('CRM_Contact_DAO_Contact', ['is_deceased' => 1], 3); |
6a488035 TO |
82 | |
83 | // Add $c1, $c2, $c3 to $child | |
84 | foreach ($deceased as $contact) { | |
9099cab3 | 85 | $this->callAPISuccess('group_contact', 'create', [ |
6a488035 TO |
86 | 'contact_id' => $contact->id, |
87 | 'group_id' => $child->id, | |
9099cab3 | 88 | ]); |
6a488035 TO |
89 | } |
90 | ||
6a488035 TO |
91 | CRM_Contact_BAO_GroupContactCache::load($parent, TRUE); |
92 | $this->assertCacheMatches( | |
9099cab3 | 93 | [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id], |
6a488035 TO |
94 | $parent->id |
95 | ); | |
b6708aeb | 96 | |
6a488035 | 97 | // Remove $c1 from $parent |
9099cab3 | 98 | $this->callAPISuccess('group_contact', 'create', [ |
6a488035 TO |
99 | 'contact_id' => $deceased[0]->id, |
100 | 'group_id' => $parent->id, | |
101 | 'status' => 'Removed', | |
9099cab3 | 102 | ]); |
b6708aeb | 103 | |
6a488035 TO |
104 | // Assert $c1 not in $parent |
105 | CRM_Contact_BAO_GroupContactCache::load($parent, TRUE); | |
106 | $this->assertCacheMatches( | |
9099cab3 | 107 | [ |
92915c55 | 108 | $deceased[1]->id, |
28a04ea9 | 109 | $deceased[2]->id, |
9099cab3 | 110 | ], |
6a488035 TO |
111 | $parent->id |
112 | ); | |
b6708aeb | 113 | |
6a488035 | 114 | // Assert $c1 still in $child |
b6708aeb | 115 | $this->assertDBQuery(1, |
6a488035 | 116 | 'select count(*) from civicrm_group_contact where group_id=%1 and contact_id=%2 and status=%3', |
9099cab3 CW |
117 | [ |
118 | 1 => [$child->id, 'Integer'], | |
119 | 2 => [$deceased[0]->id, 'Integer'], | |
120 | 3 => ['Added', 'String'], | |
121 | ] | |
6a488035 TO |
122 | ); |
123 | } | |
124 | ||
125 | /** | |
eceb18cc | 126 | * Assert that the cache for a group contains exactly the listed contacts. |
6a488035 | 127 | * |
5a4f6742 | 128 | * @param array $expectedContactIds |
e16033b4 | 129 | * Array(int). |
5a4f6742 | 130 | * @param int $groupId |
6a488035 | 131 | */ |
00be9182 | 132 | public function assertCacheMatches($expectedContactIds, $groupId) { |
6a488035 | 133 | $sql = 'SELECT contact_id FROM civicrm_group_contact_cache WHERE group_id = %1'; |
9099cab3 | 134 | $params = [1 => [$groupId, 'Integer']]; |
6a488035 | 135 | $dao = CRM_Core_DAO::executeQuery($sql, $params); |
9099cab3 | 136 | $actualContactIds = []; |
6a488035 TO |
137 | while ($dao->fetch()) { |
138 | $actualContactIds[] = $dao->contact_id; | |
139 | } | |
140 | ||
141 | sort($expectedContactIds); | |
142 | sort($actualContactIds); | |
143 | $this->assertEquals($expectedContactIds, $actualContactIds); | |
144 | } | |
145 | ||
0a0e1aab | 146 | /** |
147 | * Test the opportunistic refresh cache function does not touch non-expired entries. | |
148 | */ | |
149 | public function testOpportunisticRefreshCacheNoChangeIfNotExpired() { | |
150 | list($group, $living, $deceased) = $this->setupSmartGroup(); | |
9099cab3 | 151 | $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]); |
0a0e1aab | 152 | $this->assertCacheMatches( |
9099cab3 | 153 | [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id], |
0a0e1aab | 154 | $group->id |
155 | ); | |
2b68a50c | 156 | CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush(); |
0a0e1aab | 157 | |
2a4fb809 | 158 | $this->assertCacheNotRefreshed($deceased, $group); |
0a0e1aab | 159 | } |
160 | ||
161 | /** | |
2a4fb809 | 162 | * Test the opportunistic refresh cache function does refresh stale entries. |
0a0e1aab | 163 | */ |
164 | public function testOpportunisticRefreshChangeIfCacheDateFieldStale() { | |
165 | list($group, $living, $deceased) = $this->setupSmartGroup(); | |
9099cab3 | 166 | $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]); |
0a0e1aab | 167 | CRM_Core_DAO::executeQuery('UPDATE civicrm_group SET cache_date = DATE_SUB(NOW(), INTERVAL 7 MINUTE) WHERE id = ' . $group->id); |
2a4fb809 | 168 | $group->find(TRUE); |
0a0e1aab | 169 | Civi::$statics['CRM_Contact_BAO_GroupContactCache']['is_refresh_init'] = FALSE; |
170 | sleep(1); | |
2b68a50c | 171 | CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush(); |
0a0e1aab | 172 | |
2a4fb809 | 173 | $this->assertCacheRefreshed($group); |
174 | } | |
0a0e1aab | 175 | |
2a4fb809 | 176 | /** |
177 | * Test the opportunistic refresh cache function does refresh expired entries if mode is deterministic. | |
178 | */ | |
179 | public function testOpportunisticRefreshNoChangeWithDeterministicSetting() { | |
180 | list($group, $living, $deceased) = $this->setupSmartGroup(); | |
9099cab3 CW |
181 | $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'deterministic']); |
182 | $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]); | |
2a4fb809 | 183 | $this->makeCacheStale($group); |
2b68a50c | 184 | CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush(); |
2a4fb809 | 185 | $this->assertCacheNotRefreshed($deceased, $group); |
9099cab3 | 186 | $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']); |
2a4fb809 | 187 | } |
188 | ||
189 | /** | |
190 | * Test the deterministic cache function refreshes with the deterministic setting. | |
191 | */ | |
192 | public function testDeterministicRefreshChangeWithDeterministicSetting() { | |
193 | list($group, $living, $deceased) = $this->setupSmartGroup(); | |
9099cab3 CW |
194 | $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'deterministic']); |
195 | $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]); | |
2a4fb809 | 196 | $this->makeCacheStale($group); |
2b68a50c | 197 | CRM_Contact_BAO_GroupContactCache::deterministicCacheFlush(); |
2a4fb809 | 198 | $this->assertCacheRefreshed($group); |
9099cab3 | 199 | $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']); |
2a4fb809 | 200 | } |
201 | ||
202 | /** | |
203 | * Test the deterministic cache function refresh doesn't mess up non-expired. | |
204 | */ | |
205 | public function testDeterministicRefreshChangeDoesNotTouchNonExpired() { | |
206 | list($group, $living, $deceased) = $this->setupSmartGroup(); | |
9099cab3 CW |
207 | $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'deterministic']); |
208 | $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]); | |
2b68a50c | 209 | CRM_Contact_BAO_GroupContactCache::deterministicCacheFlush(); |
2a4fb809 | 210 | $this->assertCacheNotRefreshed($deceased, $group); |
9099cab3 | 211 | $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']); |
2a4fb809 | 212 | } |
213 | ||
214 | /** | |
215 | * Test the deterministic cache function refreshes with the opportunistic setting. | |
216 | * | |
217 | * (hey it's an opportunity!). | |
218 | */ | |
219 | public function testDeterministicRefreshChangeWithOpportunisticSetting() { | |
220 | list($group, $living, $deceased) = $this->setupSmartGroup(); | |
9099cab3 CW |
221 | $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']); |
222 | $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]); | |
2a4fb809 | 223 | $this->makeCacheStale($group); |
2b68a50c | 224 | CRM_Contact_BAO_GroupContactCache::deterministicCacheFlush(); |
2a4fb809 | 225 | $this->assertCacheRefreshed($group); |
226 | } | |
227 | ||
228 | /** | |
229 | * Test the api job wrapper around the deterministic refresh works. | |
230 | */ | |
231 | public function testJobWrapper() { | |
232 | list($group, $living, $deceased) = $this->setupSmartGroup(); | |
9099cab3 CW |
233 | $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']); |
234 | $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]); | |
2a4fb809 | 235 | $this->makeCacheStale($group); |
9099cab3 | 236 | $this->callAPISuccess('Job', 'group_cache_flush', []); |
2a4fb809 | 237 | $this->assertCacheRefreshed($group); |
0a0e1aab | 238 | } |
239 | ||
6a488035 TO |
240 | // *** Everything below this should be moved to parent class **** |
241 | ||
242 | /** | |
1d3260ea SL |
243 | * @var array |
244 | * (DAO_Name => array(int)) List of items to garbage-collect during tearDown | |
6a488035 TO |
245 | */ |
246 | private $_testObjects; | |
247 | ||
6a488035 TO |
248 | /** |
249 | * Tears down the fixture, for example, closes a network connection. | |
2828c36d | 250 | * |
6a488035 | 251 | * This method is called after a test is executed. |
6a488035 | 252 | */ |
594a9328 | 253 | protected function tearDown(): void { |
6a488035 | 254 | parent::tearDown(); |
eb68c129 | 255 | $this->deleteTestObjects(); |
6a488035 TO |
256 | } |
257 | ||
258 | /** | |
2828c36d | 259 | * This is a wrapper for CRM_Core_DAO::createTestObject which tracks created entities. |
6a488035 TO |
260 | * |
261 | * @see CRM_Core_DAO::createTestObject | |
2828c36d | 262 | * |
263 | * @param string $daoName | |
1e1fdcf6 EM |
264 | * @param array $params |
265 | * @param int $numObjects | |
266 | * @param bool $createOnly | |
2828c36d | 267 | * |
268 | * @return array|NULL|object | |
6a488035 | 269 | */ |
9099cab3 | 270 | public function createTestObject($daoName, $params = [], $numObjects = 1, $createOnly = FALSE) { |
6a488035 TO |
271 | $objects = CRM_Core_DAO::createTestObject($daoName, $params, $numObjects, $createOnly); |
272 | if (is_array($objects)) { | |
273 | $this->registerTestObjects($objects); | |
0db6c3e1 TO |
274 | } |
275 | else { | |
9099cab3 | 276 | $this->registerTestObjects([$objects]); |
6a488035 TO |
277 | } |
278 | return $objects; | |
279 | } | |
280 | ||
281 | /** | |
2828c36d | 282 | * Register test objects. |
283 | * | |
5a4f6742 CW |
284 | * @param array $objects |
285 | * DAO or BAO objects. | |
6a488035 | 286 | */ |
00be9182 | 287 | public function registerTestObjects($objects) { |
6a488035 TO |
288 | foreach ($objects as $object) { |
289 | $daoName = preg_replace('/_BAO_/', '_DAO_', get_class($object)); | |
290 | $this->_testObjects[$daoName][] = $object->id; | |
291 | } | |
292 | } | |
293 | ||
2828c36d | 294 | /** |
295 | * Delete test objects. | |
296 | * | |
297 | * Note: You might argue that the FK relations between test | |
298 | * objects could make this problematic; however, it should | |
299 | * behave intuitively as long as we mentally split our | |
300 | * test-objects between the "manual/primary records" | |
301 | * and the "automatic/secondary records" | |
302 | */ | |
00be9182 | 303 | public function deleteTestObjects() { |
6a488035 TO |
304 | foreach ($this->_testObjects as $daoName => $daoIds) { |
305 | foreach ($daoIds as $daoId) { | |
9099cab3 | 306 | CRM_Core_DAO::deleteTestObjects($daoName, ['id' => $daoId]); |
6a488035 TO |
307 | } |
308 | } | |
9099cab3 | 309 | $this->_testObjects = []; |
6a488035 TO |
310 | } |
311 | ||
0a0e1aab | 312 | /** |
313 | * Set up a smart group testing scenario. | |
314 | * | |
315 | * @return array | |
316 | */ | |
317 | protected function setupSmartGroup() { | |
9099cab3 | 318 | $params = [ |
0a0e1aab | 319 | 'name' => 'Deceased Contacts', |
320 | 'title' => 'Deceased Contacts', | |
321 | 'is_active' => 1, | |
9099cab3 CW |
322 | 'formValues' => ['is_deceased' => 1], |
323 | ]; | |
0a0e1aab | 324 | $group = CRM_Contact_BAO_Group::createSmartGroup($params); |
9099cab3 | 325 | $this->registerTestObjects([$group]); |
0a0e1aab | 326 | |
327 | // Create contacts $y1, $y2, $y3 which do match $g; create $n1, $n2, $n3 which do not match $g | |
9099cab3 CW |
328 | $living = $this->createTestObject('CRM_Contact_DAO_Contact', ['is_deceased' => 0], 3); |
329 | $deceased = $this->createTestObject('CRM_Contact_DAO_Contact', ['is_deceased' => 1], 3); | |
0a0e1aab | 330 | $this->assertEquals(3, count($deceased)); |
331 | $this->assertEquals(3, count($living)); | |
332 | ||
333 | // Assert: $g cache has exactly $y1, $y2, $y3 | |
334 | CRM_Contact_BAO_GroupContactCache::load($group, TRUE); | |
335 | $group->find(TRUE); | |
336 | $this->assertCacheMatches( | |
9099cab3 | 337 | [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id], |
0a0e1aab | 338 | $group->id |
339 | ); | |
340 | // Reload the group so we have the cache_date & refresh_date. | |
9099cab3 | 341 | return [$group, $living, $deceased]; |
0a0e1aab | 342 | } |
343 | ||
2a4fb809 | 344 | /** |
345 | * @param $deceased | |
346 | * @param $group | |
347 | * | |
348 | * @throws \Exception | |
349 | */ | |
350 | protected function assertCacheNotRefreshed($deceased, $group) { | |
351 | $this->assertCacheMatches( | |
9099cab3 | 352 | [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id], |
2a4fb809 | 353 | $group->id |
354 | ); | |
9099cab3 | 355 | $afterGroup = $this->callAPISuccessGetSingle('Group', ['id' => $group->id]); |
2a4fb809 | 356 | $this->assertEquals($group->cache_date, $afterGroup['cache_date']); |
357 | } | |
358 | ||
359 | /** | |
360 | * Make the cache for the group stale, resetting it to before the timeout period. | |
361 | * | |
362 | * @param CRM_Contact_BAO_Group $group | |
363 | */ | |
364 | protected function makeCacheStale(&$group) { | |
365 | CRM_Core_DAO::executeQuery('UPDATE civicrm_group SET cache_date = DATE_SUB(NOW(), INTERVAL 7 MINUTE) WHERE id = ' . $group->id); | |
366 | unset($group->cache_date); | |
367 | $group->find(TRUE); | |
368 | Civi::$statics['CRM_Contact_BAO_GroupContactCache']['is_refresh_init'] = FALSE; | |
369 | } | |
370 | ||
371 | /** | |
372 | * @param $group | |
373 | * | |
374 | * @throws \Exception | |
375 | */ | |
376 | protected function assertCacheRefreshed($group) { | |
377 | $this->assertCacheMatches( | |
9099cab3 | 378 | [], |
2a4fb809 | 379 | $group->id |
380 | ); | |
381 | ||
9099cab3 | 382 | $afterGroup = $this->callAPISuccessGetSingle('Group', ['id' => $group->id]); |
826096b0 | 383 | $this->assertTrue(empty($afterGroup['cache_date']), 'cache date should not be set as the cache is not built'); |
2a4fb809 | 384 | } |
385 | ||
6451118c JP |
386 | /** |
387 | * Test Smart group search | |
388 | */ | |
389 | public function testSmartGroupSearchBuilder() { | |
9099cab3 | 390 | $returnProperties = [ |
6451118c JP |
391 | 'contact_type' => 1, |
392 | 'contact_sub_type' => 1, | |
393 | 'sort_name' => 1, | |
394 | 'group' => 1, | |
9099cab3 | 395 | ]; |
6451118c JP |
396 | list($group, $living, $deceased) = $this->setupSmartGroup(); |
397 | ||
9099cab3 | 398 | $params = [ |
6451118c JP |
399 | 'name' => 'Living Contacts', |
400 | 'title' => 'Living Contacts', | |
401 | 'is_active' => 1, | |
9099cab3 CW |
402 | 'formValues' => ['is_deceased' => 0], |
403 | ]; | |
6451118c JP |
404 | $group2 = CRM_Contact_BAO_Group::createSmartGroup($params); |
405 | ||
406 | //Filter on smart group with =, !=, IN and NOT IN operator. | |
9099cab3 | 407 | $params = [['group', '=', $group2->id, 1, 0]]; |
6451118c JP |
408 | $query = new CRM_Contact_BAO_Query( |
409 | $params, $returnProperties, | |
410 | NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS, | |
411 | FALSE, | |
412 | FALSE, FALSE | |
413 | ); | |
9f6a1556 JP |
414 | $ids = $query->searchQuery(0, 0, NULL, |
415 | FALSE, FALSE, FALSE, | |
416 | TRUE, FALSE | |
417 | ); | |
e24f9388 | 418 | $key = $query->getGroupCacheTableKeys()[0]; |
ed17b5e2 | 419 | $expectedWhere = "civicrm_group_contact_cache_{$key}.group_id IN (\"{$group2->id}\")"; |
6451118c | 420 | $this->assertContains($expectedWhere, $query->_whereClause); |
9f6a1556 | 421 | $this->_assertContactIds($query, "group_id = {$group2->id}"); |
6451118c | 422 | |
9099cab3 | 423 | $params = [['group', '!=', $group->id, 1, 0]]; |
6451118c JP |
424 | $query = new CRM_Contact_BAO_Query( |
425 | $params, $returnProperties, | |
426 | NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS, | |
427 | FALSE, | |
428 | FALSE, FALSE | |
429 | ); | |
e24f9388 | 430 | $key = $query->getGroupCacheTableKeys()[0]; |
6451118c | 431 | //Assert if proper where clause is present. |
ed17b5e2 | 432 | $expectedWhere = "civicrm_group_contact_{$key}.group_id != {$group->id} AND civicrm_group_contact_cache_{$key}.group_id IS NULL OR ( civicrm_group_contact_cache_{$key}.contact_id NOT IN (SELECT contact_id FROM civicrm_group_contact_cache cgcc WHERE cgcc.group_id IN ( {$group->id} ) ) )"; |
6451118c | 433 | $this->assertContains($expectedWhere, $query->_whereClause); |
9f6a1556 | 434 | $this->_assertContactIds($query, "group_id != {$group->id}"); |
6451118c | 435 | |
9099cab3 | 436 | $params = [['group', 'IN', [$group->id, $group2->id], 1, 0]]; |
6451118c JP |
437 | $query = new CRM_Contact_BAO_Query( |
438 | $params, $returnProperties, | |
439 | NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS, | |
440 | FALSE, | |
441 | FALSE, FALSE | |
442 | ); | |
e24f9388 | 443 | $key = $query->getGroupCacheTableKeys()[0]; |
ed17b5e2 | 444 | $expectedWhere = "civicrm_group_contact_cache_{$key}.group_id IN (\"{$group->id}\", \"{$group2->id}\")"; |
6451118c | 445 | $this->assertContains($expectedWhere, $query->_whereClause); |
9f6a1556 | 446 | $this->_assertContactIds($query, "group_id IN ({$group->id}, {$group2->id})"); |
6451118c | 447 | |
9099cab3 | 448 | $params = [['group', 'NOT IN', [$group->id], 1, 0]]; |
6451118c JP |
449 | $query = new CRM_Contact_BAO_Query( |
450 | $params, $returnProperties, | |
451 | NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS, | |
452 | FALSE, | |
453 | FALSE, FALSE | |
454 | ); | |
e24f9388 | 455 | $key = $query->getGroupCacheTableKeys()[0]; |
ed17b5e2 | 456 | $expectedWhere = "civicrm_group_contact_{$key}.group_id NOT IN ( {$group->id} ) AND civicrm_group_contact_cache_{$key}.group_id IS NULL OR ( civicrm_group_contact_cache_{$key}.contact_id NOT IN (SELECT contact_id FROM civicrm_group_contact_cache cgcc WHERE cgcc.group_id IN ( {$group->id} ) ) )"; |
6451118c | 457 | $this->assertContains($expectedWhere, $query->_whereClause); |
9f6a1556 | 458 | $this->_assertContactIds($query, "group_id NOT IN ({$group->id})"); |
e24f9388 SL |
459 | $this->callAPISuccess('group', 'delete', ['id' => $group->id]); |
460 | $this->callAPISuccess('group', 'delete', ['id' => $group2->id]); | |
461 | } | |
462 | ||
e24f9388 | 463 | public function testMultipleGroupWhereClause() { |
9099cab3 | 464 | $returnProperties = [ |
e24f9388 SL |
465 | 'contact_type' => 1, |
466 | 'contact_sub_type' => 1, | |
467 | 'sort_name' => 1, | |
468 | 'group' => 1, | |
9099cab3 | 469 | ]; |
e24f9388 SL |
470 | list($group, $living, $deceased) = $this->setupSmartGroup(); |
471 | ||
9099cab3 | 472 | $params = [ |
e24f9388 SL |
473 | 'name' => 'Living Contacts', |
474 | 'title' => 'Living Contacts', | |
475 | 'is_active' => 1, | |
9099cab3 CW |
476 | 'formValues' => ['is_deceased' => 0], |
477 | ]; | |
e24f9388 SL |
478 | $group2 = CRM_Contact_BAO_Group::createSmartGroup($params); |
479 | ||
480 | //Filter on smart group with =, !=, IN and NOT IN operator. | |
9099cab3 | 481 | $params = [['group', '=', $group2->id, 1, 0], ['group', '=', $group->id, 1, 0]]; |
e24f9388 SL |
482 | $query = new CRM_Contact_BAO_Query( |
483 | $params, $returnProperties, | |
484 | NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS, | |
485 | FALSE, | |
486 | FALSE, FALSE | |
487 | ); | |
488 | $ids = $query->searchQuery(0, 0, NULL, | |
489 | FALSE, FALSE, FALSE, | |
490 | TRUE, FALSE | |
491 | ); | |
492 | $key1 = $query->getGroupCacheTableKeys()[0]; | |
493 | $key2 = $query->getGroupCacheTableKeys()[1]; | |
494 | $expectedWhere = 'civicrm_group_contact_cache_' . $key1 . '.group_id IN ("' . $group2->id . '") ) ) AND ( ( civicrm_group_contact_cache_' . $key2 . '.group_id IN ("' . $group->id . '")'; | |
495 | $this->assertContains($expectedWhere, $query->_whereClause); | |
496 | // Check that we have 3 joins to the group contact cache 1 for each of the group where clauses and 1 for the fact we are returning groups in the select. | |
497 | $expectedFrom1 = 'LEFT JOIN civicrm_group_contact_cache civicrm_group_contact_cache_' . $key1 . ' ON contact_a.id = civicrm_group_contact_cache_' . $key1 . '.contact_id'; | |
498 | $this->assertContains($expectedFrom1, $query->_fromClause); | |
499 | $expectedFrom2 = 'LEFT JOIN civicrm_group_contact_cache civicrm_group_contact_cache_' . $key2 . ' ON contact_a.id = civicrm_group_contact_cache_' . $key2 . '.contact_id'; | |
500 | $this->assertContains($expectedFrom2, $query->_fromClause); | |
501 | $expectedFrom3 = 'LEFT JOIN civicrm_group_contact_cache ON contact_a.id = civicrm_group_contact_cache.contact_id'; | |
502 | $this->assertContains($expectedFrom3, $query->_fromClause); | |
9f6a1556 JP |
503 | } |
504 | ||
505 | /** | |
506 | * Check if contact ids are fetched correctly. | |
507 | * | |
508 | * @param object $query | |
509 | * @param string $groupWhereClause | |
510 | */ | |
511 | public function _assertContactIds($query, $groupWhereClause) { | |
512 | $contactIds = explode(',', $query->searchQuery(0, 0, NULL, | |
513 | FALSE, FALSE, FALSE, | |
514 | TRUE, FALSE | |
515 | )); | |
9099cab3 | 516 | $expectedContactIds = []; |
9f6a1556 JP |
517 | $groupDAO = CRM_Core_DAO::executeQuery("SELECT contact_id FROM civicrm_group_contact_cache WHERE {$groupWhereClause}"); |
518 | while ($groupDAO->fetch()) { | |
519 | $expectedContactIds[] = $groupDAO->contact_id; | |
520 | } | |
521 | $this->assertEquals(sort($expectedContactIds), sort($contactIds)); | |
6451118c JP |
522 | } |
523 | ||
6a488035 | 524 | } |