Updated to Drupal 8.6.4, which is PHP 7.3 friendly. Also updated HTMLaw library....
[yaffs-website] / web / core / tests / Drupal / KernelTests / Core / Entity / EntityQueryTest.php
1 <?php
2
3 namespace Drupal\KernelTests\Core\Entity;
4
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;
14
15 /**
16  * Tests Entity Query functionality.
17  *
18  * @group Entity
19  */
20 class EntityQueryTest extends EntityKernelTestBase {
21
22   use EntityReferenceTestTrait;
23
24   /**
25    * Modules to enable.
26    *
27    * @var array
28    */
29   public static $modules = ['field_test', 'language'];
30
31   /**
32    * @var array
33    */
34   protected $queryResults;
35
36   /**
37    * A list of bundle machine names created for this test.
38    *
39    * @var string[]
40    */
41   protected $bundles;
42
43   /**
44    * Field name for the greetings field.
45    *
46    * @var string
47    */
48   public $greetings;
49
50   /**
51    * Field name for the figures field.
52    *
53    * @var string
54    */
55   public $figures;
56
57   /**
58    * The entity_test_mulrev entity storage.
59    *
60    * @var \Drupal\Core\Entity\EntityStorageInterface
61    */
62   protected $storage;
63
64   protected function setUp() {
65     parent::setUp();
66
67     $this->installEntitySchema('entity_test_mulrev');
68
69     $this->installConfig(['language']);
70
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,
78         'cardinality' => 2,
79       ]);
80       $field_storage->save();
81       $field_storages[] = $field_storage;
82     }
83     $bundles = [];
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.
87       do {
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) {
92         FieldConfig::create([
93           'field_storage' => $field_storage,
94           'bundle' => $bundle,
95         ])->save();
96       }
97       $bundles[] = $bundle;
98     }
99     // Each unit is a list of field name, langcode and a column-value array.
100     $units[] = [$figures, 'en', [
101         'color' => 'red',
102         'shape' => 'triangle',
103       ],
104     ];
105     $units[] = [$figures, 'en', [
106         'color' => 'blue',
107         'shape' => 'circle',
108       ],
109     ];
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',
115       ],
116     ];
117     $units[] = [$greetings, 'pl', [
118         'value' => 'siema',
119         'format' => 'format-pl',
120       ],
121     ];
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
128     // entity.
129     for ($i = 1; $i <= 15; $i++) {
130       $entity = EntityTestMulRev::create([
131         'type' => $bundles[$i & 1],
132         'name' => $this->randomMachineName(),
133         'langcode' => 'en',
134       ]);
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();
138       }
139       foreach (array_reverse(str_split(decbin($i))) as $key => $bit) {
140         if ($bit) {
141           list($field_name, $langcode, $values) = $units[$key];
142           $entity->getTranslation($langcode)->{$field_name}[] = $values;
143         }
144       }
145       $entity->save();
146     }
147     $this->bundles = $bundles;
148     $this->figures = $figures;
149     $this->greetings = $greetings;
150     $this->storage = $this->container->get('entity_type.manager')->getStorage('entity_test_mulrev');
151   }
152
153   /**
154    * Test basic functionality.
155    */
156   public function testEntityQuery() {
157     $greetings = $this->greetings;
158     $figures = $this->figures;
159     $this->queryResults = $this->storage
160       ->getQuery()
161       ->exists($greetings, 'tr')
162       ->condition("$figures.color", 'red')
163       ->sort('id')
164       ->execute();
165     // As unit 0 was the red triangle and unit 2 was the turkish greeting,
166     // bit 0 and bit 2 needs to be set.
167     $this->assertResult(5, 7, 13, 15);
168
169     $query = $this->storage
170       ->getQuery('OR')
171       ->exists($greetings, 'tr')
172       ->condition("$figures.color", 'red')
173       ->sort('id');
174     $count_query = clone $query;
175     $this->assertEqual(12, $count_query->count()->execute());
176     $this->queryResults = $query->execute();
177     // Now bit 0 (1, 3, 5, 7, 9, 11, 13, 15) or bit 2 (4, 5, 6, 7, 12, 13, 14,
178     // 15) needs to be set.
179     $this->assertResult(1, 3, 4, 5, 6, 7, 9, 11, 12, 13, 14, 15);
180
181     // Test cloning of query conditions.
182     $query = $this->storage
183       ->getQuery()
184       ->condition("$figures.color", 'red')
185       ->sort('id');
186     $cloned_query = clone $query;
187     $cloned_query
188       ->condition("$figures.shape", 'circle');
189     // Bit 0 (1, 3, 5, 7, 9, 11, 13, 15) needs to be set.
190     $this->queryResults = $query->execute();
191     $this->assertResult(1, 3, 5, 7, 9, 11, 13, 15);
192     // No red color has a circle shape.
193     $this->queryResults = $cloned_query->execute();
194     $this->assertResult();
195
196     $query = $this->storage->getQuery();
197     $group = $query->orConditionGroup()
198       ->exists($greetings, 'tr')
199       ->condition("$figures.color", 'red');
200     $this->queryResults = $query
201       ->condition($group)
202       ->condition("$greetings.value", 'sie', 'STARTS_WITH')
203       ->sort('revision_id')
204       ->execute();
205     // Bit 3 and (bit 0 or 2) -- the above 8 part of the above.
206     $this->assertResult(9, 11, 12, 13, 14, 15);
207
208     // No figure has both the colors blue and red at the same time.
209     $this->queryResults = $this->storage
210       ->getQuery()
211       ->condition("$figures.color", 'blue')
212       ->condition("$figures.color", 'red')
213       ->sort('id')
214       ->execute();
215     $this->assertResult();
216
217     // But an entity might have a red and a blue figure both.
218     $query = $this->storage->getQuery();
219     $group_blue = $query->andConditionGroup()->condition("$figures.color", 'blue');
220     $group_red = $query->andConditionGroup()->condition("$figures.color", 'red');
221     $this->queryResults = $query
222       ->condition($group_blue)
223       ->condition($group_red)
224       ->sort('revision_id')
225       ->execute();
226     // Unit 0 and unit 1, so bits 0 1.
227     $this->assertResult(3, 7, 11, 15);
228
229     // Do the same test but with IN operator.
230     $query = $this->storage->getQuery();
231     $group_blue = $query->andConditionGroup()->condition("$figures.color", ['blue'], 'IN');
232     $group_red = $query->andConditionGroup()->condition("$figures.color", ['red'], 'IN');
233     $this->queryResults = $query
234       ->condition($group_blue)
235       ->condition($group_red)
236       ->sort('id')
237       ->execute();
238     // Unit 0 and unit 1, so bits 0 1.
239     $this->assertResult(3, 7, 11, 15);
240
241     // An entity might have either red or blue figure.
242     $this->queryResults = $this->storage
243       ->getQuery()
244       ->condition("$figures.color", ['blue', 'red'], 'IN')
245       ->sort('id')
246       ->execute();
247     // Bit 0 or 1 is on.
248     $this->assertResult(1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15);
249
250     $this->queryResults = $this->storage
251       ->getQuery()
252       ->exists("$figures.color")
253       ->notExists("$greetings.value")
254       ->sort('id')
255       ->execute();
256     // Bit 0 or 1 is on but 2 and 3 are not.
257     $this->assertResult(1, 2, 3);
258     // Now update the 'merhaba' string to xsiemax which is not a meaningful
259     // word but allows us to test revisions and string operations.
260     $ids = $this->storage
261       ->getQuery()
262       ->condition("$greetings.value", 'merhaba')
263       ->sort('id')
264       ->execute();
265     $entities = EntityTestMulRev::loadMultiple($ids);
266     $first_entity = reset($entities);
267     $old_name = $first_entity->name->value;
268     foreach ($entities as $entity) {
269       $entity->setNewRevision();
270       $entity->getTranslation('tr')->$greetings->value = 'xsiemax';
271       $entity->name->value .= 'x';
272       $entity->save();
273     }
274     // We changed the entity names, so the current revision should not match.
275     $this->queryResults = $this->storage
276       ->getQuery()
277       ->condition('name.value', $old_name)
278       ->execute();
279     $this->assertResult();
280     // Only if all revisions are queried, we find the old revision.
281     $this->queryResults = $this->storage
282       ->getQuery()
283       ->condition('name.value', $old_name)
284       ->allRevisions()
285       ->sort('revision_id')
286       ->execute();
287     $this->assertRevisionResult([$first_entity->id()], [$first_entity->id()]);
288     // When querying current revisions, this string is no longer found.
289     $this->queryResults = $this->storage
290       ->getQuery()
291       ->condition("$greetings.value", 'merhaba')
292       ->execute();
293     $this->assertResult();
294     $this->queryResults = $this->storage
295       ->getQuery()
296       ->condition("$greetings.value", 'merhaba')
297       ->allRevisions()
298       ->sort('revision_id')
299       ->execute();
300     // The query only matches the original revisions.
301     $this->assertRevisionResult([4, 5, 6, 7, 12, 13, 14, 15], [4, 5, 6, 7, 12, 13, 14, 15]);
302     $results = $this->storage
303       ->getQuery()
304       ->condition("$greetings.value", 'siema', 'CONTAINS')
305       ->sort('id')
306       ->execute();
307     // This matches both the original and new current revisions, multiple
308     // revisions are returned for some entities.
309     $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'];
310     $this->assertIdentical($results, $assert);
311     $results = $this->storage
312       ->getQuery()
313       ->condition("$greetings.value", 'siema', 'STARTS_WITH')
314       ->sort('revision_id')
315       ->execute();
316     // Now we only get the ones that originally were siema, entity id 8 and
317     // above.
318     $this->assertIdentical($results, array_slice($assert, 4, 8, TRUE));
319     $results = $this->storage
320       ->getQuery()
321       ->condition("$greetings.value", 'a', 'ENDS_WITH')
322       ->sort('revision_id')
323       ->execute();
324     // It is very important that we do not get the ones which only have
325     // xsiemax despite originally they were merhaba, ie. ended with a.
326     $this->assertIdentical($results, array_slice($assert, 4, 8, TRUE));
327     $results = $this->storage
328       ->getQuery()
329       ->condition("$greetings.value", 'a', 'ENDS_WITH')
330       ->allRevisions()
331       ->sort('id')
332       ->sort('revision_id')
333       ->execute();
334     // Now we get everything.
335     $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'];
336     $this->assertIdentical($results, $assert);
337
338     // Check that a query on the latest revisions without any condition returns
339     // the correct results.
340     $results = $this->storage
341       ->getQuery()
342       ->latestRevision()
343       ->sort('id')
344       ->sort('revision_id')
345       ->execute();
346     $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'];
347     $this->assertSame($expected, $results);
348   }
349
350   /**
351    * Test sort().
352    *
353    * Warning: this is complicated.
354    */
355   public function testSort() {
356     $greetings = $this->greetings;
357     $figures = $this->figures;
358     // Order up and down on a number.
359     $this->queryResults = $this->storage
360       ->getQuery()
361       ->sort('id')
362       ->execute();
363     $this->assertResult(range(1, 15));
364     $this->queryResults = $this->storage
365       ->getQuery()
366       ->sort('id', 'DESC')
367       ->execute();
368     $this->assertResult(range(15, 1));
369     $query = $this->storage
370       ->getQuery()
371       ->sort("$figures.color")
372       ->sort("$greetings.format")
373       ->sort('id');
374     // As we do not have any conditions, here are the possible colors and
375     // language codes, already in order, with the first occurrence of the
376     // entity id marked with *:
377     // 8  NULL pl *
378     // 12 NULL pl *
379
380     // 4  NULL tr *
381     // 12 NULL tr
382
383     // 2  blue NULL *
384     // 3  blue NULL *
385
386     // 10 blue pl *
387     // 11 blue pl *
388     // 14 blue pl *
389     // 15 blue pl *
390
391     // 6  blue tr *
392     // 7  blue tr *
393     // 14 blue tr
394     // 15 blue tr
395
396     // 1  red  NULL
397     // 3  red  NULL
398
399     // 9  red  pl *
400     // 11 red  pl
401     // 13 red  pl *
402     // 15 red  pl
403
404     // 5  red  tr *
405     // 7  red  tr
406     // 13 red  tr
407     // 15 red  tr
408     $count_query = clone $query;
409     $this->assertEqual(15, $count_query->count()->execute());
410     $this->queryResults = $query->execute();
411     $this->assertResult(8, 12, 4, 2, 3, 10, 11, 14, 15, 6, 7, 1, 9, 13, 5);
412
413     // Test the pager by setting element #1 to page 2 with a page size of 4.
414     // Results will be #8-12 from above.
415     $request = Request::createFromGlobals();
416     $request->query->replace([
417       'page' => '0,2',
418     ]);
419     \Drupal::getContainer()->get('request_stack')->push($request);
420     $this->queryResults = $this->storage
421       ->getQuery()
422       ->sort("$figures.color")
423       ->sort("$greetings.format")
424       ->sort('id')
425       ->pager(4, 1)
426       ->execute();
427     $this->assertResult(15, 6, 7, 1);
428
429     // Now test the reversed order.
430     $query = $this->storage
431       ->getQuery()
432       ->sort("$figures.color", 'DESC')
433       ->sort("$greetings.format", 'DESC')
434       ->sort('id', 'DESC');
435     $count_query = clone $query;
436     $this->assertEqual(15, $count_query->count()->execute());
437     $this->queryResults = $query->execute();
438     $this->assertResult(15, 13, 7, 5, 11, 9, 3, 1, 14, 6, 10, 2, 12, 4, 8);
439   }
440
441   /**
442    * Test tablesort().
443    */
444   public function testTableSort() {
445     // While ordering on bundles do not give us a definite order, we can still
446     // assert that all entities from one bundle are after the other as the
447     // order dictates.
448     $request = Request::createFromGlobals();
449     $request->query->replace([
450       'sort' => 'asc',
451       'order' => 'Type',
452     ]);
453     \Drupal::getContainer()->get('request_stack')->push($request);
454
455     $header = [
456       'id' => ['data' => 'Id', 'specifier' => 'id'],
457       'type' => ['data' => 'Type', 'specifier' => 'type'],
458     ];
459
460     $this->queryResults = array_values($this->storage
461       ->getQuery()
462       ->tableSort($header)
463       ->execute());
464     $this->assertBundleOrder('asc');
465
466     $request->query->add([
467       'sort' => 'desc',
468     ]);
469     \Drupal::getContainer()->get('request_stack')->push($request);
470
471     $header = [
472       'id' => ['data' => 'Id', 'specifier' => 'id'],
473       'type' => ['data' => 'Type', 'specifier' => 'type'],
474     ];
475     $this->queryResults = array_values($this->storage
476       ->getQuery()
477       ->tableSort($header)
478       ->execute());
479     $this->assertBundleOrder('desc');
480
481     // Ordering on ID is definite, however.
482     $request->query->add([
483       'order' => 'Id',
484     ]);
485     \Drupal::getContainer()->get('request_stack')->push($request);
486     $this->queryResults = $this->storage
487       ->getQuery()
488       ->tableSort($header)
489       ->execute();
490     $this->assertResult(range(15, 1));
491   }
492
493   /**
494    * Test that count queries are separated across entity types.
495    */
496   public function testCount() {
497     // Create a field with the same name in a different entity type.
498     $field_name = $this->figures;
499     $field_storage = FieldStorageConfig::create([
500       'field_name' => $field_name,
501       'entity_type' => 'entity_test',
502       'type' => 'shape',
503       'cardinality' => 2,
504       'translatable' => TRUE,
505     ]);
506     $field_storage->save();
507     $bundle = $this->randomMachineName();
508     FieldConfig::create([
509       'field_storage' => $field_storage,
510       'bundle' => $bundle,
511     ])->save();
512
513     $entity = EntityTest::create([
514       'id' => 1,
515       'type' => $bundle,
516     ]);
517     $entity->enforceIsNew();
518     $entity->save();
519
520     // As the single entity of this type we just saved does not have a value
521     // in the color field, the result should be 0.
522     $count = $this->container->get('entity_type.manager')
523       ->getStorage('entity_test')
524       ->getQuery()
525       ->exists("$field_name.color")
526       ->count()
527       ->execute();
528     $this->assertFalse($count);
529   }
530
531   /**
532    * Tests that nested condition groups work as expected.
533    */
534   public function testNestedConditionGroups() {
535     // Query for all entities of the first bundle that have either a red
536     // triangle as a figure or the Turkish greeting as a greeting.
537     $query = $this->storage->getQuery();
538
539     $first_and = $query->andConditionGroup()
540       ->condition($this->figures . '.color', 'red')
541       ->condition($this->figures . '.shape', 'triangle');
542     $second_and = $query->andConditionGroup()
543       ->condition($this->greetings . '.value', 'merhaba')
544       ->condition($this->greetings . '.format', 'format-tr');
545
546     $or = $query->orConditionGroup()
547       ->condition($first_and)
548       ->condition($second_and);
549
550     $this->queryResults = $query
551       ->condition($or)
552       ->condition('type', reset($this->bundles))
553       ->sort('id')
554       ->execute();
555
556     $this->assertResult(4, 6, 12, 14);
557   }
558
559   /**
560    * Tests that condition count returns expected number of conditions.
561    */
562   public function testConditionCount() {
563     // Query for all entities of the first bundle that
564     // have red as a colour AND are triangle shaped.
565     $query = $this->storage->getQuery();
566
567     // Add an AND condition group with 2 conditions in it.
568     $and_condition_group = $query->andConditionGroup()
569       ->condition($this->figures . '.color', 'red')
570       ->condition($this->figures . '.shape', 'triangle');
571
572     // We added 2 conditions so count should be 2.
573     $this->assertEqual($and_condition_group->count(), 2);
574
575     // Add an OR condition group with 2 conditions in it.
576     $or_condition_group = $query->orConditionGroup()
577       ->condition($this->figures . '.color', 'red')
578       ->condition($this->figures . '.shape', 'triangle');
579
580     // We added 2 conditions so count should be 2.
581     $this->assertEqual($or_condition_group->count(), 2);
582   }
583
584   /**
585    * Test queries with delta conditions.
586    */
587   public function testDelta() {
588     $figures = $this->figures;
589     // Test numeric delta value in field condition.
590     $this->queryResults = $this->storage
591       ->getQuery()
592       ->condition("$figures.0.color", 'red')
593       ->sort('id')
594       ->execute();
595     // As unit 0 at delta 0 was the red triangle bit 0 needs to be set.
596     $this->assertResult(1, 3, 5, 7, 9, 11, 13, 15);
597
598     $this->queryResults = $this->storage
599       ->getQuery()
600       ->condition("$figures.1.color", 'red')
601       ->sort('id')
602       ->execute();
603     // Delta 1 is not red.
604     $this->assertResult();
605
606     // Test on two different deltas.
607     $query = $this->storage->getQuery();
608     $or = $query->andConditionGroup()
609       ->condition("$figures.0.color", 'red')
610       ->condition("$figures.1.color", 'blue');
611     $this->queryResults = $query
612       ->condition($or)
613       ->sort('id')
614       ->execute();
615     $this->assertResult(3, 7, 11, 15);
616
617     // Test the delta range condition.
618     $this->queryResults = $this->storage
619       ->getQuery()
620       ->condition("$figures.%delta.color", ['blue', 'red'], 'IN')
621       ->condition("$figures.%delta", [0, 1], 'IN')
622       ->sort('id')
623       ->execute();
624     // Figure delta 0 or 1 can be blue or red, this matches a lot of entities.
625     $this->assertResult(1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15);
626
627     // Test the delta range condition without conditions on the value.
628     $this->queryResults = $this->storage
629       ->getQuery()
630       ->condition("$figures.%delta", 1)
631       ->sort('id')
632       ->execute();
633     // Entity needs to have at least two figures.
634     $this->assertResult(3, 7, 11, 15);
635
636     // Numeric delta on single value base field should return results only if
637     // the first item is being targeted.
638     $this->queryResults = $this->storage
639       ->getQuery()
640       ->condition("id.0.value", [1, 3, 5], 'IN')
641       ->sort('id')
642       ->execute();
643     $this->assertResult(1, 3, 5);
644     $this->queryResults = $this->storage
645       ->getQuery()
646       ->condition("id.1.value", [1, 3, 5], 'IN')
647       ->sort('id')
648       ->execute();
649     $this->assertResult();
650
651     // Delta range condition on single value base field should return results
652     // only if just the field value is targeted.
653     $this->queryResults = $this->storage
654       ->getQuery()
655       ->condition("id.%delta.value", [1, 3, 5], 'IN')
656       ->sort('id')
657       ->execute();
658     $this->assertResult(1, 3, 5);
659     $this->queryResults = $this->storage
660       ->getQuery()
661       ->condition("id.%delta.value", [1, 3, 5], 'IN')
662       ->condition("id.%delta", 0, '=')
663       ->sort('id')
664       ->execute();
665     $this->assertResult(1, 3, 5);
666     $this->queryResults = $this->storage
667       ->getQuery()
668       ->condition("id.%delta.value", [1, 3, 5], 'IN')
669       ->condition("id.%delta", 1, '=')
670       ->sort('id')
671       ->execute();
672     $this->assertResult();
673
674   }
675
676   protected function assertResult() {
677     $assert = [];
678     $expected = func_get_args();
679     if ($expected && is_array($expected[0])) {
680       $expected = $expected[0];
681     }
682     foreach ($expected as $binary) {
683       $assert[$binary] = strval($binary);
684     }
685     $this->assertIdentical($this->queryResults, $assert);
686   }
687
688   protected function assertRevisionResult($keys, $expected) {
689     $assert = [];
690     foreach ($expected as $key => $binary) {
691       $assert[$keys[$key]] = strval($binary);
692     }
693     $this->assertIdentical($this->queryResults, $assert);
694     return $assert;
695   }
696
697   protected function assertBundleOrder($order) {
698     // This loop is for bundle1 entities.
699     for ($i = 1; $i <= 15; $i += 2) {
700       $ok = TRUE;
701       $index1 = array_search($i, $this->queryResults);
702       $this->assertNotIdentical($index1, FALSE, "$i found at $index1.");
703       // This loop is for bundle2 entities.
704       for ($j = 2; $j <= 15; $j += 2) {
705         if ($ok) {
706           if ($order == 'asc') {
707             $ok = $index1 > array_search($j, $this->queryResults);
708           }
709           else {
710             $ok = $index1 < array_search($j, $this->queryResults);
711           }
712         }
713       }
714       $this->assertTrue($ok, "$i is after all entities in bundle2");
715     }
716   }
717
718   /**
719    * Test adding a tag and metadata to the Entity query object.
720    *
721    * The tags and metadata should propagate to the SQL query object.
722    */
723   public function testMetaData() {
724     $query = $this->storage->getQuery();
725     $query
726       ->addTag('efq_metadata_test')
727       ->addMetaData('foo', 'bar')
728       ->execute();
729
730     global $efq_test_metadata;
731     $this->assertEqual($efq_test_metadata, 'bar', 'Tag and metadata propagated to the SQL query object.');
732   }
733
734   /**
735    * Test case sensitive and in-sensitive query conditions.
736    */
737   public function testCaseSensitivity() {
738     $bundle = $this->randomMachineName();
739
740     $field_storage = FieldStorageConfig::create([
741       'field_name' => 'field_ci',
742       'entity_type' => 'entity_test_mulrev',
743       'type' => 'string',
744       'cardinality' => 1,
745       'translatable' => FALSE,
746       'settings' => [
747         'case_sensitive' => FALSE,
748       ],
749     ]);
750     $field_storage->save();
751
752     FieldConfig::create([
753       'field_storage' => $field_storage,
754       'bundle' => $bundle,
755     ])->save();
756
757     $field_storage = FieldStorageConfig::create([
758       'field_name' => 'field_cs',
759       'entity_type' => 'entity_test_mulrev',
760       'type' => 'string',
761       'cardinality' => 1,
762       'translatable' => FALSE,
763       'settings' => [
764         'case_sensitive' => TRUE,
765       ],
766     ]);
767     $field_storage->save();
768
769     FieldConfig::create([
770       'field_storage' => $field_storage,
771       'bundle' => $bundle,
772     ])->save();
773
774     $fixtures = [];
775
776     for ($i = 0; $i < 2; $i++) {
777       // If the last 4 of the string are all numbers, then there is no
778       // difference between upper and lowercase and the case sensitive CONTAINS
779       // test will fail. Ensure that can not happen by appending a non-numeric
780       // character. See https://www.drupal.org/node/2397297.
781       $string = $this->randomMachineName(7) . 'a';
782       $fixtures[] = [
783         'original' => $string,
784         'uppercase' => mb_strtoupper($string),
785         'lowercase' => mb_strtolower($string),
786       ];
787     }
788
789     EntityTestMulRev::create([
790       'type' => $bundle,
791       'name' => $this->randomMachineName(),
792       'langcode' => 'en',
793       'field_ci' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'],
794       'field_cs' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'],
795     ])->save();
796
797     // Check the case insensitive field, = operator.
798     $result = $this->storage
799       ->getQuery()
800       ->condition('field_ci', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase'])
801       ->execute();
802     $this->assertIdentical(count($result), 1, 'Case insensitive, lowercase');
803
804     $result = $this->storage
805       ->getQuery()
806       ->condition('field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase'])
807       ->execute();
808     $this->assertIdentical(count($result), 1, 'Case insensitive, uppercase');
809
810     $result = $this->storage
811       ->getQuery()
812       ->condition('field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'])
813       ->execute();
814     $this->assertIdentical(count($result), 1, 'Case insensitive, mixed.');
815
816     // Check the case sensitive field, = operator.
817     $result = $this->storage
818       ->getQuery()
819       ->condition('field_cs', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase'])
820       ->execute();
821     $this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.');
822
823     $result = $this->storage
824       ->getQuery()
825       ->condition('field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase'])
826       ->execute();
827     $this->assertIdentical(count($result), 0, 'Case sensitive, uppercase.');
828
829     $result = $this->storage
830       ->getQuery()
831       ->condition('field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'])
832       ->execute();
833     $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
834
835     // Check the case insensitive field, IN operator.
836     $result = $this->storage
837       ->getQuery()
838       ->condition('field_ci', [$fixtures[0]['lowercase'] . $fixtures[1]['lowercase']], 'IN')
839       ->execute();
840     $this->assertIdentical(count($result), 1, 'Case insensitive, lowercase');
841
842     $result = $this->storage
843       ->getQuery()
844       ->condition('field_ci', [$fixtures[0]['uppercase'] . $fixtures[1]['uppercase']], 'IN')->execute();
845     $this->assertIdentical(count($result), 1, 'Case insensitive, uppercase');
846
847     $result = $this->storage
848       ->getQuery()
849       ->condition('field_ci', [$fixtures[0]['uppercase'] . $fixtures[1]['lowercase']], 'IN')
850       ->execute();
851     $this->assertIdentical(count($result), 1, 'Case insensitive, mixed');
852
853     // Check the case sensitive field, IN operator.
854     $result = $this->storage
855       ->getQuery()
856       ->condition('field_cs', [$fixtures[0]['lowercase'] . $fixtures[1]['lowercase']], 'IN')
857       ->execute();
858     $this->assertIdentical(count($result), 0, 'Case sensitive, lowercase');
859
860     $result = $this->storage
861       ->getQuery()
862       ->condition('field_cs', [$fixtures[0]['uppercase'] . $fixtures[1]['uppercase']], 'IN')
863       ->execute();
864     $this->assertIdentical(count($result), 0, 'Case sensitive, uppercase');
865
866     $result = $this->storage
867       ->getQuery()
868       ->condition('field_cs', [$fixtures[0]['uppercase'] . $fixtures[1]['lowercase']], 'IN')
869       ->execute();
870     $this->assertIdentical(count($result), 1, 'Case sensitive, mixed');
871
872     // Check the case insensitive field, STARTS_WITH operator.
873     $result = $this->storage
874       ->getQuery()
875       ->condition('field_ci', $fixtures[0]['lowercase'], 'STARTS_WITH')
876       ->execute();
877     $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
878
879     $result = $this->storage
880       ->getQuery()
881       ->condition('field_ci', $fixtures[0]['uppercase'], 'STARTS_WITH')
882       ->execute();
883     $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
884
885     // Check the case sensitive field, STARTS_WITH operator.
886     $result = $this->storage
887       ->getQuery()
888       ->condition('field_cs', $fixtures[0]['lowercase'], 'STARTS_WITH')
889       ->execute();
890     $this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.');
891
892     $result = $this->storage
893       ->getQuery()
894       ->condition('field_cs', $fixtures[0]['uppercase'], 'STARTS_WITH')
895       ->execute();
896     $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
897
898     // Check the case insensitive field, ENDS_WITH operator.
899     $result = $this->storage
900       ->getQuery()
901       ->condition('field_ci', $fixtures[1]['lowercase'], 'ENDS_WITH')
902       ->execute();
903     $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
904
905     $result = $this->storage
906       ->getQuery()
907       ->condition('field_ci', $fixtures[1]['uppercase'], 'ENDS_WITH')
908       ->execute();
909     $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
910
911     // Check the case sensitive field, ENDS_WITH operator.
912     $result = $this->storage
913       ->getQuery()
914       ->condition('field_cs', $fixtures[1]['lowercase'], 'ENDS_WITH')
915       ->execute();
916     $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
917
918     $result = $this->storage
919       ->getQuery()
920       ->condition('field_cs', $fixtures[1]['uppercase'], 'ENDS_WITH')
921       ->execute();
922     $this->assertIdentical(count($result), 0, 'Case sensitive, exact match.');
923
924     // Check the case insensitive field, CONTAINS operator, use the inner 8
925     // characters of the uppercase and lowercase strings.
926     $result = $this->storage
927       ->getQuery()
928       ->condition('field_ci', mb_substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS')
929       ->execute();
930     $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
931
932     $result = $this->storage
933       ->getQuery()
934       ->condition('field_ci', mb_strtolower(mb_substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS')
935       ->execute();
936     $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
937
938     // Check the case sensitive field, CONTAINS operator.
939     $result = $this->storage
940       ->getQuery()
941       ->condition('field_cs', mb_substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS')
942       ->execute();
943     $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
944
945     $result = $this->storage
946       ->getQuery()
947       ->condition('field_cs', mb_strtolower(mb_substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS')
948       ->execute();
949     $this->assertIdentical(count($result), 0, 'Case sensitive, exact match.');
950
951   }
952
953   /**
954    * Test base fields with multiple columns.
955    */
956   public function testBaseFieldMultipleColumns() {
957     $this->enableModules(['taxonomy']);
958     $this->installEntitySchema('taxonomy_term');
959
960     Vocabulary::create(['vid' => 'tags']);
961
962     $term1 = Term::create([
963       'name' => $this->randomMachineName(),
964       'vid' => 'tags',
965       'description' => [
966         'value' => $this->randomString(),
967         'format' => 'format1',
968       ],
969     ]);
970     $term1->save();
971
972     $term2 = Term::create([
973       'name' => $this->randomMachineName(),
974       'vid' => 'tags',
975       'description' => [
976         'value' => $this->randomString(),
977         'format' => 'format2',
978       ],
979     ]);
980     $term2->save();
981
982     $ids = $this->container->get('entity_type.manager')
983       ->getStorage('taxonomy_term')
984       ->getQuery()
985       ->condition('description.format', 'format1')
986       ->execute();
987
988     $this->assertEqual(count($ids), 1);
989     $this->assertEqual($term1->id(), reset($ids));
990   }
991
992   /**
993    * Test pending revisions.
994    */
995   public function testPendingRevisions() {
996     // Ensure entity 14 is returned.
997     $result = $this->storage
998       ->getQuery()
999       ->condition('id', [14], 'IN')
1000       ->execute();
1001     $this->assertEqual(count($result), 1);
1002
1003     // Set a revision on entity 14 that isn't the current default.
1004     $entity = EntityTestMulRev::load(14);
1005     $current_values = $entity->{$this->figures}->getValue();
1006
1007     $entity->setNewRevision(TRUE);
1008     $entity->isDefaultRevision(FALSE);
1009     $entity->{$this->figures}->setValue([
1010       'color' => 'red',
1011       'shape' => 'square',
1012     ]);
1013     $entity->save();
1014
1015     // Entity query should still return entity 14.
1016     $result = $this->storage
1017       ->getQuery()
1018       ->condition('id', [14], 'IN')
1019       ->execute();
1020     $this->assertEqual(count($result), 1);
1021
1022     // Verify that field conditions on the default and pending revision are
1023     // work as expected.
1024     $result = $this->storage
1025       ->getQuery()
1026       ->condition('id', [14], 'IN')
1027       ->condition("$this->figures.color", $current_values[0]['color'])
1028       ->execute();
1029     $this->assertEqual($result, [14 => '14']);
1030     $result = $this->storage
1031       ->getQuery()
1032       ->condition('id', [14], 'IN')
1033       ->condition("$this->figures.color", 'red')
1034       ->allRevisions()
1035       ->execute();
1036     $this->assertEqual($result, [16 => '14']);
1037
1038     // Add another pending revision on the same entity and repeat the checks.
1039     $entity->setNewRevision(TRUE);
1040     $entity->isDefaultRevision(FALSE);
1041     $entity->{$this->figures}->setValue([
1042       'color' => 'red',
1043       'shape' => 'square',
1044     ]);
1045     $entity->save();
1046
1047     // A non-revisioned entity query should still return entity 14.
1048     $result = $this->storage
1049       ->getQuery()
1050       ->condition('id', [14], 'IN')
1051       ->execute();
1052     $this->assertCount(1, $result);
1053     $this->assertSame([14 => '14'], $result);
1054
1055     // Now check an entity query on the latest revision.
1056     $result = $this->storage
1057       ->getQuery()
1058       ->condition('id', [14], 'IN')
1059       ->latestRevision()
1060       ->execute();
1061     $this->assertCount(1, $result);
1062     $this->assertSame([17 => '14'], $result);
1063
1064     // Verify that field conditions on the default and pending revision still
1065     // work as expected.
1066     $result = $this->storage
1067       ->getQuery()
1068       ->condition('id', [14], 'IN')
1069       ->condition("$this->figures.color", $current_values[0]['color'])
1070       ->execute();
1071     $this->assertSame([14 => '14'], $result);
1072
1073     // Now there are two revisions with same value for the figure color.
1074     $result = $this->storage
1075       ->getQuery()
1076       ->condition('id', [14], 'IN')
1077       ->condition("$this->figures.color", 'red')
1078       ->allRevisions()
1079       ->execute();
1080     $this->assertSame([16 => '14', 17 => '14'], $result);
1081
1082     // Check that querying for the latest revision returns the correct one.
1083     $result = $this->storage
1084       ->getQuery()
1085       ->condition('id', [14], 'IN')
1086       ->condition("$this->figures.color", 'red')
1087       ->latestRevision()
1088       ->execute();
1089     $this->assertSame([17 => '14'], $result);
1090   }
1091
1092   /**
1093    * Test against SQL inject of condition field. This covers a
1094    * database driver's EntityQuery\Condition class.
1095    */
1096   public function testInjectionInCondition() {
1097     try {
1098       $this->queryResults = $this->storage
1099         ->getQuery()
1100         ->condition('1 ; -- ', [0, 1], 'IN')
1101         ->sort('id')
1102         ->execute();
1103       $this->fail('SQL Injection attempt in Entity Query condition in operator should result in an exception.');
1104     }
1105     catch (\Exception $e) {
1106       $this->pass('SQL Injection attempt in Entity Query condition in operator should result in an exception.');
1107     }
1108   }
1109
1110   /**
1111    * Tests that EntityQuery works when querying the same entity from two fields.
1112    */
1113   public function testWithTwoEntityReferenceFieldsToSameEntityType() {
1114     // Create two entity reference fields referring 'entity_test' entities.
1115     $this->createEntityReferenceField('entity_test', 'entity_test', 'ref1', $this->randomMachineName(), 'entity_test');
1116     $this->createEntityReferenceField('entity_test', 'entity_test', 'ref2', $this->randomMachineName(), 'entity_test');
1117
1118     $storage = $this->container->get('entity_type.manager')
1119       ->getStorage('entity_test');
1120
1121     // Create two entities to be referred.
1122     $ref1 = EntityTest::create(['type' => 'entity_test']);
1123     $ref1->save();
1124     $ref2 = EntityTest::create(['type' => 'entity_test']);
1125     $ref2->save();
1126
1127     // Create a main entity referring the previous created entities.
1128     $entity = EntityTest::create([
1129       'type' => 'entity_test',
1130       'ref1' => $ref1->id(),
1131       'ref2' => $ref2->id(),
1132     ]);
1133     $entity->save();
1134
1135     // Check that works when referring with "{$field_name}".
1136     $result = $storage->getQuery()
1137       ->condition('type', 'entity_test')
1138       ->condition('ref1', $ref1->id())
1139       ->condition('ref2', $ref2->id())
1140       ->execute();
1141     $this->assertCount(1, $result);
1142     $this->assertEquals($entity->id(), reset($result));
1143
1144     // Check that works when referring with "{$field_name}.target_id".
1145     $result = $storage->getQuery()
1146       ->condition('type', 'entity_test')
1147       ->condition('ref1.target_id', $ref1->id())
1148       ->condition('ref2.target_id', $ref2->id())
1149       ->execute();
1150     $this->assertCount(1, $result);
1151     $this->assertEquals($entity->id(), reset($result));
1152
1153     // Check that works when referring with "{$field_name}.entity.id".
1154     $result = $storage->getQuery()
1155       ->condition('type', 'entity_test')
1156       ->condition('ref1.entity.id', $ref1->id())
1157       ->condition('ref2.entity.id', $ref2->id())
1158       ->execute();
1159     $this->assertCount(1, $result);
1160     $this->assertEquals($entity->id(), reset($result));
1161   }
1162
1163 }