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