EntityQueryTest.php 41 KB

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