Further Drupal 8.6.4 changes. Some core files were not committed before a commit...
[yaffs-website] / web / core / modules / views / tests / src / Unit / Plugin / query / SqlTest.php
1 <?php
2
3 namespace Drupal\Tests\views\Unit\Plugin\query;
4
5 use Drupal\Core\Entity\EntityInterface;
6 use Drupal\Core\Entity\EntityStorageInterface;
7 use Drupal\Core\Entity\EntityType;
8 use Drupal\Core\Entity\EntityTypeManagerInterface;
9 use Drupal\Core\Messenger\MessengerInterface;
10 use Drupal\Tests\UnitTestCase;
11 use Drupal\views\Plugin\views\query\DateSqlInterface;
12 use Drupal\views\Plugin\views\query\Sql;
13 use Drupal\views\Plugin\views\relationship\RelationshipPluginBase;
14 use Drupal\views\ResultRow;
15 use Drupal\views\ViewEntityInterface;
16 use Drupal\views\ViewExecutable;
17 use Drupal\views\ViewsData;
18 use Symfony\Component\DependencyInjection\ContainerBuilder;
19
20 /**
21  * @coversDefaultClass \Drupal\views\Plugin\views\query\Sql
22  *
23  * @group views
24  */
25 class SqlTest extends UnitTestCase {
26
27   /**
28    * @covers ::getCacheTags
29    * @covers ::getAllEntities
30    */
31   public function testGetCacheTags() {
32     $view = $this->prophesize('Drupal\views\ViewExecutable')->reveal();
33     $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
34     $date_sql = $this->prophesize(DateSqlInterface::class);
35     $messenger = $this->prophesize(MessengerInterface::class);
36
37     $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal(), $messenger->reveal());
38     $query->view = $view;
39
40     $result = [];
41     $view->result = $result;
42
43     // Add a row with an entity.
44     $row = new ResultRow();
45     $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface');
46     $prophecy->getCacheTags()->willReturn(['entity_test:123']);
47     $entity = $prophecy->reveal();
48     $row->_entity = $entity;
49
50     $result[] = $row;
51     $view->result = $result;
52
53     // Add a row with an entity and a relationship entity.
54     $row = new ResultRow();
55     $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface');
56     $prophecy->getCacheTags()->willReturn(['entity_test:124']);
57     $entity = $prophecy->reveal();
58     $row->_entity = $entity;
59
60     $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface');
61     $prophecy->getCacheTags()->willReturn(['entity_test:125']);
62     $entity = $prophecy->reveal();
63     $row->_relationship_entities[] = $entity;
64     $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface');
65     $prophecy->getCacheTags()->willReturn(['entity_test:126']);
66     $entity = $prophecy->reveal();
67     $row->_relationship_entities[] = $entity;
68
69     $result[] = $row;
70     $view->result = $result;
71
72     $this->assertEquals(['entity_test:123', 'entity_test:124', 'entity_test:125', 'entity_test:126'], $query->getCacheTags());
73   }
74
75   /**
76    * @covers ::getCacheTags
77    * @covers ::getAllEntities
78    */
79   public function testGetCacheMaxAge() {
80     $view = $this->prophesize('Drupal\views\ViewExecutable')->reveal();
81     $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
82     $date_sql = $this->prophesize(DateSqlInterface::class);
83     $messenger = $this->prophesize(MessengerInterface::class);
84
85     $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal(), $messenger->reveal());
86     $query->view = $view;
87
88     $view->result = [];
89
90     // Add a row with an entity.
91     $row = new ResultRow();
92     $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface');
93     $prophecy->getCacheMaxAge()->willReturn(10);
94     $entity = $prophecy->reveal();
95
96     $row->_entity = $entity;
97     $view->result[] = $row;
98
99     // Add a row with an entity and a relationship entity.
100     $row = new ResultRow();
101     $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface');
102     $prophecy->getCacheMaxAge()->willReturn(20);
103     $entity = $prophecy->reveal();
104     $row->_entity = $entity;
105
106     $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface');
107     $prophecy->getCacheMaxAge()->willReturn(30);
108     $entity = $prophecy->reveal();
109     $row->_relationship_entities[] = $entity;
110     $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface');
111     $prophecy->getCacheMaxAge()->willReturn(40);
112     $entity = $prophecy->reveal();
113     $row->_relationship_entities[] = $entity;
114
115     $this->assertEquals(10, $query->getCacheMaxAge());
116   }
117
118   /**
119    * Sets up the views data in the container.
120    *
121    * @param \Drupal\views\ViewsData $views_data
122    *   The views data.
123    */
124   protected function setupViewsData(ViewsData $views_data) {
125     $container = \Drupal::hasContainer() ? \Drupal::getContainer() : new ContainerBuilder();
126     $container->set('views.views_data', $views_data);
127     \Drupal::setContainer($container);
128   }
129
130   /**
131    * Sets up the entity type manager in the container.
132    *
133    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
134    *   The entity type manager.
135    */
136   protected function setupEntityTypeManager(EntityTypeManagerInterface $entity_type_manager) {
137     $container = \Drupal::hasContainer() ? \Drupal::getContainer() : new ContainerBuilder();
138     $container->set('entity_type.manager', $entity_type_manager);
139     $container->set('entity.manager', $entity_type_manager);
140     \Drupal::setContainer($container);
141   }
142
143   /**
144    * Sets up some test entity types and corresponding views data.
145    *
146    * @param \Drupal\Core\Entity\EntityInterface[][] $entities_by_type
147    *   Test entities keyed by entity type and entity ID.
148    * @param \Drupal\Core\Entity\EntityInterface[][] $entity_revisions_by_type
149    *   Test entities keyed by entity type and revision ID.
150    *
151    * @return \Prophecy\Prophecy\ObjectProphecy
152    */
153   protected function setupEntityTypes($entities_by_type = [], $entity_revisions_by_type = []) {
154     $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
155     $entity_type0 = new EntityType([
156       'label' => 'First',
157       'id' => 'first',
158       'base_table' => 'entity_first',
159       'revision_table' => 'entity_first__revision',
160       'entity_keys' => [
161         'id' => 'id',
162         'revision' => 'vid',
163       ],
164     ]);
165     $entity_type1 = new EntityType([
166       'label' => 'second',
167       'id' => 'second',
168       'base_table' => 'entity_second',
169       'revision_table' => 'entity_second__revision',
170       'entity_keys' => [
171         'id' => 'id',
172         'revision' => 'vid',
173       ],
174     ]);
175
176     $entity_type_manager->getDefinitions()->willReturn([
177       'first' => $entity_type0,
178       'second' => $entity_type1,
179       'base_table' => 'entity_second',
180     ]);
181
182     $entity_type_manager->getDefinition('first')->willReturn($entity_type0);
183     $entity_type_manager->getDefinition('second')->willReturn($entity_type1);
184
185     // Setup the views data corresponding to the entity types.
186     $views_data = $this->prophesize(ViewsData::class);
187     $views_data->get('entity_first')->willReturn([
188       'table' => [
189         'entity type' => 'first',
190         'entity revision' => FALSE,
191       ],
192     ]);
193     $views_data->get('entity_first__revision')->willReturn([
194       'table' => [
195         'entity type' => 'first',
196         'entity revision' => TRUE,
197       ],
198     ]);
199     $views_data->get('entity_second')->willReturn([
200       'table' => [
201         'entity type' => 'second',
202         'entity revision' => FALSE,
203       ],
204     ]);
205     $views_data->get('entity_second__revision')->willReturn([
206       'table' => [
207         'entity type' => 'second',
208         'entity revision' => TRUE,
209       ],
210     ]);
211     $views_data->get('entity_first_field_data')->willReturn([
212       'table' => [
213         'entity type' => 'first',
214         'entity revision' => FALSE,
215       ],
216     ]);
217     $this->setupViewsData($views_data->reveal());
218
219     // Setup the loading of entities and entity revisions.
220     $entity_storages = [
221       'first' => $this->prophesize(EntityStorageInterface::class),
222       'second' => $this->prophesize(EntityStorageInterface::class),
223     ];
224
225     foreach ($entities_by_type as $entity_type_id => $entities) {
226       foreach ($entities as $entity_id => $entity) {
227         $entity_storages[$entity_type_id]->load($entity_id)->willReturn($entity);
228       }
229       $entity_storages[$entity_type_id]->loadMultiple(array_keys($entities))->willReturn($entities);
230     }
231
232     foreach ($entity_revisions_by_type as $entity_type_id => $entity_revisions) {
233       foreach ($entity_revisions as $revision_id => $revision) {
234         $entity_storages[$entity_type_id]->loadRevision($revision_id)->willReturn($revision);
235       }
236     }
237
238     $entity_type_manager->getStorage('first')->willReturn($entity_storages['first']);
239     $entity_type_manager->getStorage('second')->willReturn($entity_storages['second']);
240
241     $this->setupEntityTypeManager($entity_type_manager->reveal());
242
243     return $entity_type_manager;
244   }
245
246   /**
247    * @covers ::loadEntities
248    * @covers ::assignEntitiesToResult
249    */
250   public function testLoadEntitiesWithEmptyResult() {
251     $view = $this->prophesize('Drupal\views\ViewExecutable')->reveal();
252     $view_entity = $this->prophesize(ViewEntityInterface::class);
253     $view_entity->get('base_table')->willReturn('entity_first');
254     $view_entity->get('base_field')->willReturn('id');
255     $view->storage = $view_entity->reveal();
256
257     $entity_type_manager = $this->setupEntityTypes();
258     $date_sql = $this->prophesize(DateSqlInterface::class);
259     $messenger = $this->prophesize(MessengerInterface::class);
260
261     $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal(), $messenger->reveal());
262     $query->view = $view;
263
264     $result = [];
265     $query->addField('entity_first', 'id', 'id');
266     $query->loadEntities($result);
267     $this->assertEmpty($result);
268   }
269
270   /**
271    * @covers ::loadEntities
272    * @covers ::assignEntitiesToResult
273    */
274   public function testLoadEntitiesWithNoRelationshipAndNoRevision() {
275     $view = $this->prophesize('Drupal\views\ViewExecutable')->reveal();
276     $view_entity = $this->prophesize(ViewEntityInterface::class);
277     $view_entity->get('base_table')->willReturn('entity_first');
278     $view_entity->get('base_field')->willReturn('id');
279     $view->storage = $view_entity->reveal();
280
281     $entities = [
282       'first' => [
283         1 => $this->prophesize(EntityInterface::class)->reveal(),
284         2 => $this->prophesize(EntityInterface::class)->reveal(),
285       ],
286     ];
287     $entity_type_manager = $this->setupEntityTypes($entities);
288     $date_sql = $this->prophesize(DateSqlInterface::class);
289     $messenger = $this->prophesize(MessengerInterface::class);
290
291     $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal(), $messenger->reveal());
292     $query->view = $view;
293
294     $result = [];
295     $result[] = new ResultRow([
296       'id' => 1,
297     ]);
298     // Note: Let the same entity be returned multiple times, for example to
299     // support the translation usecase.
300     $result[] = new ResultRow([
301       'id' => 2,
302     ]);
303     $result[] = new ResultRow([
304       'id' => 2,
305     ]);
306
307     $query->addField('entity_first', 'id', 'id');
308     $query->loadEntities($result);
309
310     $this->assertSame($entities['first'][1], $result[0]->_entity);
311     $this->assertSame($entities['first'][2], $result[1]->_entity);
312     $this->assertSame($entities['first'][2], $result[2]->_entity);
313   }
314
315   /**
316    * Create a view with a relationship.
317    */
318   protected function setupViewWithRelationships(ViewExecutable $view, $base = 'entity_second') {
319     // We don't use prophecy, because prophecy enforces methods.
320     $relationship = $this->getMockBuilder(RelationshipPluginBase::class)->disableOriginalConstructor()->getMock();
321     $relationship->definition['base'] = $base;
322     $relationship->tableAlias = $base;
323     $relationship->alias = $base;
324
325     $view->relationship[$base] = $relationship;
326   }
327
328   /**
329    * @covers ::loadEntities
330    * @covers ::assignEntitiesToResult
331    */
332   public function testLoadEntitiesWithRelationship() {
333     // We don't use prophecy, because prophecy enforces methods.
334     $view = $this->getMockBuilder(ViewExecutable::class)->disableOriginalConstructor()->getMock();
335     $this->setupViewWithRelationships($view);
336
337     $view_entity = $this->prophesize(ViewEntityInterface::class);
338     $view_entity->get('base_table')->willReturn('entity_first');
339     $view_entity->get('base_field')->willReturn('id');
340     $view->storage = $view_entity->reveal();
341
342     $entities = [
343       'first' => [
344         1 => $this->prophesize(EntityInterface::class)->reveal(),
345         2 => $this->prophesize(EntityInterface::class)->reveal(),
346       ],
347       'second' => [
348         11 => $this->prophesize(EntityInterface::class)->reveal(),
349         12 => $this->prophesize(EntityInterface::class)->reveal(),
350       ],
351     ];
352     $entity_type_manager = $this->setupEntityTypes($entities);
353     $date_sql = $this->prophesize(DateSqlInterface::class);
354     $messenger = $this->prophesize(MessengerInterface::class);
355
356     $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal(), $messenger->reveal());
357     $query->view = $view;
358
359     $result = [];
360     $result[] = new ResultRow([
361       'id' => 1,
362       'entity_second__id' => 11,
363     ]);
364     // Provide an explicit NULL value, to test the case of a non required
365     // relationship.
366     $result[] = new ResultRow([
367       'id' => 2,
368       'entity_second__id' => NULL,
369     ]);
370     $result[] = new ResultRow([
371       'id' => 2,
372       'entity_second__id' => 12,
373     ]);
374
375     $query->addField('entity_first', 'id', 'id');
376     $query->addField('entity_second', 'id', 'entity_second__id');
377     $query->loadEntities($result);
378
379     $this->assertSame($entities['first'][1], $result[0]->_entity);
380     $this->assertSame($entities['first'][2], $result[1]->_entity);
381     $this->assertSame($entities['first'][2], $result[2]->_entity);
382
383     $this->assertSame($entities['second'][11], $result[0]->_relationship_entities['entity_second']);
384     $this->assertEquals([], $result[1]->_relationship_entities);
385     $this->assertSame($entities['second'][12], $result[2]->_relationship_entities['entity_second']);
386   }
387
388   /**
389    * @covers ::loadEntities
390    * @covers ::assignEntitiesToResult
391    */
392   public function testLoadEntitiesWithNonEntityRelationship() {
393     // We don't use prophecy, because prophecy enforces methods.
394     $view = $this->getMockBuilder(ViewExecutable::class)->disableOriginalConstructor()->getMock();
395     $this->setupViewWithRelationships($view, 'entity_first_field_data');
396
397     $view_entity = $this->prophesize(ViewEntityInterface::class);
398     $view_entity->get('base_table')->willReturn('entity_first');
399     $view_entity->get('base_field')->willReturn('id');
400     $view->storage = $view_entity->reveal();
401
402     $entities = [
403       'first' => [
404         1 => $this->prophesize(EntityInterface::class)->reveal(),
405         2 => $this->prophesize(EntityInterface::class)->reveal(),
406       ],
407     ];
408     $entity_type_manager = $this->setupEntityTypes($entities);
409     $date_sql = $this->prophesize(DateSqlInterface::class);
410     $messenger = $this->prophesize(MessengerInterface::class);
411
412     $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal(), $messenger->reveal());
413     $query->view = $view;
414
415     $result = [];
416     $result[] = new ResultRow([
417       'id' => 1,
418     ]);
419     $result[] = new ResultRow([
420       'id' => 2,
421     ]);
422
423     $query->addField('entity_first', 'id', 'id');
424     $query->loadEntities($result);
425     $entity_information = $query->getEntityTableInfo();
426
427     $this->assertSame($entities['first'][1], $result[0]->_entity);
428     $this->assertSame($entities['first'][2], $result[1]->_entity);
429
430     $this->assertEquals([], $result[0]->_relationship_entities);
431     $this->assertEquals([], $result[1]->_relationship_entities);
432
433     // This is an entity table and should be in $entity_information.
434     $this->assertContains('first', array_keys($entity_information));
435     // This is not an entity table and should not be in $entity_information.
436     $this->assertNotContains('entity_first_field_data__entity_first_field_data', array_keys($entity_information));
437   }
438
439   /**
440    * @covers ::loadEntities
441    * @covers ::assignEntitiesToResult
442    */
443   public function testLoadEntitiesWithRevision() {
444     // We don't use prophecy, because prophecy enforces methods.
445     $view = $this->getMockBuilder(ViewExecutable::class)
446       ->disableOriginalConstructor()
447       ->getMock();
448
449     $view_entity = $this->prophesize(ViewEntityInterface::class);
450     $view_entity->get('base_table')->willReturn('entity_first__revision');
451     $view_entity->get('base_field')->willReturn('vid');
452     $view->storage = $view_entity->reveal();
453
454     $entity_revisions = [
455       'first' => [
456         1 => $this->prophesize(EntityInterface::class)->reveal(),
457         3 => $this->prophesize(EntityInterface::class)->reveal(),
458       ],
459     ];
460     $entity_type_manager = $this->setupEntityTypes([], $entity_revisions);
461     $date_sql = $this->prophesize(DateSqlInterface::class);
462     $messenger = $this->prophesize(MessengerInterface::class);
463
464     $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal(), $messenger->reveal());
465     $query->view = $view;
466
467     $result = [];
468     $result[] = new ResultRow([
469       'vid' => 1,
470     ]);
471     $result[] = new ResultRow([
472       'vid' => 1,
473     ]);
474     $result[] = new ResultRow([
475       'vid' => 3,
476     ]);
477
478     $query->addField('entity_first__revision', 'vid', 'vid');
479     $query->loadEntities($result);
480
481     $this->assertSame($entity_revisions['first'][1], $result[0]->_entity);
482     $this->assertSame($entity_revisions['first'][1], $result[1]->_entity);
483     $this->assertSame($entity_revisions['first'][3], $result[2]->_entity);
484   }
485
486   /**
487    * @covers ::loadEntities
488    * @covers ::assignEntitiesToResult
489    */
490   public function testLoadEntitiesWithRevisionOfSameEntityType() {
491     // We don't use prophecy, because prophecy enforces methods.
492     $view = $this->getMockBuilder(ViewExecutable::class)
493       ->disableOriginalConstructor()
494       ->getMock();
495     $this->setupViewWithRelationships($view, 'entity_first__revision');
496
497     $view_entity = $this->prophesize(ViewEntityInterface::class);
498     $view_entity->get('base_table')->willReturn('entity_first');
499     $view_entity->get('base_field')->willReturn('id');
500     $view->storage = $view_entity->reveal();
501
502     $entity = [
503       'first' => [
504         1 => $this->prophesize(EntityInterface::class)->reveal(),
505         2 => $this->prophesize(EntityInterface::class)->reveal(),
506       ],
507     ];
508     $entity_revisions = [
509       'first' => [
510         1 => $this->prophesize(EntityInterface::class)->reveal(),
511         2 => $this->prophesize(EntityInterface::class)->reveal(),
512         3 => $this->prophesize(EntityInterface::class)->reveal(),
513       ],
514     ];
515     $entity_type_manager = $this->setupEntityTypes($entity, $entity_revisions);
516     $date_sql = $this->prophesize(DateSqlInterface::class);
517     $messenger = $this->prophesize(MessengerInterface::class);
518
519     $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal(), $messenger->reveal());
520     $query->view = $view;
521
522     $result = [];
523     $result[] = new ResultRow([
524       'id' => 1,
525       'entity_first__revision__vid' => 1,
526     ]);
527     $result[] = new ResultRow([
528       'id' => 2,
529       'entity_first__revision__vid' => 2,
530     ]);
531     $result[] = new ResultRow([
532       'id' => 2,
533       'entity_first__revision__vid' => 3,
534     ]);
535
536     $query->addField('entity_first', 'id', 'id');
537     $query->addField('entity_first__revision', 'vid', 'entity_first__revision__vid');
538     $query->loadEntities($result);
539
540     $this->assertSame($entity['first'][1], $result[0]->_entity);
541     $this->assertSame($entity['first'][2], $result[1]->_entity);
542     $this->assertSame($entity['first'][2], $result[2]->_entity);
543     $this->assertSame($entity_revisions['first'][1], $result[0]->_relationship_entities['entity_first__revision']);
544     $this->assertSame($entity_revisions['first'][2], $result[1]->_relationship_entities['entity_first__revision']);
545     $this->assertSame($entity_revisions['first'][3], $result[2]->_relationship_entities['entity_first__revision']);
546   }
547
548   /**
549    * @covers ::loadEntities
550    * @covers ::assignEntitiesToResult
551    */
552   public function testLoadEntitiesWithRelationshipAndRevision() {
553     // We don't use prophecy, because prophecy enforces methods.
554     $view = $this->getMockBuilder(ViewExecutable::class)->disableOriginalConstructor()->getMock();
555     $this->setupViewWithRelationships($view);
556
557     $view_entity = $this->prophesize(ViewEntityInterface::class);
558     $view_entity->get('base_table')->willReturn('entity_first__revision');
559     $view_entity->get('base_field')->willReturn('vid');
560     $view->storage = $view_entity->reveal();
561
562     $entities = [
563       'second' => [
564         11 => $this->prophesize(EntityInterface::class)->reveal(),
565         12 => $this->prophesize(EntityInterface::class)->reveal(),
566       ],
567     ];
568     $entity_revisions = [
569       'first' => [
570         1 => $this->prophesize(EntityInterface::class)->reveal(),
571         3 => $this->prophesize(EntityInterface::class)->reveal(),
572       ],
573     ];
574     $entity_type_manager = $this->setupEntityTypes($entities, $entity_revisions);
575     $date_sql = $this->prophesize(DateSqlInterface::class);
576     $messenger = $this->prophesize(MessengerInterface::class);
577
578     $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal(), $messenger->reveal());
579     $query->view = $view;
580
581     $result = [];
582     $result[] = new ResultRow([
583       'vid' => 1,
584       'entity_second__id' => 11,
585     ]);
586     // Provide an explicit NULL value, to test the case of a non required
587     // relationship.
588     $result[] = new ResultRow([
589       'vid' => 1,
590       'entity_second__id' => NULL,
591     ]);
592     $result[] = new ResultRow([
593       'vid' => 3,
594       'entity_second__id' => 12,
595     ]);
596
597     $query->addField('entity_first__revision', 'vid', 'vid');
598     $query->addField('entity_second', 'id', 'entity_second__id');
599     $query->loadEntities($result);
600
601     $this->assertSame($entity_revisions['first'][1], $result[0]->_entity);
602     $this->assertSame($entity_revisions['first'][1], $result[1]->_entity);
603     $this->assertSame($entity_revisions['first'][3], $result[2]->_entity);
604
605     $this->assertSame($entities['second'][11], $result[0]->_relationship_entities['entity_second']);
606     $this->assertEquals([], $result[1]->_relationship_entities);
607     $this->assertSame($entities['second'][12], $result[2]->_relationship_entities['entity_second']);
608   }
609
610 }