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