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