3 namespace Drupal\KernelTests\Core\Entity;
5 use Drupal\Core\Config\Entity\Query\QueryFactory;
6 use Drupal\config_test\Entity\ConfigQueryTest;
7 use Drupal\KernelTests\KernelTestBase;
10 * Tests Config Entity Query functionality.
13 * @see \Drupal\Core\Config\Entity\Query
15 class ConfigEntityQueryTest extends KernelTestBase {
22 public static $modules = ['config_test'];
25 * Stores the search results for alter comparison.
29 protected $queryResults;
32 * The query factory used to construct all queries in the test.
34 * @var \Drupal\Core\Entity\Query\QueryFactory
39 * Stores all config entities created for the test.
45 protected function setUp() {
49 $this->factory = $this->container->get('entity.query');
51 // These two are here to make sure that matchArray needs to go over several
52 // non-matches on every levels.
53 $array['level1']['level2a'] = 9;
54 $array['level1a']['level2'] = 9;
55 // The tests match array.level1.level2.
56 $array['level1']['level2'] = 1;
57 $entity = ConfigQueryTest::create([
58 'label' => $this->randomMachineName(),
63 $this->entities[] = $entity;
64 $entity->enforceIsNew();
67 $array['level1']['level2'] = 2;
68 $entity = ConfigQueryTest::create([
69 'label' => $this->randomMachineName(),
74 $this->entities[] = $entity;
75 $entity->enforceIsNew();
78 $array['level1']['level2'] = 1;
79 $entity = ConfigQueryTest::create([
80 'label' => 'test_prefix_' . $this->randomMachineName(),
85 $this->entities[] = $entity;
86 $entity->enforceIsNew();
89 $array['level1']['level2'] = 2;
90 $entity = ConfigQueryTest::create([
91 'label' => $this->randomMachineName() . '_test_suffix',
96 $this->entities[] = $entity;
97 $entity->enforceIsNew();
100 $array['level1']['level2'] = 3;
101 $entity = ConfigQueryTest::create([
102 'label' => $this->randomMachineName() . '_TEST_contains_' . $this->randomMachineName(),
107 $this->entities[] = $entity;
108 $entity->enforceIsNew();
113 * Tests basic functionality.
115 public function testConfigEntityQuery() {
116 // Run a test without any condition.
117 $this->queryResults = $this->factory->get('config_query_test')
119 $this->assertResults(['1', '2', '3', '4', '5']);
120 // No conditions, OR.
121 $this->queryResults = $this->factory->get('config_query_test', 'OR')
123 $this->assertResults(['1', '2', '3', '4', '5']);
125 // Filter by ID with equality.
126 $this->queryResults = $this->factory->get('config_query_test')
127 ->condition('id', '3')
129 $this->assertResults(['3']);
131 // Filter by label with a known prefix.
132 $this->queryResults = $this->factory->get('config_query_test')
133 ->condition('label', 'test_prefix', 'STARTS_WITH')
135 $this->assertResults(['3']);
137 // Filter by label with a known suffix.
138 $this->queryResults = $this->factory->get('config_query_test')
139 ->condition('label', 'test_suffix', 'ENDS_WITH')
141 $this->assertResults(['4']);
143 // Filter by label with a known containing word.
144 $this->queryResults = $this->factory->get('config_query_test')
145 ->condition('label', 'test_contains', 'CONTAINS')
147 $this->assertResults(['5']);
149 // Filter by ID with the IN operator.
150 $this->queryResults = $this->factory->get('config_query_test')
151 ->condition('id', ['2', '3'], 'IN')
153 $this->assertResults(['2', '3']);
155 // Filter by ID with the implicit IN operator.
156 $this->queryResults = $this->factory->get('config_query_test')
157 ->condition('id', ['2', '3'])
159 $this->assertResults(['2', '3']);
161 // Filter by ID with the > operator.
162 $this->queryResults = $this->factory->get('config_query_test')
163 ->condition('id', '3', '>')
165 $this->assertResults(['4', '5']);
167 // Filter by ID with the >= operator.
168 $this->queryResults = $this->factory->get('config_query_test')
169 ->condition('id', '3', '>=')
171 $this->assertResults(['3', '4', '5']);
173 // Filter by ID with the <> operator.
174 $this->queryResults = $this->factory->get('config_query_test')
175 ->condition('id', '3', '<>')
177 $this->assertResults(['1', '2', '4', '5']);
179 // Filter by ID with the < operator.
180 $this->queryResults = $this->factory->get('config_query_test')
181 ->condition('id', '3', '<')
183 $this->assertResults(['1', '2']);
185 // Filter by ID with the <= operator.
186 $this->queryResults = $this->factory->get('config_query_test')
187 ->condition('id', '3', '<=')
189 $this->assertResults(['1', '2', '3']);
191 // Filter by two conditions on the same field.
192 $this->queryResults = $this->factory->get('config_query_test')
193 ->condition('label', 'test_pref', 'STARTS_WITH')
194 ->condition('label', 'test_prefix', 'STARTS_WITH')
196 $this->assertResults(['3']);
198 // Filter by two conditions on different fields. The first query matches for
199 // a different ID, so the result is empty.
200 $this->queryResults = $this->factory->get('config_query_test')
201 ->condition('label', 'test_prefix', 'STARTS_WITH')
202 ->condition('id', '5')
204 $this->assertResults([]);
206 // Filter by two different conditions on different fields. This time the
207 // first condition matches on one item, but the second one does as well.
208 $this->queryResults = $this->factory->get('config_query_test')
209 ->condition('label', 'test_prefix', 'STARTS_WITH')
210 ->condition('id', '3')
212 $this->assertResults(['3']);
214 // Filter by two different conditions, of which the first one matches for
215 // every entry, the second one as well, but just the third one filters so
216 // that just two are left.
217 $this->queryResults = $this->factory->get('config_query_test')
218 ->condition('id', '1', '>=')
219 ->condition('number', 10, '>=')
220 ->condition('number', 50, '>=')
222 $this->assertResults(['3', '5']);
224 // Filter with an OR condition group.
225 $this->queryResults = $this->factory->get('config_query_test', 'OR')
227 ->condition('id', '2')
229 $this->assertResults(['1', '2']);
231 // Simplify it with IN.
232 $this->queryResults = $this->factory->get('config_query_test')
233 ->condition('id', ['1', '2'])
235 $this->assertResults(['1', '2']);
237 $this->queryResults = $this->factory->get('config_query_test')
238 ->condition('id', ['1', '2'], 'IN')
240 $this->assertResults(['1', '2']);
242 $this->queryResults = $this->factory->get('config_query_test')
243 ->condition('id', ['1', '2'], 'NOT IN')
245 $this->assertResults(['3', '4', '5']);
247 // Filter with an OR condition group on different fields.
248 $this->queryResults = $this->factory->get('config_query_test', 'OR')
250 ->condition('number', 41)
252 $this->assertResults(['1', '2']);
254 // Filter with an OR condition group on different fields but matching on the
256 $this->queryResults = $this->factory->get('config_query_test', 'OR')
258 ->condition('number', 31)
260 $this->assertResults(['1']);
262 // NO simple conditions, YES complex conditions, 'AND'.
263 $query = $this->factory->get('config_query_test', 'AND');
264 $and_condition_1 = $query->orConditionGroup()
265 ->condition('id', '2')
266 ->condition('label', $this->entities[0]->label);
267 $and_condition_2 = $query->orConditionGroup()
269 ->condition('label', $this->entities[3]->label);
270 $this->queryResults = $query
271 ->condition($and_condition_1)
272 ->condition($and_condition_2)
274 $this->assertResults(['1']);
276 // NO simple conditions, YES complex conditions, 'OR'.
277 $query = $this->factory->get('config_query_test', 'OR');
278 $and_condition_1 = $query->andConditionGroup()
280 ->condition('label', $this->entities[0]->label);
281 $and_condition_2 = $query->andConditionGroup()
282 ->condition('id', '2')
283 ->condition('label', $this->entities[1]->label);
284 $this->queryResults = $query
285 ->condition($and_condition_1)
286 ->condition($and_condition_2)
288 $this->assertResults(['1', '2']);
290 // YES simple conditions, YES complex conditions, 'AND'.
291 $query = $this->factory->get('config_query_test', 'AND');
292 $and_condition_1 = $query->orConditionGroup()
293 ->condition('id', '2')
294 ->condition('label', $this->entities[0]->label);
295 $and_condition_2 = $query->orConditionGroup()
297 ->condition('label', $this->entities[3]->label);
298 $this->queryResults = $query
299 ->condition('number', 31)
300 ->condition($and_condition_1)
301 ->condition($and_condition_2)
303 $this->assertResults(['1']);
305 // YES simple conditions, YES complex conditions, 'OR'.
306 $query = $this->factory->get('config_query_test', 'OR');
307 $and_condition_1 = $query->orConditionGroup()
308 ->condition('id', '2')
309 ->condition('label', $this->entities[0]->label);
310 $and_condition_2 = $query->orConditionGroup()
312 ->condition('label', $this->entities[3]->label);
313 $this->queryResults = $query
314 ->condition('number', 53)
315 ->condition($and_condition_1)
316 ->condition($and_condition_2)
318 $this->assertResults(['1', '2', '4', '5']);
320 // Test the exists and notExists conditions.
321 $this->queryResults = $this->factory->get('config_query_test')
324 $this->assertResults(['1', '2', '3', '4', '5']);
326 $this->queryResults = $this->factory->get('config_query_test')
327 ->exists('non-existent')
329 $this->assertResults([]);
331 $this->queryResults = $this->factory->get('config_query_test')
334 $this->assertResults([]);
336 $this->queryResults = $this->factory->get('config_query_test')
337 ->notExists('non-existent')
339 $this->assertResults(['1', '2', '3', '4', '5']);
343 * Tests ID conditions.
345 public function testStringIdConditions() {
346 // We need an entity with a non-numeric ID.
347 $entity = ConfigQueryTest::create([
348 'label' => $this->randomMachineName(),
351 $this->entities[] = $entity;
352 $entity->enforceIsNew();
355 // Test 'STARTS_WITH' condition.
356 $this->queryResults = $this->factory->get('config_query_test')
357 ->condition('id', 'foo.bar', 'STARTS_WITH')
359 $this->assertResults(['foo.bar']);
360 $this->queryResults = $this->factory->get('config_query_test')
361 ->condition('id', 'f', 'STARTS_WITH')
363 $this->assertResults(['foo.bar']);
364 $this->queryResults = $this->factory->get('config_query_test')
365 ->condition('id', 'miss', 'STARTS_WITH')
367 $this->assertResults([]);
369 // Test 'CONTAINS' condition.
370 $this->queryResults = $this->factory->get('config_query_test')
371 ->condition('id', 'foo.bar', 'CONTAINS')
373 $this->assertResults(['foo.bar']);
374 $this->queryResults = $this->factory->get('config_query_test')
375 ->condition('id', 'oo.ba', 'CONTAINS')
377 $this->assertResults(['foo.bar']);
378 $this->queryResults = $this->factory->get('config_query_test')
379 ->condition('id', 'miss', 'CONTAINS')
381 $this->assertResults([]);
383 // Test 'ENDS_WITH' condition.
384 $this->queryResults = $this->factory->get('config_query_test')
385 ->condition('id', 'foo.bar', 'ENDS_WITH')
387 $this->assertResults(['foo.bar']);
388 $this->queryResults = $this->factory->get('config_query_test')
389 ->condition('id', 'r', 'ENDS_WITH')
391 $this->assertResults(['foo.bar']);
392 $this->queryResults = $this->factory->get('config_query_test')
393 ->condition('id', 'miss', 'ENDS_WITH')
395 $this->assertResults([]);
401 public function testCount() {
402 // Test count on no conditions.
403 $count = $this->factory->get('config_query_test')
406 $this->assertIdentical($count, count($this->entities));
408 // Test count on a complex query.
409 $query = $this->factory->get('config_query_test', 'OR');
410 $and_condition_1 = $query->andConditionGroup()
412 ->condition('label', $this->entities[0]->label);
413 $and_condition_2 = $query->andConditionGroup()
414 ->condition('id', '2')
415 ->condition('label', $this->entities[1]->label);
417 ->condition($and_condition_1)
418 ->condition($and_condition_2)
421 $this->assertIdentical($count, 2);
425 * Tests sorting and range on config entity queries.
427 public function testSortRange() {
428 // Sort by simple ascending/descending.
429 $this->queryResults = $this->factory->get('config_query_test')
430 ->sort('number', 'DESC')
432 $this->assertIdentical(array_values($this->queryResults), ['3', '5', '2', '1', '4']);
434 $this->queryResults = $this->factory->get('config_query_test')
435 ->sort('number', 'ASC')
437 $this->assertIdentical(array_values($this->queryResults), ['4', '1', '2', '5', '3']);
439 // Apply some filters and sort.
440 $this->queryResults = $this->factory->get('config_query_test')
441 ->condition('id', '3', '>')
442 ->sort('number', 'DESC')
444 $this->assertIdentical(array_values($this->queryResults), ['5', '4']);
446 $this->queryResults = $this->factory->get('config_query_test')
447 ->condition('id', '3', '>')
448 ->sort('number', 'ASC')
450 $this->assertIdentical(array_values($this->queryResults), ['4', '5']);
452 // Apply a pager and sort.
453 $this->queryResults = $this->factory->get('config_query_test')
454 ->sort('number', 'DESC')
457 $this->assertIdentical(array_values($this->queryResults), ['2', '1']);
459 $this->queryResults = $this->factory->get('config_query_test')
460 ->sort('number', 'ASC')
463 $this->assertIdentical(array_values($this->queryResults), ['2', '5']);
465 // Add a range to a query without a start parameter.
466 $this->queryResults = $this->factory->get('config_query_test')
470 $this->assertIdentical(array_values($this->queryResults), ['1', '2', '3']);
472 // Apply a pager with limit 4.
473 $this->queryResults = $this->factory->get('config_query_test')
477 $this->assertIdentical(array_values($this->queryResults), ['1', '2', '3', '4']);
481 * Tests sorting with tableSort on config entity queries.
483 public function testTableSort() {
485 ['data' => t('ID'), 'specifier' => 'id'],
486 ['data' => t('Number'), 'specifier' => 'number'],
490 // Sorting with 'DESC' upper case
491 $this->queryResults = $this->factory->get('config_query_test')
495 $this->assertIdentical(array_values($this->queryResults), ['5', '4', '3', '2', '1']);
497 // Sorting with 'ASC' upper case
498 $this->queryResults = $this->factory->get('config_query_test')
502 $this->assertIdentical(array_values($this->queryResults), ['1', '2', '3', '4', '5']);
504 // Sorting with 'desc' lower case
505 $this->queryResults = $this->factory->get('config_query_test')
509 $this->assertIdentical(array_values($this->queryResults), ['5', '4', '3', '2', '1']);
511 // Sorting with 'asc' lower case
512 $this->queryResults = $this->factory->get('config_query_test')
516 $this->assertIdentical(array_values($this->queryResults), ['1', '2', '3', '4', '5']);
519 // Sorting with 'DeSc' mixed upper and lower case
520 $this->queryResults = $this->factory->get('config_query_test')
522 ->sort('number', 'DeSc')
524 $this->assertIdentical(array_values($this->queryResults), ['3', '5', '2', '1', '4']);
526 // Sorting with 'AsC' mixed upper and lower case
527 $this->queryResults = $this->factory->get('config_query_test')
529 ->sort('number', 'AsC')
531 $this->assertIdentical(array_values($this->queryResults), ['4', '1', '2', '5', '3']);
533 // Sorting with 'dEsC' mixed upper and lower case
534 $this->queryResults = $this->factory->get('config_query_test')
536 ->sort('number', 'dEsC')
538 $this->assertIdentical(array_values($this->queryResults), ['3', '5', '2', '1', '4']);
540 // Sorting with 'aSc' mixed upper and lower case
541 $this->queryResults = $this->factory->get('config_query_test')
543 ->sort('number', 'aSc')
545 $this->assertIdentical(array_values($this->queryResults), ['4', '1', '2', '5', '3']);
549 * Tests dotted path matching.
551 public function testDotted() {
552 $this->queryResults = $this->factory->get('config_query_test')
553 ->condition('array.level1.*', 1)
555 $this->assertResults(['1', '3']);
556 $this->queryResults = $this->factory->get('config_query_test')
557 ->condition('*.level1.level2', 2)
559 $this->assertResults(['2', '4']);
560 $this->queryResults = $this->factory->get('config_query_test')
561 ->condition('array.level1.*', 3)
563 $this->assertResults(['5']);
564 $this->queryResults = $this->factory->get('config_query_test')
565 ->condition('array.level1.level2', 3)
567 $this->assertResults(['5']);
568 // Make sure that values on the wildcard level do not match if there are
569 // sub-keys defined. This must not find anything even if entity 2 has a
570 // top-level key number with value 41.
571 $this->queryResults = $this->factory->get('config_query_test')
572 ->condition('*.level1.level2', 41)
574 $this->assertResults([]);
575 // Make sure that "IS NULL" and "IS NOT NULL" work correctly with
576 // array-valued fields/keys.
577 $all = ['1', '2', '3', '4', '5'];
578 $this->queryResults = $this->factory->get('config_query_test')
579 ->exists('array.level1.level2')
581 $this->assertResults($all);
582 $this->queryResults = $this->factory->get('config_query_test')
583 ->exists('array.level1')
585 $this->assertResults($all);
586 $this->queryResults = $this->factory->get('config_query_test')
589 $this->assertResults($all);
590 $this->queryResults = $this->factory->get('config_query_test')
591 ->notExists('array.level1.level2')
593 $this->assertResults([]);
594 $this->queryResults = $this->factory->get('config_query_test')
595 ->notExists('array.level1')
597 $this->assertResults([]);
598 $this->queryResults = $this->factory->get('config_query_test')
601 $this->assertResults([]);
605 * Tests case sensitivity.
607 public function testCaseSensitivity() {
608 // Filter by label with a known containing case-sensitive word.
609 $this->queryResults = $this->factory->get('config_query_test')
610 ->condition('label', 'TEST', 'CONTAINS')
612 $this->assertResults(['3', '4', '5']);
614 $this->queryResults = $this->factory->get('config_query_test')
615 ->condition('label', 'test', 'CONTAINS')
617 $this->assertResults(['3', '4', '5']);
621 * Tests lookup keys are added to the key value store.
623 public function testLookupKeys() {
624 \Drupal::service('state')->set('config_test.lookup_keys', TRUE);
625 \Drupal::entityManager()->clearCachedDefinitions();
626 $key_value = $this->container->get('keyvalue')->get(QueryFactory::CONFIG_LOOKUP_PREFIX . 'config_test');
629 $storage = \Drupal::entityTypeManager()->getStorage('config_test');
630 $entity = $storage->create([
631 'label' => $this->randomMachineName(),
635 $test_entities[$entity->getConfigDependencyName()] = $entity;
636 $entity->enforceIsNew();
639 $expected[] = $entity->getConfigDependencyName();
640 $this->assertEqual($expected, $key_value->get('style:test'));
642 $entity = $storage->create([
643 'label' => $this->randomMachineName(),
647 $test_entities[$entity->getConfigDependencyName()] = $entity;
648 $entity->enforceIsNew();
650 $expected[] = $entity->getConfigDependencyName();
651 $this->assertEqual($expected, $key_value->get('style:test'));
653 $entity = $storage->create([
654 'label' => $this->randomMachineName(),
658 $entity->enforceIsNew();
660 // Do not add this entity to the list of expected result as it has a
662 $this->assertEqual($expected, $key_value->get('style:test'));
663 $this->assertEqual([$entity->getConfigDependencyName()], $key_value->get('style:blah'));
665 // Ensure that a delete clears a key.
667 $this->assertEqual(NULL, $key_value->get('style:blah'));
669 // Ensure that delete only clears one key.
670 $entity_id = array_pop($expected);
671 $test_entities[$entity_id]->delete();
672 $this->assertEqual($expected, $key_value->get('style:test'));
673 $entity_id = array_pop($expected);
674 $test_entities[$entity_id]->delete();
675 $this->assertEqual(NULL, $key_value->get('style:test'));
679 * Asserts the results as expected regardless of order.
681 * @param array $expected
682 * Array of expected entity IDs.
684 protected function assertResults($expected) {
685 $this->assertIdentical(count($this->queryResults), count($expected));
686 foreach ($expected as $value) {
687 // This also tests whether $this->queryResults[$value] is even set at all.
688 $this->assertIdentical($this->queryResults[$value], $value);