1<?php
2
3namespace Drupal\Tests\field\Kernel\EntityReference\Views;
4
5use Drupal\entity_test\Entity\EntityTestMulChanged;
6use Drupal\field\Entity\FieldStorageConfig;
7use Drupal\entity_test\Entity\EntityTest;
8use Drupal\entity_test\Entity\EntityTestMul;
9use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
10use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
11use Drupal\views\Tests\ViewTestData;
12use Drupal\views\Views;
13
14/**
15 * Tests entity reference relationship data.
16 *
17 * @group entity_reference
18 *
19 * @see core_field_views_data()
20 */
21class EntityReferenceRelationshipTest extends ViewsKernelTestBase {
22
23  use EntityReferenceTestTrait;
24
25  /**
26   * Views used by this test.
27   *
28   * @var array
29   */
30  public static $testViews = [
31    'test_entity_reference_entity_test_view',
32    'test_entity_reference_entity_test_view_long',
33    'test_entity_reference_reverse_entity_test_view',
34    'test_entity_reference_entity_test_mul_view',
35    'test_entity_reference_reverse_entity_test_mul_view',
36    'test_entity_reference_group_by_empty_relationships',
37    ];
38
39  /**
40   * Modules to install.
41   *
42   * @var array
43   */
44  protected static $modules = [
45    'user',
46    'field',
47    'entity_test',
48    'views',
49    'entity_reference_test_views',
50  ];
51
52  /**
53   * The entity_test entities used by the test.
54   *
55   * @var array
56   */
57  protected $entities = [];
58
59  /**
60   * {@inheritdoc}
61   */
62  protected function setUp($import_test_views = TRUE): void {
63    parent::setUp();
64
65    $this->installEntitySchema('user');
66    $this->installEntitySchema('user_role');
67    $this->installEntitySchema('entity_test');
68    $this->installEntitySchema('entity_test_mul');
69    $this->installEntitySchema('entity_test_mul_changed');
70
71    // Create reference from entity_test to entity_test_mul.
72    $this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_data', 'field_test_data', 'entity_test_mul');
73
74    // Create reference from entity_test_mul to entity_test.
75    $this->createEntityReferenceField('entity_test_mul', 'entity_test_mul', 'field_data_test', 'field_data_test', 'entity_test');
76
77    // Create another field for testing with a long name. So its storage name
78    // will become hashed. Use entity_test_mul_changed, so the resulting field
79    // tables created will be greater than 48 chars long.
80    // @see \Drupal\Core\Entity\Sql\DefaultTableMapping::generateFieldTableName()
81    $this->createEntityReferenceField('entity_test_mul_changed', 'entity_test_mul_changed', 'field_test_data_with_a_long_name', 'field_test_data_with_a_long_name', 'entity_test');
82
83    // Create reference from entity_test_mul to entity_test cardinality: infinite.
84    $this->createEntityReferenceField('entity_test_mul', 'entity_test_mul', 'field_data_test_unlimited', 'field_data_test_unlimited', 'entity_test', 'default', [], FieldStorageConfig::CARDINALITY_UNLIMITED);
85
86    ViewTestData::createTestViews(static::class, ['entity_reference_test_views']);
87  }
88
89  /**
90   * Tests using the views relationship.
91   */
92  public function testNoDataTableRelationship() {
93
94    // Create some test entities which link each other.
95    $referenced_entity = EntityTestMul::create();
96    $referenced_entity->save();
97
98    $entity = EntityTest::create();
99    $entity->field_test_data->target_id = $referenced_entity->id();
100    $entity->save();
101    $this->assertEquals($referenced_entity->id(), $entity->field_test_data[0]->entity->id());
102    $this->entities[] = $entity;
103
104    $entity = EntityTest::create();
105    $entity->field_test_data->target_id = $referenced_entity->id();
106    $entity->save();
107    $this->assertEquals($referenced_entity->id(), $entity->field_test_data[0]->entity->id());
108    $this->entities[] = $entity;
109
110    Views::viewsData()->clear();
111
112    // Check the generated views data.
113    $views_data = Views::viewsData()->get('entity_test__field_test_data');
114    $this->assertEquals('standard', $views_data['field_test_data']['relationship']['id']);
115    $this->assertEquals('entity_test_mul_property_data', $views_data['field_test_data']['relationship']['base']);
116    $this->assertEquals('id', $views_data['field_test_data']['relationship']['base field']);
117    $this->assertEquals('field_test_data_target_id', $views_data['field_test_data']['relationship']['relationship field']);
118    $this->assertEquals('entity_test_mul', $views_data['field_test_data']['relationship']['entity type']);
119
120    // Check the backwards reference.
121    $views_data = Views::viewsData()->get('entity_test_mul_property_data');
122    $this->assertEquals('entity_reverse', $views_data['reverse__entity_test__field_test_data']['relationship']['id']);
123    $this->assertEquals('entity_test', $views_data['reverse__entity_test__field_test_data']['relationship']['base']);
124    $this->assertEquals('id', $views_data['reverse__entity_test__field_test_data']['relationship']['base field']);
125    $this->assertEquals('entity_test__field_test_data', $views_data['reverse__entity_test__field_test_data']['relationship']['field table']);
126    $this->assertEquals('field_test_data_target_id', $views_data['reverse__entity_test__field_test_data']['relationship']['field field']);
127    $this->assertEquals('field_test_data', $views_data['reverse__entity_test__field_test_data']['relationship']['field_name']);
128    $this->assertEquals('entity_test', $views_data['reverse__entity_test__field_test_data']['relationship']['entity_type']);
129    $this->assertEquals(['field' => 'deleted', 'value' => 0, 'numeric' => TRUE], $views_data['reverse__entity_test__field_test_data']['relationship']['join_extra'][0]);
130
131    // Check an actual test view.
132    $view = Views::getView('test_entity_reference_entity_test_view');
133    $this->executeView($view);
134    /** @var \Drupal\views\ResultRow $row */
135    foreach ($view->result as $index => $row) {
136      // Check that the actual ID of the entity is the expected one.
137      $this->assertEquals($this->entities[$index]->id(), $row->id);
138
139      // Also check that we have the correct result entity.
140      $this->assertEquals($this->entities[$index]->id(), $row->_entity->id());
141
142      // Test the forward relationship.
143      $this->assertEquals(1, $row->entity_test_mul_property_data_entity_test__field_test_data_i);
144
145      // Test that the correct relationship entity is on the row.
146      $this->assertEquals(1, $row->_relationship_entities['field_test_data']->id());
147      $this->assertEquals('entity_test_mul', $row->_relationship_entities['field_test_data']->bundle());
148    }
149
150    // Check the backwards reference view.
151    $view = Views::getView('test_entity_reference_reverse_entity_test_view');
152    $this->executeView($view);
153    /** @var \Drupal\views\ResultRow $row */
154    foreach ($view->result as $index => $row) {
155      $this->assertEquals(1, $row->id);
156      $this->assertEquals(1, $row->_entity->id());
157
158      // Test the backwards relationship.
159      $this->assertEquals($this->entities[$index]->id(), $row->field_test_data_entity_test_mul_property_data_id);
160
161      // Test that the correct relationship entity is on the row.
162      $this->assertEquals($this->entities[$index]->id(), $row->_relationship_entities['reverse__entity_test__field_test_data']->id());
163      $this->assertEquals('entity_test', $row->_relationship_entities['reverse__entity_test__field_test_data']->bundle());
164    }
165  }
166
167  /**
168   * Tests views data generated for relationship.
169   *
170   * @see entity_reference_field_views_data()
171   */
172  public function testDataTableRelationship() {
173
174    // Create some test entities which link each other.
175    $referenced_entity = EntityTest::create();
176    $referenced_entity->save();
177
178    $entity = EntityTestMul::create();
179    $entity->field_data_test->target_id = $referenced_entity->id();
180    $entity->save();
181    $this->assertEquals($referenced_entity->id(), $entity->field_data_test[0]->entity->id());
182    $this->entities[] = $entity;
183
184    $entity = EntityTestMul::create();
185    $entity->field_data_test->target_id = $referenced_entity->id();
186    $entity->save();
187    $this->assertEquals($referenced_entity->id(), $entity->field_data_test[0]->entity->id());
188    $this->entities[] = $entity;
189
190    Views::viewsData()->clear();
191
192    // Check the generated views data.
193    $views_data = Views::viewsData()->get('entity_test_mul__field_data_test');
194    $this->assertEquals('standard', $views_data['field_data_test']['relationship']['id']);
195    $this->assertEquals('entity_test', $views_data['field_data_test']['relationship']['base']);
196    $this->assertEquals('id', $views_data['field_data_test']['relationship']['base field']);
197    $this->assertEquals('field_data_test_target_id', $views_data['field_data_test']['relationship']['relationship field']);
198    $this->assertEquals('entity_test', $views_data['field_data_test']['relationship']['entity type']);
199
200    // Check the backwards reference.
201    $views_data = Views::viewsData()->get('entity_test');
202    $this->assertEquals('entity_reverse', $views_data['reverse__entity_test_mul__field_data_test']['relationship']['id']);
203    $this->assertEquals('entity_test_mul_property_data', $views_data['reverse__entity_test_mul__field_data_test']['relationship']['base']);
204    $this->assertEquals('id', $views_data['reverse__entity_test_mul__field_data_test']['relationship']['base field']);
205    $this->assertEquals('entity_test_mul__field_data_test', $views_data['reverse__entity_test_mul__field_data_test']['relationship']['field table']);
206    $this->assertEquals('field_data_test_target_id', $views_data['reverse__entity_test_mul__field_data_test']['relationship']['field field']);
207    $this->assertEquals('field_data_test', $views_data['reverse__entity_test_mul__field_data_test']['relationship']['field_name']);
208    $this->assertEquals('entity_test_mul', $views_data['reverse__entity_test_mul__field_data_test']['relationship']['entity_type']);
209    $this->assertEquals(['field' => 'deleted', 'value' => 0, 'numeric' => TRUE], $views_data['reverse__entity_test_mul__field_data_test']['relationship']['join_extra'][0]);
210
211    // Check an actual test view.
212    $view = Views::getView('test_entity_reference_entity_test_mul_view');
213    $this->executeView($view);
214    /** @var \Drupal\views\ResultRow $row */
215    foreach ($view->result as $index => $row) {
216      // Check that the actual ID of the entity is the expected one.
217      $this->assertEquals($this->entities[$index]->id(), $row->id);
218
219      // Also check that we have the correct result entity.
220      $this->assertEquals($this->entities[$index]->id(), $row->_entity->id());
221
222      // Test the forward relationship.
223      $this->assertEquals(1, $row->entity_test_entity_test_mul__field_data_test_id);
224
225      // Test that the correct relationship entity is on the row.
226      $this->assertEquals(1, $row->_relationship_entities['field_data_test']->id());
227      $this->assertEquals('entity_test', $row->_relationship_entities['field_data_test']->bundle());
228
229    }
230
231    // Check the backwards reference view.
232    $view = Views::getView('test_entity_reference_reverse_entity_test_mul_view');
233    $this->executeView($view);
234    /** @var \Drupal\views\ResultRow $row */
235    foreach ($view->result as $index => $row) {
236      $this->assertEquals(1, $row->id);
237      $this->assertEquals(1, $row->_entity->id());
238
239      // Test the backwards relationship.
240      $this->assertEquals($this->entities[$index]->id(), $row->field_data_test_entity_test_id);
241
242      // Test that the correct relationship entity is on the row.
243      $this->assertEquals($this->entities[$index]->id(), $row->_relationship_entities['reverse__entity_test_mul__field_data_test']->id());
244      $this->assertEquals('entity_test_mul', $row->_relationship_entities['reverse__entity_test_mul__field_data_test']->bundle());
245    }
246  }
247
248  /**
249   * Tests views data generated for relationship.
250   *
251   * @see entity_reference_field_views_data()
252   */
253  public function testDataTableRelationshipWithLongFieldName() {
254    // Create some test entities which link each other.
255    $referenced_entity = EntityTest::create();
256    $referenced_entity->save();
257
258    $entity = EntityTestMulChanged::create();
259    $entity->field_test_data_with_a_long_name->target_id = $referenced_entity->id();
260    $entity->save();
261    $this->entities[] = $entity;
262
263    $entity = EntityTestMulChanged::create();
264    $entity->field_test_data_with_a_long_name->target_id = $referenced_entity->id();
265    $entity->save();
266    $this->entities[] = $entity;
267
268    Views::viewsData()->clear();
269
270    // Check an actual test view.
271    $view = Views::getView('test_entity_reference_entity_test_view_long');
272    $this->executeView($view);
273    /** @var \Drupal\views\ResultRow $row */
274    foreach ($view->result as $index => $row) {
275      // Check that the actual ID of the entity is the expected one.
276      $this->assertEquals($this->entities[$index]->id(), $row->id);
277
278      // Also check that we have the correct result entity.
279      $this->assertEquals($this->entities[$index]->id(), $row->_entity->id());
280
281      // Test the forward relationship.
282      // $this->assertEquals(1, $row->entity_test_entity_test_mul__field_data_test_id);
283
284      // Test that the correct relationship entity is on the row.
285      $this->assertEquals(1, $row->_relationship_entities['field_test_data_with_a_long_name']->id());
286      $this->assertEquals('entity_test', $row->_relationship_entities['field_test_data_with_a_long_name']->bundle());
287
288    }
289  }
290
291  /**
292   * Tests group by with optional and empty relationship.
293   */
294  public function testGroupByWithEmptyRelationships() {
295    $entities = [];
296    // Create 4 entities with name1 and 3 entities with name2.
297    for ($i = 1; $i <= 4; $i++) {
298      $entity = [
299        'name' => 'name' . $i,
300      ];
301      $entity = EntityTest::create($entity);
302      $entities[] = $entity;
303      $entity->save();
304    }
305
306    $entity = EntityTestMul::create([
307      'name' => 'name1',
308    ]);
309    $entity->field_data_test_unlimited = [
310      ['target_id' => $entities[0]->id()],
311      ['target_id' => $entities[1]->id()],
312      ['target_id' => $entities[2]->id()],
313    ];
314    $entity->save();
315
316    $entity = EntityTestMul::create([
317      'name' => 'name2',
318    ]);
319    $entity->field_data_test_unlimited = [
320      ['target_id' => $entities[0]->id()],
321      ['target_id' => $entities[1]->id()],
322    ];
323    $entity->save();
324
325    $entity = EntityTestMul::create([
326      'name' => 'name3',
327    ]);
328    $entity->field_data_test_unlimited->target_id = $entities[0]->id();
329    $entity->save();
330
331    $view = Views::getView('test_entity_reference_group_by_empty_relationships');
332    $this->executeView($view);
333    $this->assertCount(4, $view->result);
334    // First three results should contain a reference from EntityTestMul.
335    $this->assertNotEmpty($view->getStyle()->getField(0, 'name_2'));
336    $this->assertNotEmpty($view->getStyle()->getField(1, 'name_2'));
337    $this->assertNotEmpty($view->getStyle()->getField(2, 'name_2'));
338    // Fourth result has no reference from EntityTestMul hence the output for
339    // should be empty.
340    $this->assertEquals('', $view->getStyle()->getField(3, 'name_2'));
341
342    $fields = $view->field;
343    // Check getValue for reference with a value. The first 3 rows reference
344    // EntityTestMul, so have value 'name1'.
345    $this->assertEquals('name1', $fields['name_2']->getValue($view->result[0]));
346    $this->assertEquals('name1', $fields['name_2']->getValue($view->result[1]));
347    $this->assertEquals('name1', $fields['name_2']->getValue($view->result[2]));
348    // Ensure getValue works on empty references.
349    $this->assertNull($fields['name_2']->getValue($view->result[3]));
350  }
351
352  /**
353   * Test that config entities don't get relationships added.
354   */
355  public function testEntityReferenceConfigEntity() {
356    // Create reference from entity_test to a config entity.
357    $this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_config_entity', 'field_test_config_entity', 'user_role');
358    Views::viewsData()->clear();
359    $views_data = Views::viewsData()->getAll();
360    // Test that a relationship got added for content entities but not config
361    // entities.
362    $this->assertTrue(isset($views_data['entity_test__field_test_data']['field_test_data']['relationship']));
363    $this->assertFalse(isset($views_data['entity_test__field_test_config_entity']['field_test_config_entity']['relationship']));
364  }
365
366}
367