Chris@18
|
1 <?php
|
Chris@18
|
2
|
Chris@18
|
3 namespace Drupal\Tests\views\Kernel;
|
Chris@18
|
4
|
Chris@18
|
5 use Drupal\Component\Render\MarkupInterface;
|
Chris@18
|
6 use Drupal\field\Entity\FieldConfig;
|
Chris@18
|
7 use Drupal\field\Entity\FieldStorageConfig;
|
Chris@18
|
8 use Drupal\language\Entity\ConfigurableLanguage;
|
Chris@18
|
9 use Drupal\language\Entity\ContentLanguageSettings;
|
Chris@18
|
10 use Drupal\node\Entity\Node;
|
Chris@18
|
11 use Drupal\node\Entity\NodeType;
|
Chris@18
|
12 use Drupal\views\Views;
|
Chris@18
|
13
|
Chris@18
|
14 /**
|
Chris@18
|
15 * Tests the Field Views data.
|
Chris@18
|
16 *
|
Chris@18
|
17 * @group views
|
Chris@18
|
18 */
|
Chris@18
|
19 class FieldApiDataTest extends ViewsKernelTestBase {
|
Chris@18
|
20
|
Chris@18
|
21 /**
|
Chris@18
|
22 * {@inheritdoc}
|
Chris@18
|
23 */
|
Chris@18
|
24 public static $modules = [
|
Chris@18
|
25 'field',
|
Chris@18
|
26 'filter',
|
Chris@18
|
27 'language',
|
Chris@18
|
28 'node',
|
Chris@18
|
29 'user',
|
Chris@18
|
30 ];
|
Chris@18
|
31
|
Chris@18
|
32 /**
|
Chris@18
|
33 * {@inheritdoc}
|
Chris@18
|
34 */
|
Chris@18
|
35 public static $testViews = ['test_field_config_translation_filter'];
|
Chris@18
|
36
|
Chris@18
|
37 /**
|
Chris@18
|
38 * {@inheritdoc}
|
Chris@18
|
39 */
|
Chris@18
|
40 protected function setUp($import_test_views = TRUE) {
|
Chris@18
|
41 parent::setUp($import_test_views);
|
Chris@18
|
42 $this->installEntitySchema('user');
|
Chris@18
|
43 $this->installEntitySchema('node');
|
Chris@18
|
44 $this->installSchema('node', ['node_access']);
|
Chris@18
|
45 }
|
Chris@18
|
46
|
Chris@18
|
47 /**
|
Chris@18
|
48 * Unit testing the views data structure.
|
Chris@18
|
49 *
|
Chris@18
|
50 * We check data structure for both node and node revision tables.
|
Chris@18
|
51 */
|
Chris@18
|
52 public function testViewsData() {
|
Chris@18
|
53 $field_storage_string = FieldStorageConfig::create([
|
Chris@18
|
54 'field_name' => 'field_string',
|
Chris@18
|
55 'entity_type' => 'node',
|
Chris@18
|
56 'type' => 'string',
|
Chris@18
|
57 ]);
|
Chris@18
|
58 $field_storage_string->save();
|
Chris@18
|
59
|
Chris@18
|
60 $field_storage_string_long = FieldStorageConfig::create([
|
Chris@18
|
61 'field_name' => 'field_string_long',
|
Chris@18
|
62 'entity_type' => 'node',
|
Chris@18
|
63 'type' => 'string_long',
|
Chris@18
|
64 ]);
|
Chris@18
|
65 $field_storage_string_long->save();
|
Chris@18
|
66
|
Chris@18
|
67 NodeType::create(['type' => 'page'])->save();
|
Chris@18
|
68 NodeType::create(['type' => 'article'])->save();
|
Chris@18
|
69
|
Chris@18
|
70 // Attach the field to nodes.
|
Chris@18
|
71 FieldConfig::create([
|
Chris@18
|
72 'field_name' => 'field_string',
|
Chris@18
|
73 'entity_type' => 'node',
|
Chris@18
|
74 'bundle' => 'page',
|
Chris@18
|
75 'label' => 'GiraffeA" label',
|
Chris@18
|
76 ])->save();
|
Chris@18
|
77
|
Chris@18
|
78 // Attach the string_long field to the page node type.
|
Chris@18
|
79 FieldConfig::create([
|
Chris@18
|
80 'field_name' => 'field_string_long',
|
Chris@18
|
81 'entity_type' => 'node',
|
Chris@18
|
82 'bundle' => 'page',
|
Chris@18
|
83 'label' => 'string_long label',
|
Chris@18
|
84 ])->save();
|
Chris@18
|
85
|
Chris@18
|
86 // Attach the same field to a different bundle with a different label.
|
Chris@18
|
87 FieldConfig::create([
|
Chris@18
|
88 'field_name' => 'field_string',
|
Chris@18
|
89 'entity_type' => 'node',
|
Chris@18
|
90 'bundle' => 'article',
|
Chris@18
|
91 'label' => 'GiraffeB" label',
|
Chris@18
|
92 ])->save();
|
Chris@18
|
93
|
Chris@18
|
94 // Now create some example nodes/users for the view result.
|
Chris@18
|
95 for ($i = 0; $i < 5; $i++) {
|
Chris@18
|
96 $edit = [
|
Chris@18
|
97 'field_string' => [(['value' => $this->randomMachineName()])],
|
Chris@18
|
98 ];
|
Chris@18
|
99 $nodes[] = Node::create(['type' => 'page'] + $edit);
|
Chris@18
|
100 }
|
Chris@18
|
101
|
Chris@18
|
102 /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
|
Chris@18
|
103 $table_mapping = $this->container->get('entity_type.manager')
|
Chris@18
|
104 ->getStorage('node')
|
Chris@18
|
105 ->getTableMapping();
|
Chris@18
|
106
|
Chris@18
|
107 $current_table = $table_mapping->getDedicatedDataTableName($field_storage_string);
|
Chris@18
|
108 $revision_table = $table_mapping->getDedicatedRevisionTableName($field_storage_string);
|
Chris@18
|
109 $data = $this->getViewsData();
|
Chris@18
|
110
|
Chris@18
|
111 $this->assertArrayHasKey($current_table, $data);
|
Chris@18
|
112 $this->assertArrayHasKey($revision_table, $data);
|
Chris@18
|
113
|
Chris@18
|
114 // The node field should join against node_field_data.
|
Chris@18
|
115 $this->assertArrayHasKey('node_field_data', $data[$current_table]['table']['join']);
|
Chris@18
|
116 $this->assertArrayHasKey('node_field_revision', $data[$revision_table]['table']['join']);
|
Chris@18
|
117
|
Chris@18
|
118 $expected_join = [
|
Chris@18
|
119 'table' => $current_table,
|
Chris@18
|
120 'left_field' => 'nid',
|
Chris@18
|
121 'field' => 'entity_id',
|
Chris@18
|
122 'extra' => [
|
Chris@18
|
123 ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE],
|
Chris@18
|
124 ['left_field' => 'langcode', 'field' => 'langcode'],
|
Chris@18
|
125 ],
|
Chris@18
|
126 ];
|
Chris@18
|
127 $this->assertSame($expected_join, $data[$current_table]['table']['join']['node_field_data']);
|
Chris@18
|
128 $expected_join = [
|
Chris@18
|
129 'table' => $revision_table,
|
Chris@18
|
130 'left_field' => 'vid',
|
Chris@18
|
131 'field' => 'revision_id',
|
Chris@18
|
132 'extra' => [
|
Chris@18
|
133 ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE],
|
Chris@18
|
134 ['left_field' => 'langcode', 'field' => 'langcode'],
|
Chris@18
|
135 ],
|
Chris@18
|
136 ];
|
Chris@18
|
137 $this->assertSame($expected_join, $data[$revision_table]['table']['join']['node_field_revision']);
|
Chris@18
|
138
|
Chris@18
|
139 // Test click sortable for string field.
|
Chris@18
|
140 $this->assertTrue($data[$current_table][$field_storage_string->getName()]['field']['click sortable']);
|
Chris@18
|
141 // Click sort should only be on the primary field.
|
Chris@18
|
142 $this->assertTrue(empty($data[$revision_table][$field_storage_string->getName()]['field']['click sortable']));
|
Chris@18
|
143 // Test click sortable for long text field.
|
Chris@18
|
144 $data_long = $this->getViewsData('field_string_long');
|
Chris@18
|
145 $current_table_long = $table_mapping->getDedicatedDataTableName($field_storage_string_long);
|
Chris@18
|
146 $this->assertTrue($data_long[$current_table_long][$field_storage_string_long->getName()]['field']['click sortable']);
|
Chris@18
|
147
|
Chris@18
|
148 $this->assertInstanceOf(MarkupInterface::class, $data[$current_table][$field_storage_string->getName()]['help']);
|
Chris@18
|
149 $this->assertEquals('Appears in: page, article. Also known as: Content: GiraffeB" label', $data[$current_table][$field_storage_string->getName()]['help']);
|
Chris@18
|
150
|
Chris@18
|
151 $this->assertInstanceOf(MarkupInterface::class, $data[$current_table][$field_storage_string->getName() . '_value']['help']);
|
Chris@18
|
152 $this->assertEquals('Appears in: page, article. Also known as: Content: GiraffeA" label (field_string)', $data[$current_table][$field_storage_string->getName() . '_value']['help']);
|
Chris@18
|
153
|
Chris@18
|
154 // Since each label is only used once, views_entity_field_label() will
|
Chris@18
|
155 // return a label using alphabetical sorting.
|
Chris@18
|
156 $this->assertEquals('GiraffeA" label (field_string)', $data[$current_table][$field_storage_string->getName() . '_value']['title']);
|
Chris@18
|
157
|
Chris@18
|
158 // Attach the same field to a different bundle with a different label.
|
Chris@18
|
159 NodeType::create(['type' => 'news'])->save();
|
Chris@18
|
160 FieldConfig::create([
|
Chris@18
|
161 'field_name' => $field_storage_string->getName(),
|
Chris@18
|
162 'entity_type' => 'node',
|
Chris@18
|
163 'bundle' => 'news',
|
Chris@18
|
164 'label' => 'GiraffeB" label',
|
Chris@18
|
165 ])->save();
|
Chris@18
|
166 $this->container->get('views.views_data')->clear();
|
Chris@18
|
167 $data = $this->getViewsData();
|
Chris@18
|
168
|
Chris@18
|
169 // Now the 'GiraffeB" label' is used twice and therefore will be
|
Chris@18
|
170 // selected by views_entity_field_label().
|
Chris@18
|
171 $this->assertEquals('GiraffeB" label (field_string)', $data[$current_table][$field_storage_string->getName() . '_value']['title']);
|
Chris@18
|
172 $this->assertInstanceOf(MarkupInterface::class, $data[$current_table][$field_storage_string->getName()]['help']);
|
Chris@18
|
173 $this->assertEquals('Appears in: page, article, news. Also known as: Content: GiraffeA" label', $data[$current_table][$field_storage_string->getName()]['help']);
|
Chris@18
|
174 }
|
Chris@18
|
175
|
Chris@18
|
176 /**
|
Chris@18
|
177 * Gets the views data for the field created in setUp().
|
Chris@18
|
178 *
|
Chris@18
|
179 * @param string $field_storage_key
|
Chris@18
|
180 * (optional) The optional field name.
|
Chris@18
|
181 *
|
Chris@18
|
182 * @return array
|
Chris@18
|
183 * Views data.
|
Chris@18
|
184 */
|
Chris@18
|
185 protected function getViewsData($field_storage_key = 'field_string') {
|
Chris@18
|
186 $views_data = $this->container->get('views.views_data');
|
Chris@18
|
187 $data = [];
|
Chris@18
|
188
|
Chris@18
|
189 // Check the table and the joins of the first field. Attached to node only.
|
Chris@18
|
190 /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
|
Chris@18
|
191 $table_mapping = $this->container->get('entity_type.manager')->getStorage('node')->getTableMapping();
|
Chris@18
|
192 $field_storage = FieldStorageConfig::loadByName('node', $field_storage_key);
|
Chris@18
|
193 $current_table = $table_mapping->getDedicatedDataTableName($field_storage);
|
Chris@18
|
194 $revision_table = $table_mapping->getDedicatedRevisionTableName($field_storage);
|
Chris@18
|
195 $data[$current_table] = $views_data->get($current_table);
|
Chris@18
|
196 $data[$revision_table] = $views_data->get($revision_table);
|
Chris@18
|
197 return $data;
|
Chris@18
|
198 }
|
Chris@18
|
199
|
Chris@18
|
200 /**
|
Chris@18
|
201 * Tests filtering entries with different translatability.
|
Chris@18
|
202 */
|
Chris@18
|
203 public function testEntityFieldFilter() {
|
Chris@18
|
204 NodeType::create(['type' => 'bundle1'])->save();
|
Chris@18
|
205 NodeType::create(['type' => 'bundle2'])->save();
|
Chris@18
|
206
|
Chris@18
|
207 // Create some example content.
|
Chris@18
|
208 ConfigurableLanguage::create(['id' => 'es'])->save();
|
Chris@18
|
209 ConfigurableLanguage::create(['id' => 'fr'])->save();
|
Chris@18
|
210
|
Chris@18
|
211 ContentLanguageSettings::loadByEntityTypeBundle('node', 'bundle1')
|
Chris@18
|
212 ->setDefaultLangcode('es')
|
Chris@18
|
213 ->setLanguageAlterable(TRUE)
|
Chris@18
|
214 ->save();
|
Chris@18
|
215 ContentLanguageSettings::loadByEntityTypeBundle('node', 'bundle2')
|
Chris@18
|
216 ->setDefaultLangcode('es')
|
Chris@18
|
217 ->setLanguageAlterable(TRUE)
|
Chris@18
|
218 ->save();
|
Chris@18
|
219
|
Chris@18
|
220 $field_translation_map = [
|
Chris@18
|
221 1 => ['bundle1' => TRUE, 'bundle2' => TRUE],
|
Chris@18
|
222 2 => ['bundle1' => FALSE, 'bundle2' => FALSE],
|
Chris@18
|
223 3 => ['bundle1' => TRUE, 'bundle2' => FALSE],
|
Chris@18
|
224 ];
|
Chris@18
|
225
|
Chris@18
|
226 for ($i = 1; $i < 4; $i++) {
|
Chris@18
|
227 $field_name = "field_name_$i";
|
Chris@18
|
228 FieldStorageConfig::create([
|
Chris@18
|
229 'field_name' => $field_name,
|
Chris@18
|
230 'entity_type' => 'node',
|
Chris@18
|
231 'type' => 'string',
|
Chris@18
|
232 ])->save();
|
Chris@18
|
233
|
Chris@18
|
234 foreach (['bundle1', 'bundle2'] as $bundle) {
|
Chris@18
|
235 FieldConfig::create([
|
Chris@18
|
236 'field_name' => $field_name,
|
Chris@18
|
237 'entity_type' => 'node',
|
Chris@18
|
238 'bundle' => $bundle,
|
Chris@18
|
239 'translatable' => $field_translation_map[$i][$bundle],
|
Chris@18
|
240 ])->save();
|
Chris@18
|
241 }
|
Chris@18
|
242 }
|
Chris@18
|
243
|
Chris@18
|
244 $node1 = Node::create([
|
Chris@18
|
245 'title' => 'Test title bundle1',
|
Chris@18
|
246 'type' => 'bundle1',
|
Chris@18
|
247 'langcode' => 'es',
|
Chris@18
|
248 'field_name_1' => 'field name 1: es',
|
Chris@18
|
249 'field_name_2' => 'field name 2: es',
|
Chris@18
|
250 'field_name_3' => 'field name 3: es',
|
Chris@18
|
251 ]);
|
Chris@18
|
252 $node1->save();
|
Chris@18
|
253 /** @var \Drupal\node\NodeInterface $translation */
|
Chris@18
|
254 $node1->addTranslation('fr', [
|
Chris@18
|
255 'title' => $node1->title->value,
|
Chris@18
|
256 'field_name_1' => 'field name 1: fr',
|
Chris@18
|
257 'field_name_3' => 'field name 3: fr',
|
Chris@18
|
258 ])->save();
|
Chris@18
|
259
|
Chris@18
|
260 $node2 = Node::create([
|
Chris@18
|
261 'title' => 'Test title bundle2',
|
Chris@18
|
262 'type' => 'bundle2',
|
Chris@18
|
263 'langcode' => 'es',
|
Chris@18
|
264 'field_name_1' => 'field name 1: es',
|
Chris@18
|
265 'field_name_2' => 'field name 2: es',
|
Chris@18
|
266 'field_name_3' => 'field name 3: es',
|
Chris@18
|
267 ]);
|
Chris@18
|
268 $node2->save();
|
Chris@18
|
269
|
Chris@18
|
270 $node2->addTranslation('fr', [
|
Chris@18
|
271 'title' => $node2->title->value,
|
Chris@18
|
272 'field_name_1' => 'field name 1: fr',
|
Chris@18
|
273 ])->save();
|
Chris@18
|
274
|
Chris@18
|
275 $map = [
|
Chris@18
|
276 'nid' => 'nid',
|
Chris@18
|
277 'langcode' => 'langcode',
|
Chris@18
|
278 ];
|
Chris@18
|
279
|
Chris@18
|
280 $view = Views::getView('test_field_config_translation_filter');
|
Chris@18
|
281
|
Chris@18
|
282 // Filter by 'field name 1: es'.
|
Chris@18
|
283 $view->setDisplay('embed_1');
|
Chris@18
|
284 $this->executeView($view);
|
Chris@18
|
285 $expected = [
|
Chris@18
|
286 [
|
Chris@18
|
287 'nid' => $node1->id(),
|
Chris@18
|
288 'langcode' => 'es',
|
Chris@18
|
289 ],
|
Chris@18
|
290 [
|
Chris@18
|
291 'nid' => $node2->id(),
|
Chris@18
|
292 'langcode' => 'es',
|
Chris@18
|
293 ],
|
Chris@18
|
294 ];
|
Chris@18
|
295
|
Chris@18
|
296 $this->assertIdenticalResultset($view, $expected, $map);
|
Chris@18
|
297 $view->destroy();
|
Chris@18
|
298
|
Chris@18
|
299 // Filter by 'field name 1: fr'.
|
Chris@18
|
300 $view->setDisplay('embed_2');
|
Chris@18
|
301 $this->executeView($view);
|
Chris@18
|
302 $expected = [
|
Chris@18
|
303 [
|
Chris@18
|
304 'nid' => $node1->id(),
|
Chris@18
|
305 'langcode' => 'fr',
|
Chris@18
|
306 ],
|
Chris@18
|
307 [
|
Chris@18
|
308 'nid' => $node2->id(),
|
Chris@18
|
309 'langcode' => 'fr',
|
Chris@18
|
310 ],
|
Chris@18
|
311 ];
|
Chris@18
|
312
|
Chris@18
|
313 $this->assertIdenticalResultset($view, $expected, $map);
|
Chris@18
|
314 $view->destroy();
|
Chris@18
|
315
|
Chris@18
|
316 // Filter by 'field name 2: es'.
|
Chris@18
|
317 $view->setDisplay('embed_3');
|
Chris@18
|
318 $this->executeView($view);
|
Chris@18
|
319 $expected = [
|
Chris@18
|
320 [
|
Chris@18
|
321 'nid' => $node1->id(),
|
Chris@18
|
322 'langcode' => 'es',
|
Chris@18
|
323 ],
|
Chris@18
|
324 [
|
Chris@18
|
325 'nid' => $node1->id(),
|
Chris@18
|
326 'langcode' => 'fr',
|
Chris@18
|
327 ],
|
Chris@18
|
328 [
|
Chris@18
|
329 'nid' => $node2->id(),
|
Chris@18
|
330 'langcode' => 'es',
|
Chris@18
|
331 ],
|
Chris@18
|
332 [
|
Chris@18
|
333 'nid' => $node2->id(),
|
Chris@18
|
334 'langcode' => 'fr',
|
Chris@18
|
335 ],
|
Chris@18
|
336 ];
|
Chris@18
|
337
|
Chris@18
|
338 $this->assertIdenticalResultset($view, $expected, $map);
|
Chris@18
|
339 $view->destroy();
|
Chris@18
|
340
|
Chris@18
|
341 // Filter by 'field name 2: fr', which doesn't exist.
|
Chris@18
|
342 $view->setDisplay('embed_4');
|
Chris@18
|
343 $this->executeView($view);
|
Chris@18
|
344 $expected = [];
|
Chris@18
|
345
|
Chris@18
|
346 $this->assertIdenticalResultset($view, $expected, $map);
|
Chris@18
|
347 $view->destroy();
|
Chris@18
|
348
|
Chris@18
|
349 // Filter by 'field name 3: es'.
|
Chris@18
|
350 $view->setDisplay('embed_5');
|
Chris@18
|
351 $this->executeView($view);
|
Chris@18
|
352 $expected = [
|
Chris@18
|
353 [
|
Chris@18
|
354 'nid' => $node1->id(),
|
Chris@18
|
355 'langcode' => 'es',
|
Chris@18
|
356 ],
|
Chris@18
|
357 [
|
Chris@18
|
358 'nid' => $node2->id(),
|
Chris@18
|
359 'langcode' => 'es',
|
Chris@18
|
360 ],
|
Chris@18
|
361 // Why is this one returned?
|
Chris@18
|
362 [
|
Chris@18
|
363 'nid' => $node2->id(),
|
Chris@18
|
364 'langcode' => 'fr',
|
Chris@18
|
365 ],
|
Chris@18
|
366 ];
|
Chris@18
|
367
|
Chris@18
|
368 $this->assertIdenticalResultset($view, $expected, $map);
|
Chris@18
|
369 $view->destroy();
|
Chris@18
|
370
|
Chris@18
|
371 // Filter by 'field name 3: fr'.
|
Chris@18
|
372 $view->setDisplay('embed_6');
|
Chris@18
|
373 $this->executeView($view);
|
Chris@18
|
374 $expected = [
|
Chris@18
|
375 [
|
Chris@18
|
376 'nid' => $node1->id(),
|
Chris@18
|
377 'langcode' => 'fr',
|
Chris@18
|
378 ],
|
Chris@18
|
379 ];
|
Chris@18
|
380
|
Chris@18
|
381 $this->assertIdenticalResultset($view, $expected, $map);
|
Chris@18
|
382 $view->destroy();
|
Chris@18
|
383 }
|
Chris@18
|
384
|
Chris@18
|
385 }
|