EntityQueryTest.php 37 KB

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