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