3 namespace Drupal\KernelTests\Core\Entity;
5 use Drupal\entity_test\Entity\EntityTest;
6 use Drupal\entity_test\Entity\EntityTestMulRev;
7 use Drupal\field\Entity\FieldConfig;
8 use Drupal\field\Entity\FieldStorageConfig;
9 use Drupal\language\Entity\ConfigurableLanguage;
10 use Drupal\taxonomy\Entity\Term;
11 use Drupal\taxonomy\Entity\Vocabulary;
12 use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
13 use Symfony\Component\HttpFoundation\Request;
16 * Tests Entity Query functionality.
20 class EntityQueryTest extends EntityKernelTestBase {
22 use EntityReferenceTestTrait;
29 public static $modules = ['field_test', 'language'];
34 protected $queryResults;
37 * A list of bundle machine names created for this test.
44 * Field name for the greetings field.
51 * Field name for the figures field.
58 * The entity_test_mulrev entity storage.
60 * @var \Drupal\Core\Entity\EntityStorageInterface
64 protected function setUp() {
67 $this->installEntitySchema('entity_test_mulrev');
69 $this->installConfig(['language']);
71 $figures = mb_strtolower($this->randomMachineName());
72 $greetings = mb_strtolower($this->randomMachineName());
73 foreach ([$figures => 'shape', $greetings => 'text'] as $field_name => $field_type) {
74 $field_storage = FieldStorageConfig::create([
75 'field_name' => $field_name,
76 'entity_type' => 'entity_test_mulrev',
77 'type' => $field_type,
80 $field_storage->save();
81 $field_storages[] = $field_storage;
84 for ($i = 0; $i < 2; $i++) {
85 // For the sake of tablesort, make sure the second bundle is higher than
86 // the first one. Beware: MySQL is not case sensitive.
88 $bundle = $this->randomMachineName();
89 } while ($bundles && strtolower($bundles[0]) >= strtolower($bundle));
90 entity_test_create_bundle($bundle);
91 foreach ($field_storages as $field_storage) {
93 'field_storage' => $field_storage,
99 // Each unit is a list of field name, langcode and a column-value array.
100 $units[] = [$figures, 'en', [
102 'shape' => 'triangle',
105 $units[] = [$figures, 'en', [
110 // To make it easier to test sorting, the greetings get formats according
111 // to their langcode.
112 $units[] = [$greetings, 'tr', [
113 'value' => 'merhaba',
114 'format' => 'format-tr',
117 $units[] = [$greetings, 'pl', [
119 'format' => 'format-pl',
122 // Make these languages available to the greetings field.
123 ConfigurableLanguage::createFromLangcode('tr')->save();
124 ConfigurableLanguage::createFromLangcode('pl')->save();
125 // Calculate the cartesian product of the unit array by looking at the
126 // bits of $i and add the unit at the bits that are 1. For example,
127 // decimal 13 is binary 1101 so unit 3,2 and 0 will be added to the
129 for ($i = 1; $i <= 15; $i++) {
130 $entity = EntityTestMulRev::create([
131 'type' => $bundles[$i & 1],
132 'name' => $this->randomMachineName(),
135 // Make sure the name is set for every language that we might create.
136 foreach (['tr', 'pl'] as $langcode) {
137 $entity->addTranslation($langcode)->name = $this->randomMachineName();
139 foreach (array_reverse(str_split(decbin($i))) as $key => $bit) {
141 // @todo https://www.drupal.org/project/drupal/issues/3001920 Doing
142 // list($field_name, $langcode, $values) = $units[$key]; causes
143 // problems in PHP 7.3. Revert to better variable names once
144 // https://bugs.php.net/bug.php?id=76937 is fixed.
145 $entity->getTranslation($units[$key][1])->{$units[$key][0]}[] = $units[$key][2];
150 $this->bundles = $bundles;
151 $this->figures = $figures;
152 $this->greetings = $greetings;
153 $this->storage = $this->container->get('entity_type.manager')->getStorage('entity_test_mulrev');
157 * Test basic functionality.
159 public function testEntityQuery() {
160 $greetings = $this->greetings;
161 $figures = $this->figures;
162 $this->queryResults = $this->storage
164 ->exists($greetings, 'tr')
165 ->condition("$figures.color", 'red')
168 // As unit 0 was the red triangle and unit 2 was the turkish greeting,
169 // bit 0 and bit 2 needs to be set.
170 $this->assertResult(5, 7, 13, 15);
172 $query = $this->storage
174 ->exists($greetings, 'tr')
175 ->condition("$figures.color", 'red')
177 $count_query = clone $query;
178 $this->assertEqual(12, $count_query->count()->execute());
179 $this->queryResults = $query->execute();
180 // Now bit 0 (1, 3, 5, 7, 9, 11, 13, 15) or bit 2 (4, 5, 6, 7, 12, 13, 14,
181 // 15) needs to be set.
182 $this->assertResult(1, 3, 4, 5, 6, 7, 9, 11, 12, 13, 14, 15);
184 // Test cloning of query conditions.
185 $query = $this->storage
187 ->condition("$figures.color", 'red')
189 $cloned_query = clone $query;
191 ->condition("$figures.shape", 'circle');
192 // Bit 0 (1, 3, 5, 7, 9, 11, 13, 15) needs to be set.
193 $this->queryResults = $query->execute();
194 $this->assertResult(1, 3, 5, 7, 9, 11, 13, 15);
195 // No red color has a circle shape.
196 $this->queryResults = $cloned_query->execute();
197 $this->assertResult();
199 $query = $this->storage->getQuery();
200 $group = $query->orConditionGroup()
201 ->exists($greetings, 'tr')
202 ->condition("$figures.color", 'red');
203 $this->queryResults = $query
205 ->condition("$greetings.value", 'sie', 'STARTS_WITH')
206 ->sort('revision_id')
208 // Bit 3 and (bit 0 or 2) -- the above 8 part of the above.
209 $this->assertResult(9, 11, 12, 13, 14, 15);
211 // No figure has both the colors blue and red at the same time.
212 $this->queryResults = $this->storage
214 ->condition("$figures.color", 'blue')
215 ->condition("$figures.color", 'red')
218 $this->assertResult();
220 // But an entity might have a red and a blue figure both.
221 $query = $this->storage->getQuery();
222 $group_blue = $query->andConditionGroup()->condition("$figures.color", 'blue');
223 $group_red = $query->andConditionGroup()->condition("$figures.color", 'red');
224 $this->queryResults = $query
225 ->condition($group_blue)
226 ->condition($group_red)
227 ->sort('revision_id')
229 // Unit 0 and unit 1, so bits 0 1.
230 $this->assertResult(3, 7, 11, 15);
232 // Do the same test but with IN operator.
233 $query = $this->storage->getQuery();
234 $group_blue = $query->andConditionGroup()->condition("$figures.color", ['blue'], 'IN');
235 $group_red = $query->andConditionGroup()->condition("$figures.color", ['red'], 'IN');
236 $this->queryResults = $query
237 ->condition($group_blue)
238 ->condition($group_red)
241 // Unit 0 and unit 1, so bits 0 1.
242 $this->assertResult(3, 7, 11, 15);
244 // An entity might have either red or blue figure.
245 $this->queryResults = $this->storage
247 ->condition("$figures.color", ['blue', 'red'], 'IN')
251 $this->assertResult(1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15);
253 $this->queryResults = $this->storage
255 ->exists("$figures.color")
256 ->notExists("$greetings.value")
259 // Bit 0 or 1 is on but 2 and 3 are not.
260 $this->assertResult(1, 2, 3);
261 // Now update the 'merhaba' string to xsiemax which is not a meaningful
262 // word but allows us to test revisions and string operations.
263 $ids = $this->storage
265 ->condition("$greetings.value", 'merhaba')
268 $entities = EntityTestMulRev::loadMultiple($ids);
269 $first_entity = reset($entities);
270 $old_name = $first_entity->name->value;
271 foreach ($entities as $entity) {
272 $entity->setNewRevision();
273 $entity->getTranslation('tr')->$greetings->value = 'xsiemax';
274 $entity->name->value .= 'x';
277 // We changed the entity names, so the current revision should not match.
278 $this->queryResults = $this->storage
280 ->condition('name.value', $old_name)
282 $this->assertResult();
283 // Only if all revisions are queried, we find the old revision.
284 $this->queryResults = $this->storage
286 ->condition('name.value', $old_name)
288 ->sort('revision_id')
290 $this->assertRevisionResult([$first_entity->id()], [$first_entity->id()]);
291 // When querying current revisions, this string is no longer found.
292 $this->queryResults = $this->storage
294 ->condition("$greetings.value", 'merhaba')
296 $this->assertResult();
297 $this->queryResults = $this->storage
299 ->condition("$greetings.value", 'merhaba')
301 ->sort('revision_id')
303 // The query only matches the original revisions.
304 $this->assertRevisionResult([4, 5, 6, 7, 12, 13, 14, 15], [4, 5, 6, 7, 12, 13, 14, 15]);
305 $results = $this->storage
307 ->condition("$greetings.value", 'siema', 'CONTAINS')
310 // This matches both the original and new current revisions, multiple
311 // revisions are returned for some entities.
312 $assert = [16 => '4', 17 => '5', 18 => '6', 19 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => '11', 20 => '12', 21 => '13', 22 => '14', 23 => '15'];
313 $this->assertIdentical($results, $assert);
314 $results = $this->storage
316 ->condition("$greetings.value", 'siema', 'STARTS_WITH')
317 ->sort('revision_id')
319 // Now we only get the ones that originally were siema, entity id 8 and
321 $this->assertIdentical($results, array_slice($assert, 4, 8, TRUE));
322 $results = $this->storage
324 ->condition("$greetings.value", 'a', 'ENDS_WITH')
325 ->sort('revision_id')
327 // It is very important that we do not get the ones which only have
328 // xsiemax despite originally they were merhaba, ie. ended with a.
329 $this->assertIdentical($results, array_slice($assert, 4, 8, TRUE));
330 $results = $this->storage
332 ->condition("$greetings.value", 'a', 'ENDS_WITH')
335 ->sort('revision_id')
337 // Now we get everything.
338 $assert = [4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => '11', 12 => '12', 20 => '12', 13 => '13', 21 => '13', 14 => '14', 22 => '14', 15 => '15', 23 => '15'];
339 $this->assertIdentical($results, $assert);
341 // Check that a query on the latest revisions without any condition returns
342 // the correct results.
343 $results = $this->storage
347 ->sort('revision_id')
349 $expected = [1 => '1', 2 => '2', 3 => '3', 16 => '4', 17 => '5', 18 => '6', 19 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => '11', 20 => '12', 21 => '13', 22 => '14', 23 => '15'];
350 $this->assertSame($expected, $results);
356 * Warning: this is complicated.
358 public function testSort() {
359 $greetings = $this->greetings;
360 $figures = $this->figures;
361 // Order up and down on a number.
362 $this->queryResults = $this->storage
366 $this->assertResult(range(1, 15));
367 $this->queryResults = $this->storage
371 $this->assertResult(range(15, 1));
372 $query = $this->storage
374 ->sort("$figures.color")
375 ->sort("$greetings.format")
377 // As we do not have any conditions, here are the possible colors and
378 // language codes, already in order, with the first occurrence of the
379 // entity id marked with *:
411 $count_query = clone $query;
412 $this->assertEqual(15, $count_query->count()->execute());
413 $this->queryResults = $query->execute();
414 $this->assertResult(8, 12, 4, 2, 3, 10, 11, 14, 15, 6, 7, 1, 9, 13, 5);
416 // Test the pager by setting element #1 to page 2 with a page size of 4.
417 // Results will be #8-12 from above.
418 $request = Request::createFromGlobals();
419 $request->query->replace([
422 \Drupal::getContainer()->get('request_stack')->push($request);
423 $this->queryResults = $this->storage
425 ->sort("$figures.color")
426 ->sort("$greetings.format")
430 $this->assertResult(15, 6, 7, 1);
432 // Now test the reversed order.
433 $query = $this->storage
435 ->sort("$figures.color", 'DESC')
436 ->sort("$greetings.format", 'DESC')
437 ->sort('id', 'DESC');
438 $count_query = clone $query;
439 $this->assertEqual(15, $count_query->count()->execute());
440 $this->queryResults = $query->execute();
441 $this->assertResult(15, 13, 7, 5, 11, 9, 3, 1, 14, 6, 10, 2, 12, 4, 8);
447 public function testTableSort() {
448 // While ordering on bundles do not give us a definite order, we can still
449 // assert that all entities from one bundle are after the other as the
451 $request = Request::createFromGlobals();
452 $request->query->replace([
456 \Drupal::getContainer()->get('request_stack')->push($request);
459 'id' => ['data' => 'Id', 'specifier' => 'id'],
460 'type' => ['data' => 'Type', 'specifier' => 'type'],
463 $this->queryResults = array_values($this->storage
467 $this->assertBundleOrder('asc');
469 $request->query->add([
472 \Drupal::getContainer()->get('request_stack')->push($request);
475 'id' => ['data' => 'Id', 'specifier' => 'id'],
476 'type' => ['data' => 'Type', 'specifier' => 'type'],
478 $this->queryResults = array_values($this->storage
482 $this->assertBundleOrder('desc');
484 // Ordering on ID is definite, however.
485 $request->query->add([
488 \Drupal::getContainer()->get('request_stack')->push($request);
489 $this->queryResults = $this->storage
493 $this->assertResult(range(15, 1));
497 * Test that count queries are separated across entity types.
499 public function testCount() {
500 // Create a field with the same name in a different entity type.
501 $field_name = $this->figures;
502 $field_storage = FieldStorageConfig::create([
503 'field_name' => $field_name,
504 'entity_type' => 'entity_test',
507 'translatable' => TRUE,
509 $field_storage->save();
510 $bundle = $this->randomMachineName();
511 FieldConfig::create([
512 'field_storage' => $field_storage,
516 $entity = EntityTest::create([
520 $entity->enforceIsNew();
523 // As the single entity of this type we just saved does not have a value
524 // in the color field, the result should be 0.
525 $count = $this->container->get('entity_type.manager')
526 ->getStorage('entity_test')
528 ->exists("$field_name.color")
531 $this->assertFalse($count);
535 * Tests that nested condition groups work as expected.
537 public function testNestedConditionGroups() {
538 // Query for all entities of the first bundle that have either a red
539 // triangle as a figure or the Turkish greeting as a greeting.
540 $query = $this->storage->getQuery();
542 $first_and = $query->andConditionGroup()
543 ->condition($this->figures . '.color', 'red')
544 ->condition($this->figures . '.shape', 'triangle');
545 $second_and = $query->andConditionGroup()
546 ->condition($this->greetings . '.value', 'merhaba')
547 ->condition($this->greetings . '.format', 'format-tr');
549 $or = $query->orConditionGroup()
550 ->condition($first_and)
551 ->condition($second_and);
553 $this->queryResults = $query
555 ->condition('type', reset($this->bundles))
559 $this->assertResult(4, 6, 12, 14);
563 * Tests that condition count returns expected number of conditions.
565 public function testConditionCount() {
566 // Query for all entities of the first bundle that
567 // have red as a colour AND are triangle shaped.
568 $query = $this->storage->getQuery();
570 // Add an AND condition group with 2 conditions in it.
571 $and_condition_group = $query->andConditionGroup()
572 ->condition($this->figures . '.color', 'red')
573 ->condition($this->figures . '.shape', 'triangle');
575 // We added 2 conditions so count should be 2.
576 $this->assertEqual($and_condition_group->count(), 2);
578 // Add an OR condition group with 2 conditions in it.
579 $or_condition_group = $query->orConditionGroup()
580 ->condition($this->figures . '.color', 'red')
581 ->condition($this->figures . '.shape', 'triangle');
583 // We added 2 conditions so count should be 2.
584 $this->assertEqual($or_condition_group->count(), 2);
588 * Test queries with delta conditions.
590 public function testDelta() {
591 $figures = $this->figures;
592 // Test numeric delta value in field condition.
593 $this->queryResults = $this->storage
595 ->condition("$figures.0.color", 'red')
598 // As unit 0 at delta 0 was the red triangle bit 0 needs to be set.
599 $this->assertResult(1, 3, 5, 7, 9, 11, 13, 15);
601 $this->queryResults = $this->storage
603 ->condition("$figures.1.color", 'red')
606 // Delta 1 is not red.
607 $this->assertResult();
609 // Test on two different deltas.
610 $query = $this->storage->getQuery();
611 $or = $query->andConditionGroup()
612 ->condition("$figures.0.color", 'red')
613 ->condition("$figures.1.color", 'blue');
614 $this->queryResults = $query
618 $this->assertResult(3, 7, 11, 15);
620 // Test the delta range condition.
621 $this->queryResults = $this->storage
623 ->condition("$figures.%delta.color", ['blue', 'red'], 'IN')
624 ->condition("$figures.%delta", [0, 1], 'IN')
627 // Figure delta 0 or 1 can be blue or red, this matches a lot of entities.
628 $this->assertResult(1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15);
630 // Test the delta range condition without conditions on the value.
631 $this->queryResults = $this->storage
633 ->condition("$figures.%delta", 1)
636 // Entity needs to have at least two figures.
637 $this->assertResult(3, 7, 11, 15);
639 // Numeric delta on single value base field should return results only if
640 // the first item is being targeted.
641 $this->queryResults = $this->storage
643 ->condition("id.0.value", [1, 3, 5], 'IN')
646 $this->assertResult(1, 3, 5);
647 $this->queryResults = $this->storage
649 ->condition("id.1.value", [1, 3, 5], 'IN')
652 $this->assertResult();
654 // Delta range condition on single value base field should return results
655 // only if just the field value is targeted.
656 $this->queryResults = $this->storage
658 ->condition("id.%delta.value", [1, 3, 5], 'IN')
661 $this->assertResult(1, 3, 5);
662 $this->queryResults = $this->storage
664 ->condition("id.%delta.value", [1, 3, 5], 'IN')
665 ->condition("id.%delta", 0, '=')
668 $this->assertResult(1, 3, 5);
669 $this->queryResults = $this->storage
671 ->condition("id.%delta.value", [1, 3, 5], 'IN')
672 ->condition("id.%delta", 1, '=')
675 $this->assertResult();
679 protected function assertResult() {
681 $expected = func_get_args();
682 if ($expected && is_array($expected[0])) {
683 $expected = $expected[0];
685 foreach ($expected as $binary) {
686 $assert[$binary] = strval($binary);
688 $this->assertIdentical($this->queryResults, $assert);
691 protected function assertRevisionResult($keys, $expected) {
693 foreach ($expected as $key => $binary) {
694 $assert[$keys[$key]] = strval($binary);
696 $this->assertIdentical($this->queryResults, $assert);
700 protected function assertBundleOrder($order) {
701 // This loop is for bundle1 entities.
702 for ($i = 1; $i <= 15; $i += 2) {
704 $index1 = array_search($i, $this->queryResults);
705 $this->assertNotIdentical($index1, FALSE, "$i found at $index1.");
706 // This loop is for bundle2 entities.
707 for ($j = 2; $j <= 15; $j += 2) {
709 if ($order == 'asc') {
710 $ok = $index1 > array_search($j, $this->queryResults);
713 $ok = $index1 < array_search($j, $this->queryResults);
717 $this->assertTrue($ok, "$i is after all entities in bundle2");
722 * Test adding a tag and metadata to the Entity query object.
724 * The tags and metadata should propagate to the SQL query object.
726 public function testMetaData() {
727 $query = $this->storage->getQuery();
729 ->addTag('efq_metadata_test')
730 ->addMetaData('foo', 'bar')
733 global $efq_test_metadata;
734 $this->assertEqual($efq_test_metadata, 'bar', 'Tag and metadata propagated to the SQL query object.');
738 * Test case sensitive and in-sensitive query conditions.
740 public function testCaseSensitivity() {
741 $bundle = $this->randomMachineName();
743 $field_storage = FieldStorageConfig::create([
744 'field_name' => 'field_ci',
745 'entity_type' => 'entity_test_mulrev',
748 'translatable' => FALSE,
750 'case_sensitive' => FALSE,
753 $field_storage->save();
755 FieldConfig::create([
756 'field_storage' => $field_storage,
760 $field_storage = FieldStorageConfig::create([
761 'field_name' => 'field_cs',
762 'entity_type' => 'entity_test_mulrev',
765 'translatable' => FALSE,
767 'case_sensitive' => TRUE,
770 $field_storage->save();
772 FieldConfig::create([
773 'field_storage' => $field_storage,
779 for ($i = 0; $i < 2; $i++) {
780 // If the last 4 of the string are all numbers, then there is no
781 // difference between upper and lowercase and the case sensitive CONTAINS
782 // test will fail. Ensure that can not happen by appending a non-numeric
783 // character. See https://www.drupal.org/node/2397297.
784 $string = $this->randomMachineName(7) . 'a';
786 'original' => $string,
787 'uppercase' => mb_strtoupper($string),
788 'lowercase' => mb_strtolower($string),
792 EntityTestMulRev::create([
794 'name' => $this->randomMachineName(),
796 'field_ci' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'],
797 'field_cs' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'],
800 // Check the case insensitive field, = operator.
801 $result = $this->storage
803 ->condition('field_ci', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase'])
805 $this->assertIdentical(count($result), 1, 'Case insensitive, lowercase');
807 $result = $this->storage
809 ->condition('field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase'])
811 $this->assertIdentical(count($result), 1, 'Case insensitive, uppercase');
813 $result = $this->storage
815 ->condition('field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'])
817 $this->assertIdentical(count($result), 1, 'Case insensitive, mixed.');
819 // Check the case sensitive field, = operator.
820 $result = $this->storage
822 ->condition('field_cs', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase'])
824 $this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.');
826 $result = $this->storage
828 ->condition('field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase'])
830 $this->assertIdentical(count($result), 0, 'Case sensitive, uppercase.');
832 $result = $this->storage
834 ->condition('field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'])
836 $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
838 // Check the case insensitive field, IN operator.
839 $result = $this->storage
841 ->condition('field_ci', [$fixtures[0]['lowercase'] . $fixtures[1]['lowercase']], 'IN')
843 $this->assertIdentical(count($result), 1, 'Case insensitive, lowercase');
845 $result = $this->storage
847 ->condition('field_ci', [$fixtures[0]['uppercase'] . $fixtures[1]['uppercase']], 'IN')->execute();
848 $this->assertIdentical(count($result), 1, 'Case insensitive, uppercase');
850 $result = $this->storage
852 ->condition('field_ci', [$fixtures[0]['uppercase'] . $fixtures[1]['lowercase']], 'IN')
854 $this->assertIdentical(count($result), 1, 'Case insensitive, mixed');
856 // Check the case sensitive field, IN operator.
857 $result = $this->storage
859 ->condition('field_cs', [$fixtures[0]['lowercase'] . $fixtures[1]['lowercase']], 'IN')
861 $this->assertIdentical(count($result), 0, 'Case sensitive, lowercase');
863 $result = $this->storage
865 ->condition('field_cs', [$fixtures[0]['uppercase'] . $fixtures[1]['uppercase']], 'IN')
867 $this->assertIdentical(count($result), 0, 'Case sensitive, uppercase');
869 $result = $this->storage
871 ->condition('field_cs', [$fixtures[0]['uppercase'] . $fixtures[1]['lowercase']], 'IN')
873 $this->assertIdentical(count($result), 1, 'Case sensitive, mixed');
875 // Check the case insensitive field, STARTS_WITH operator.
876 $result = $this->storage
878 ->condition('field_ci', $fixtures[0]['lowercase'], 'STARTS_WITH')
880 $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
882 $result = $this->storage
884 ->condition('field_ci', $fixtures[0]['uppercase'], 'STARTS_WITH')
886 $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
888 // Check the case sensitive field, STARTS_WITH operator.
889 $result = $this->storage
891 ->condition('field_cs', $fixtures[0]['lowercase'], 'STARTS_WITH')
893 $this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.');
895 $result = $this->storage
897 ->condition('field_cs', $fixtures[0]['uppercase'], 'STARTS_WITH')
899 $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
901 // Check the case insensitive field, ENDS_WITH operator.
902 $result = $this->storage
904 ->condition('field_ci', $fixtures[1]['lowercase'], 'ENDS_WITH')
906 $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
908 $result = $this->storage
910 ->condition('field_ci', $fixtures[1]['uppercase'], 'ENDS_WITH')
912 $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
914 // Check the case sensitive field, ENDS_WITH operator.
915 $result = $this->storage
917 ->condition('field_cs', $fixtures[1]['lowercase'], 'ENDS_WITH')
919 $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
921 $result = $this->storage
923 ->condition('field_cs', $fixtures[1]['uppercase'], 'ENDS_WITH')
925 $this->assertIdentical(count($result), 0, 'Case sensitive, exact match.');
927 // Check the case insensitive field, CONTAINS operator, use the inner 8
928 // characters of the uppercase and lowercase strings.
929 $result = $this->storage
931 ->condition('field_ci', mb_substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS')
933 $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
935 $result = $this->storage
937 ->condition('field_ci', mb_strtolower(mb_substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS')
939 $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
941 // Check the case sensitive field, CONTAINS operator.
942 $result = $this->storage
944 ->condition('field_cs', mb_substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS')
946 $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
948 $result = $this->storage
950 ->condition('field_cs', mb_strtolower(mb_substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS')
952 $this->assertIdentical(count($result), 0, 'Case sensitive, exact match.');
957 * Test base fields with multiple columns.
959 public function testBaseFieldMultipleColumns() {
960 $this->enableModules(['taxonomy']);
961 $this->installEntitySchema('taxonomy_term');
963 Vocabulary::create(['vid' => 'tags']);
965 $term1 = Term::create([
966 'name' => $this->randomMachineName(),
969 'value' => $this->randomString(),
970 'format' => 'format1',
975 $term2 = Term::create([
976 'name' => $this->randomMachineName(),
979 'value' => $this->randomString(),
980 'format' => 'format2',
985 $ids = $this->container->get('entity_type.manager')
986 ->getStorage('taxonomy_term')
988 ->condition('description.format', 'format1')
991 $this->assertEqual(count($ids), 1);
992 $this->assertEqual($term1->id(), reset($ids));
996 * Test pending revisions.
998 public function testPendingRevisions() {
999 // Ensure entity 14 is returned.
1000 $result = $this->storage
1002 ->condition('id', [14], 'IN')
1004 $this->assertEqual(count($result), 1);
1006 // Set a revision on entity 14 that isn't the current default.
1007 $entity = EntityTestMulRev::load(14);
1008 $current_values = $entity->{$this->figures}->getValue();
1010 $entity->setNewRevision(TRUE);
1011 $entity->isDefaultRevision(FALSE);
1012 $entity->{$this->figures}->setValue([
1014 'shape' => 'square',
1018 // Entity query should still return entity 14.
1019 $result = $this->storage
1021 ->condition('id', [14], 'IN')
1023 $this->assertEqual(count($result), 1);
1025 // Verify that field conditions on the default and pending revision are
1026 // work as expected.
1027 $result = $this->storage
1029 ->condition('id', [14], 'IN')
1030 ->condition("$this->figures.color", $current_values[0]['color'])
1032 $this->assertEqual($result, [14 => '14']);
1033 $result = $this->storage
1035 ->condition('id', [14], 'IN')
1036 ->condition("$this->figures.color", 'red')
1039 $this->assertEqual($result, [16 => '14']);
1041 // Add another pending revision on the same entity and repeat the checks.
1042 $entity->setNewRevision(TRUE);
1043 $entity->isDefaultRevision(FALSE);
1044 $entity->{$this->figures}->setValue([
1046 'shape' => 'square',
1050 // A non-revisioned entity query should still return entity 14.
1051 $result = $this->storage
1053 ->condition('id', [14], 'IN')
1055 $this->assertCount(1, $result);
1056 $this->assertSame([14 => '14'], $result);
1058 // Now check an entity query on the latest revision.
1059 $result = $this->storage
1061 ->condition('id', [14], 'IN')
1064 $this->assertCount(1, $result);
1065 $this->assertSame([17 => '14'], $result);
1067 // Verify that field conditions on the default and pending revision still
1068 // work as expected.
1069 $result = $this->storage
1071 ->condition('id', [14], 'IN')
1072 ->condition("$this->figures.color", $current_values[0]['color'])
1074 $this->assertSame([14 => '14'], $result);
1076 // Now there are two revisions with same value for the figure color.
1077 $result = $this->storage
1079 ->condition('id', [14], 'IN')
1080 ->condition("$this->figures.color", 'red')
1083 $this->assertSame([16 => '14', 17 => '14'], $result);
1085 // Check that querying for the latest revision returns the correct one.
1086 $result = $this->storage
1088 ->condition('id', [14], 'IN')
1089 ->condition("$this->figures.color", 'red')
1092 $this->assertSame([17 => '14'], $result);
1096 * Test against SQL inject of condition field. This covers a
1097 * database driver's EntityQuery\Condition class.
1099 public function testInjectionInCondition() {
1101 $this->queryResults = $this->storage
1103 ->condition('1 ; -- ', [0, 1], 'IN')
1106 $this->fail('SQL Injection attempt in Entity Query condition in operator should result in an exception.');
1108 catch (\Exception $e) {
1109 $this->pass('SQL Injection attempt in Entity Query condition in operator should result in an exception.');
1114 * Tests that EntityQuery works when querying the same entity from two fields.
1116 public function testWithTwoEntityReferenceFieldsToSameEntityType() {
1117 // Create two entity reference fields referring 'entity_test' entities.
1118 $this->createEntityReferenceField('entity_test', 'entity_test', 'ref1', $this->randomMachineName(), 'entity_test');
1119 $this->createEntityReferenceField('entity_test', 'entity_test', 'ref2', $this->randomMachineName(), 'entity_test');
1121 $storage = $this->container->get('entity_type.manager')
1122 ->getStorage('entity_test');
1124 // Create two entities to be referred.
1125 $ref1 = EntityTest::create(['type' => 'entity_test']);
1127 $ref2 = EntityTest::create(['type' => 'entity_test']);
1130 // Create a main entity referring the previous created entities.
1131 $entity = EntityTest::create([
1132 'type' => 'entity_test',
1133 'ref1' => $ref1->id(),
1134 'ref2' => $ref2->id(),
1138 // Check that works when referring with "{$field_name}".
1139 $result = $storage->getQuery()
1140 ->condition('type', 'entity_test')
1141 ->condition('ref1', $ref1->id())
1142 ->condition('ref2', $ref2->id())
1144 $this->assertCount(1, $result);
1145 $this->assertEquals($entity->id(), reset($result));
1147 // Check that works when referring with "{$field_name}.target_id".
1148 $result = $storage->getQuery()
1149 ->condition('type', 'entity_test')
1150 ->condition('ref1.target_id', $ref1->id())
1151 ->condition('ref2.target_id', $ref2->id())
1153 $this->assertCount(1, $result);
1154 $this->assertEquals($entity->id(), reset($result));
1156 // Check that works when referring with "{$field_name}.entity.id".
1157 $result = $storage->getQuery()
1158 ->condition('type', 'entity_test')
1159 ->condition('ref1.entity.id', $ref1->id())
1160 ->condition('ref2.entity.id', $ref2->id())
1162 $this->assertCount(1, $result);
1163 $this->assertEquals($entity->id(), reset($result));
1167 * Tests entity queries with condition on the revision metadata keys.
1169 public function testConditionOnRevisionMetadataKeys() {
1170 $this->installModule('entity_test_revlog');
1171 $this->installEntitySchema('entity_test_revlog');
1173 /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
1174 $entity_type_manager = $this->container->get('entity_type.manager');
1175 /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */
1176 $entity_type = $entity_type_manager->getDefinition('entity_test_revlog');
1177 /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
1178 $storage = $entity_type_manager->getStorage('entity_test_revlog');
1180 $revision_created_timestamp = time();
1181 $revision_created_field_name = $entity_type->getRevisionMetadataKey('revision_created');
1182 $entity = $storage->create([
1183 'type' => 'entity_test',
1184 $revision_created_field_name => $revision_created_timestamp,
1188 // Query only the default revision.
1189 $result = $storage->getQuery()
1190 ->condition($revision_created_field_name, $revision_created_timestamp)
1192 $this->assertCount(1, $result);
1193 $this->assertEquals($entity->id(), reset($result));
1195 // Query all revisions.
1196 $result = $storage->getQuery()
1197 ->condition($revision_created_field_name, $revision_created_timestamp)
1200 $this->assertCount(1, $result);
1201 $this->assertEquals($entity->id(), reset($result));