Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /**
|
Chris@0
|
4 * @file
|
Chris@0
|
5 * Provide views data that isn't tied to any other module.
|
Chris@0
|
6 */
|
Chris@0
|
7
|
Chris@0
|
8 use Drupal\Component\Utility\NestedArray;
|
Chris@0
|
9 use Drupal\Core\Entity\EntityStorageInterface;
|
Chris@0
|
10 use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
|
Chris@0
|
11 use Drupal\Core\Render\Markup;
|
Chris@0
|
12 use Drupal\field\Entity\FieldConfig;
|
Chris@0
|
13 use Drupal\field\FieldConfigInterface;
|
Chris@0
|
14 use Drupal\field\FieldStorageConfigInterface;
|
Chris@0
|
15 use Drupal\system\ActionConfigEntityInterface;
|
Chris@0
|
16
|
Chris@0
|
17 /**
|
Chris@0
|
18 * Implements hook_views_data().
|
Chris@0
|
19 */
|
Chris@0
|
20 function views_views_data() {
|
Chris@0
|
21 $data['views']['table']['group'] = t('Global');
|
Chris@0
|
22 $data['views']['table']['join'] = [
|
Chris@0
|
23 // #global is a special flag which allows a table to appear all the time.
|
Chris@0
|
24 '#global' => [],
|
Chris@0
|
25 ];
|
Chris@0
|
26
|
Chris@0
|
27 $data['views']['random'] = [
|
Chris@0
|
28 'title' => t('Random'),
|
Chris@0
|
29 'help' => t('Randomize the display order.'),
|
Chris@0
|
30 'sort' => [
|
Chris@0
|
31 'id' => 'random',
|
Chris@0
|
32 ],
|
Chris@0
|
33 ];
|
Chris@0
|
34
|
Chris@0
|
35 $data['views']['null'] = [
|
Chris@0
|
36 'title' => t('Null'),
|
Chris@0
|
37 'help' => t('Allow a contextual filter value to be ignored. The query will not be altered by this contextual filter value. Can be used when contextual filter values come from the URL, and a part of the URL needs to be ignored.'),
|
Chris@0
|
38 'argument' => [
|
Chris@0
|
39 'id' => 'null',
|
Chris@0
|
40 ],
|
Chris@0
|
41 ];
|
Chris@0
|
42
|
Chris@0
|
43 $data['views']['nothing'] = [
|
Chris@0
|
44 'title' => t('Custom text'),
|
Chris@0
|
45 'help' => t('Provide custom text or link.'),
|
Chris@0
|
46 'field' => [
|
Chris@0
|
47 'id' => 'custom',
|
Chris@0
|
48 ],
|
Chris@0
|
49 ];
|
Chris@0
|
50
|
Chris@0
|
51 $data['views']['counter'] = [
|
Chris@0
|
52 'title' => t('View result counter'),
|
Chris@0
|
53 'help' => t('Displays the actual position of the view result'),
|
Chris@0
|
54 'field' => [
|
Chris@0
|
55 'id' => 'counter',
|
Chris@0
|
56 ],
|
Chris@0
|
57 ];
|
Chris@0
|
58
|
Chris@0
|
59 $data['views']['area'] = [
|
Chris@0
|
60 'title' => t('Text area'),
|
Chris@0
|
61 'help' => t('Provide markup text for the area.'),
|
Chris@0
|
62 'area' => [
|
Chris@0
|
63 'id' => 'text',
|
Chris@0
|
64 ],
|
Chris@0
|
65 ];
|
Chris@0
|
66
|
Chris@0
|
67 $data['views']['area_text_custom'] = [
|
Chris@0
|
68 'title' => t('Unfiltered text'),
|
Chris@0
|
69 'help' => t('Add unrestricted, custom text or markup. This is similar to the custom text field.'),
|
Chris@0
|
70 'area' => [
|
Chris@0
|
71 'id' => 'text_custom',
|
Chris@0
|
72 ],
|
Chris@0
|
73 ];
|
Chris@0
|
74
|
Chris@0
|
75 $data['views']['title'] = [
|
Chris@0
|
76 'title' => t('Title override'),
|
Chris@0
|
77 'help' => t('Override the default view title for this view. This is useful to display an alternative title when a view is empty.'),
|
Chris@0
|
78 'area' => [
|
Chris@0
|
79 'id' => 'title',
|
Chris@0
|
80 'sub_type' => 'empty',
|
Chris@0
|
81 ],
|
Chris@0
|
82 ];
|
Chris@0
|
83
|
Chris@0
|
84 $data['views']['view'] = [
|
Chris@0
|
85 'title' => t('View area'),
|
Chris@0
|
86 'help' => t('Insert a view inside an area.'),
|
Chris@0
|
87 'area' => [
|
Chris@0
|
88 'id' => 'view',
|
Chris@0
|
89 ],
|
Chris@0
|
90 ];
|
Chris@0
|
91
|
Chris@0
|
92 $data['views']['result'] = [
|
Chris@0
|
93 'title' => t('Result summary'),
|
Chris@0
|
94 'help' => t('Shows result summary, for example the items per page.'),
|
Chris@0
|
95 'area' => [
|
Chris@0
|
96 'id' => 'result',
|
Chris@0
|
97 ],
|
Chris@0
|
98 ];
|
Chris@0
|
99
|
Chris@0
|
100 $data['views']['messages'] = [
|
Chris@0
|
101 'title' => t('Messages'),
|
Chris@0
|
102 'help' => t('Displays messages in an area.'),
|
Chris@0
|
103 'area' => [
|
Chris@0
|
104 'id' => 'messages',
|
Chris@0
|
105 ],
|
Chris@0
|
106 ];
|
Chris@0
|
107
|
Chris@0
|
108 $data['views']['http_status_code'] = [
|
Chris@0
|
109 'title' => t('Response status code'),
|
Chris@0
|
110 'help' => t('Alter the HTTP response status code used by this view, mostly helpful for empty results.'),
|
Chris@0
|
111 'area' => [
|
Chris@0
|
112 'id' => 'http_status_code',
|
Chris@0
|
113 ],
|
Chris@0
|
114 ];
|
Chris@0
|
115
|
Chris@0
|
116 $data['views']['combine'] = [
|
Chris@0
|
117 'title' => t('Combine fields filter'),
|
Chris@0
|
118 'help' => t('Combine multiple fields together and search by them.'),
|
Chris@0
|
119 'filter' => [
|
Chris@0
|
120 'id' => 'combine',
|
Chris@0
|
121 ],
|
Chris@0
|
122 ];
|
Chris@0
|
123
|
Chris@0
|
124 $data['views']['dropbutton'] = [
|
Chris@0
|
125 'title' => t('Dropbutton'),
|
Chris@0
|
126 'help' => t('Display fields in a dropbutton.'),
|
Chris@0
|
127 'field' => [
|
Chris@0
|
128 'id' => 'dropbutton',
|
Chris@0
|
129 ],
|
Chris@0
|
130 ];
|
Chris@0
|
131
|
Chris@18
|
132 $data['views']['display_link'] = [
|
Chris@18
|
133 'title' => t('Link to display'),
|
Chris@18
|
134 'help' => t('Displays a link to a path-based display of this view while keeping the filter criteria, sort criteria, pager settings and contextual filters.'),
|
Chris@18
|
135 'area' => [
|
Chris@18
|
136 'id' => 'display_link',
|
Chris@18
|
137 ],
|
Chris@18
|
138 ];
|
Chris@18
|
139
|
Chris@0
|
140 // Registers an entity area handler per entity type.
|
Chris@0
|
141 foreach (\Drupal::entityManager()->getDefinitions() as $entity_type_id => $entity_type) {
|
Chris@0
|
142 // Excludes entity types, which cannot be rendered.
|
Chris@0
|
143 if ($entity_type->hasViewBuilderClass()) {
|
Chris@0
|
144 $label = $entity_type->getLabel();
|
Chris@0
|
145 $data['views']['entity_' . $entity_type_id] = [
|
Chris@0
|
146 'title' => t('Rendered entity - @label', ['@label' => $label]),
|
Chris@0
|
147 'help' => t('Displays a rendered @label entity in an area.', ['@label' => $label]),
|
Chris@0
|
148 'area' => [
|
Chris@0
|
149 'entity_type' => $entity_type_id,
|
Chris@0
|
150 'id' => 'entity',
|
Chris@0
|
151 ],
|
Chris@0
|
152 ];
|
Chris@0
|
153 }
|
Chris@0
|
154 }
|
Chris@0
|
155
|
Chris@0
|
156 // Registers an action bulk form per entity.
|
Chris@0
|
157 foreach (\Drupal::entityManager()->getDefinitions() as $entity_type => $entity_info) {
|
Chris@0
|
158 $actions = array_filter(\Drupal::entityManager()->getStorage('action')->loadMultiple(), function (ActionConfigEntityInterface $action) use ($entity_type) {
|
Chris@0
|
159 return $action->getType() == $entity_type;
|
Chris@0
|
160 });
|
Chris@0
|
161 if (empty($actions)) {
|
Chris@0
|
162 continue;
|
Chris@0
|
163 }
|
Chris@0
|
164 $data[$entity_info->getBaseTable()][$entity_type . '_bulk_form'] = [
|
Chris@0
|
165 'title' => t('Bulk update'),
|
Chris@0
|
166 'help' => t('Allows users to apply an action to one or more items.'),
|
Chris@0
|
167 'field' => [
|
Chris@0
|
168 'id' => 'bulk_form',
|
Chris@0
|
169 ],
|
Chris@0
|
170 ];
|
Chris@0
|
171 }
|
Chris@0
|
172
|
Chris@0
|
173 // Registers views data for the entity itself.
|
Chris@0
|
174 foreach (\Drupal::entityManager()->getDefinitions() as $entity_type_id => $entity_type) {
|
Chris@0
|
175 if ($entity_type->hasHandlerClass('views_data')) {
|
Chris@0
|
176 /** @var \Drupal\views\EntityViewsDataInterface $views_data */
|
Chris@0
|
177 $views_data = \Drupal::entityManager()->getHandler($entity_type_id, 'views_data');
|
Chris@0
|
178 $data = NestedArray::mergeDeep($data, $views_data->getViewsData());
|
Chris@0
|
179 }
|
Chris@0
|
180 }
|
Chris@0
|
181
|
Chris@0
|
182 // Field modules can implement hook_field_views_data() to override the default
|
Chris@0
|
183 // behavior for adding fields.
|
Chris@0
|
184 $module_handler = \Drupal::moduleHandler();
|
Chris@0
|
185
|
Chris@0
|
186 $entity_manager = \Drupal::entityManager();
|
Chris@0
|
187 if ($entity_manager->hasDefinition('field_storage_config')) {
|
Chris@0
|
188 /** @var \Drupal\field\FieldStorageConfigInterface $field_storage */
|
Chris@0
|
189 foreach ($entity_manager->getStorage('field_storage_config')->loadMultiple() as $field_storage) {
|
Chris@0
|
190 if (_views_field_get_entity_type_storage($field_storage)) {
|
Chris@0
|
191 $result = (array) $module_handler->invoke($field_storage->getTypeProvider(), 'field_views_data', [$field_storage]);
|
Chris@0
|
192 if (empty($result)) {
|
Chris@0
|
193 $result = views_field_default_views_data($field_storage);
|
Chris@0
|
194 }
|
Chris@0
|
195 $module_handler->alter('field_views_data', $result, $field_storage);
|
Chris@0
|
196
|
Chris@0
|
197 if (is_array($result)) {
|
Chris@0
|
198 $data = NestedArray::mergeDeep($result, $data);
|
Chris@0
|
199 }
|
Chris@0
|
200 }
|
Chris@0
|
201 }
|
Chris@0
|
202 }
|
Chris@0
|
203
|
Chris@0
|
204 return $data;
|
Chris@0
|
205 }
|
Chris@0
|
206
|
Chris@0
|
207 /**
|
Chris@0
|
208 * Implements hook_views_data_alter().
|
Chris@0
|
209 *
|
Chris@0
|
210 * Field modules can implement hook_field_views_data_views_data_alter() to
|
Chris@0
|
211 * alter the views data on a per field basis. This is weirdly named so as
|
Chris@0
|
212 * not to conflict with the \Drupal::moduleHandler()->alter('field_views_data')
|
Chris@0
|
213 * in views_views_data().
|
Chris@0
|
214 */
|
Chris@0
|
215 function views_views_data_alter(&$data) {
|
Chris@0
|
216 $entity_manager = \Drupal::entityManager();
|
Chris@0
|
217 if (!$entity_manager->hasDefinition('field_storage_config')) {
|
Chris@0
|
218 return;
|
Chris@0
|
219 }
|
Chris@0
|
220 /** @var \Drupal\field\FieldStorageConfigInterface $field_storage */
|
Chris@0
|
221 foreach ($entity_manager->getStorage('field_storage_config')->loadMultiple() as $field_storage) {
|
Chris@0
|
222 if (_views_field_get_entity_type_storage($field_storage)) {
|
Chris@0
|
223 $function = $field_storage->getTypeProvider() . '_field_views_data_views_data_alter';
|
Chris@0
|
224 if (function_exists($function)) {
|
Chris@0
|
225 $function($data, $field_storage);
|
Chris@0
|
226 }
|
Chris@0
|
227 }
|
Chris@0
|
228 }
|
Chris@0
|
229 }
|
Chris@0
|
230
|
Chris@0
|
231 /**
|
Chris@0
|
232 * Determines whether the entity type the field appears in is SQL based.
|
Chris@0
|
233 *
|
Chris@0
|
234 * @param \Drupal\field\FieldStorageConfigInterface $field_storage
|
Chris@0
|
235 * The field storage definition.
|
Chris@0
|
236 *
|
Chris@0
|
237 * @return \Drupal\Core\Entity\Sql\SqlContentEntityStorage
|
Chris@0
|
238 * Returns the entity type storage if supported.
|
Chris@0
|
239 */
|
Chris@0
|
240 function _views_field_get_entity_type_storage(FieldStorageConfigInterface $field_storage) {
|
Chris@0
|
241 $result = FALSE;
|
Chris@0
|
242 $entity_manager = \Drupal::entityManager();
|
Chris@0
|
243 if ($entity_manager->hasDefinition($field_storage->getTargetEntityTypeId())) {
|
Chris@0
|
244 $storage = $entity_manager->getStorage($field_storage->getTargetEntityTypeId());
|
Chris@0
|
245 $result = $storage instanceof SqlContentEntityStorage ? $storage : FALSE;
|
Chris@0
|
246 }
|
Chris@0
|
247 return $result;
|
Chris@0
|
248 }
|
Chris@0
|
249
|
Chris@0
|
250 /**
|
Chris@0
|
251 * Returns the label of a certain field.
|
Chris@0
|
252 *
|
Chris@0
|
253 * Therefore it looks up in all bundles to find the most used field.
|
Chris@0
|
254 */
|
Chris@0
|
255 function views_entity_field_label($entity_type, $field_name) {
|
Chris@0
|
256 $label_counter = [];
|
Chris@0
|
257 $all_labels = [];
|
Chris@0
|
258 // Count the amount of fields per label per field storage.
|
Chris@18
|
259 foreach (array_keys(\Drupal::service('entity_type.bundle.info')->getBundleInfo($entity_type)) as $bundle) {
|
Chris@0
|
260 $bundle_fields = array_filter(\Drupal::entityManager()->getFieldDefinitions($entity_type, $bundle), function ($field_definition) {
|
Chris@0
|
261 return $field_definition instanceof FieldConfigInterface;
|
Chris@0
|
262 });
|
Chris@0
|
263 if (isset($bundle_fields[$field_name])) {
|
Chris@0
|
264 $field = $bundle_fields[$field_name];
|
Chris@0
|
265 $label = $field->getLabel();
|
Chris@0
|
266 $label_counter[$label] = isset($label_counter[$label]) ? ++$label_counter[$label] : 1;
|
Chris@0
|
267 $all_labels[$label] = TRUE;
|
Chris@0
|
268 }
|
Chris@0
|
269 }
|
Chris@0
|
270 if (empty($label_counter)) {
|
Chris@0
|
271 return [$field_name, $all_labels];
|
Chris@0
|
272 }
|
Chris@0
|
273 // Sort the field labels by it most used label and return the most used one.
|
Chris@0
|
274 // If the counts are equal, sort by the label to ensure the result is
|
Chris@0
|
275 // deterministic.
|
Chris@0
|
276 uksort($label_counter, function ($a, $b) use ($label_counter) {
|
Chris@0
|
277 if ($label_counter[$a] === $label_counter[$b]) {
|
Chris@0
|
278 return strcmp($a, $b);
|
Chris@0
|
279 }
|
Chris@0
|
280 return $label_counter[$a] > $label_counter[$b] ? -1 : 1;
|
Chris@0
|
281 });
|
Chris@0
|
282 $label_counter = array_keys($label_counter);
|
Chris@0
|
283 return [$label_counter[0], $all_labels];
|
Chris@0
|
284 }
|
Chris@0
|
285
|
Chris@0
|
286 /**
|
Chris@0
|
287 * Default views data implementation for a field.
|
Chris@0
|
288 *
|
Chris@0
|
289 * @param \Drupal\field\FieldStorageConfigInterface $field_storage
|
Chris@0
|
290 * The field definition.
|
Chris@0
|
291 *
|
Chris@0
|
292 * @return array
|
Chris@0
|
293 * The default views data for the field.
|
Chris@0
|
294 */
|
Chris@0
|
295 function views_field_default_views_data(FieldStorageConfigInterface $field_storage) {
|
Chris@0
|
296 $data = [];
|
Chris@0
|
297
|
Chris@0
|
298 // Check the field type is available.
|
Chris@0
|
299 if (!\Drupal::service('plugin.manager.field.field_type')->hasDefinition($field_storage->getType())) {
|
Chris@0
|
300 return $data;
|
Chris@0
|
301 }
|
Chris@0
|
302 // Check the field storage has fields.
|
Chris@0
|
303 if (!$field_storage->getBundles()) {
|
Chris@0
|
304 return $data;
|
Chris@0
|
305 }
|
Chris@0
|
306
|
Chris@0
|
307 // Ignore custom storage too.
|
Chris@0
|
308 if ($field_storage->hasCustomStorage()) {
|
Chris@0
|
309 return $data;
|
Chris@0
|
310 }
|
Chris@0
|
311
|
Chris@0
|
312 // Check whether the entity type storage is supported.
|
Chris@0
|
313 $storage = _views_field_get_entity_type_storage($field_storage);
|
Chris@0
|
314 if (!$storage) {
|
Chris@0
|
315 return $data;
|
Chris@0
|
316 }
|
Chris@0
|
317
|
Chris@0
|
318 $field_name = $field_storage->getName();
|
Chris@0
|
319 $field_columns = $field_storage->getColumns();
|
Chris@0
|
320
|
Chris@0
|
321 // Grab information about the entity type tables.
|
Chris@0
|
322 // We need to join to both the base table and the data table, if available.
|
Chris@0
|
323 $entity_manager = \Drupal::entityManager();
|
Chris@0
|
324 $entity_type_id = $field_storage->getTargetEntityTypeId();
|
Chris@0
|
325 $entity_type = $entity_manager->getDefinition($entity_type_id);
|
Chris@0
|
326 if (!$base_table = $entity_type->getBaseTable()) {
|
Chris@0
|
327 // We cannot do anything if for some reason there is no base table.
|
Chris@0
|
328 return $data;
|
Chris@0
|
329 }
|
Chris@0
|
330 $entity_tables = [$base_table => $entity_type_id];
|
Chris@0
|
331 // Some entities may not have a data table.
|
Chris@0
|
332 $data_table = $entity_type->getDataTable();
|
Chris@0
|
333 if ($data_table) {
|
Chris@0
|
334 $entity_tables[$data_table] = $entity_type_id;
|
Chris@0
|
335 }
|
Chris@0
|
336 $entity_revision_table = $entity_type->getRevisionTable();
|
Chris@0
|
337 $supports_revisions = $entity_type->hasKey('revision') && $entity_revision_table;
|
Chris@0
|
338 if ($supports_revisions) {
|
Chris@0
|
339 $entity_tables[$entity_revision_table] = $entity_type_id;
|
Chris@0
|
340 $entity_revision_data_table = $entity_type->getRevisionDataTable();
|
Chris@0
|
341 if ($entity_revision_data_table) {
|
Chris@0
|
342 $entity_tables[$entity_revision_data_table] = $entity_type_id;
|
Chris@0
|
343 }
|
Chris@0
|
344 }
|
Chris@0
|
345
|
Chris@0
|
346 // Description of the field tables.
|
Chris@0
|
347 // @todo Generalize this code to make it work with any table layout. See
|
Chris@0
|
348 // https://www.drupal.org/node/2079019.
|
Chris@0
|
349 $table_mapping = $storage->getTableMapping();
|
Chris@0
|
350 $field_tables = [
|
Chris@0
|
351 EntityStorageInterface::FIELD_LOAD_CURRENT => [
|
Chris@0
|
352 'table' => $table_mapping->getDedicatedDataTableName($field_storage),
|
Chris@0
|
353 'alias' => "{$entity_type_id}__{$field_name}",
|
Chris@0
|
354 ],
|
Chris@0
|
355 ];
|
Chris@0
|
356 if ($supports_revisions) {
|
Chris@0
|
357 $field_tables[EntityStorageInterface::FIELD_LOAD_REVISION] = [
|
Chris@0
|
358 'table' => $table_mapping->getDedicatedRevisionTableName($field_storage),
|
Chris@0
|
359 'alias' => "{$entity_type_id}_revision__{$field_name}",
|
Chris@0
|
360 ];
|
Chris@0
|
361 }
|
Chris@0
|
362
|
Chris@0
|
363 // Determine if the fields are translatable.
|
Chris@0
|
364 $bundles_names = $field_storage->getBundles();
|
Chris@0
|
365 $translation_join_type = FALSE;
|
Chris@0
|
366 $fields = [];
|
Chris@0
|
367 $translatable_configs = [];
|
Chris@0
|
368 $untranslatable_configs = [];
|
Chris@0
|
369 $untranslatable_config_bundles = [];
|
Chris@0
|
370
|
Chris@0
|
371 foreach ($bundles_names as $bundle) {
|
Chris@0
|
372 $fields[$bundle] = FieldConfig::loadByName($entity_type->id(), $bundle, $field_name);
|
Chris@0
|
373 }
|
Chris@0
|
374 foreach ($fields as $bundle => $config_entity) {
|
Chris@0
|
375 if (!empty($config_entity)) {
|
Chris@0
|
376 if ($config_entity->isTranslatable()) {
|
Chris@0
|
377 $translatable_configs[$bundle] = $config_entity;
|
Chris@0
|
378 }
|
Chris@0
|
379 else {
|
Chris@0
|
380 $untranslatable_configs[$bundle] = $config_entity;
|
Chris@0
|
381 }
|
Chris@0
|
382 }
|
Chris@0
|
383 else {
|
Chris@0
|
384 // https://www.drupal.org/node/2451657#comment-11462881
|
Chris@0
|
385 \Drupal::logger('views')->error(
|
Chris@17
|
386 'A non-existent config entity name returned by FieldStorageConfigInterface::getBundles(): entity type: %entity_type, bundle: %bundle, field name: %field',
|
Chris@17
|
387 [
|
Chris@17
|
388 '%entity_type' => $entity_type->id(),
|
Chris@17
|
389 '%bundle' => $bundle,
|
Chris@17
|
390 '%field' => $field_name,
|
Chris@17
|
391 ]
|
Chris@17
|
392 );
|
Chris@0
|
393 }
|
Chris@0
|
394 }
|
Chris@0
|
395
|
Chris@0
|
396 // If the field is translatable on all the bundles, there will be a join on
|
Chris@0
|
397 // the langcode.
|
Chris@0
|
398 if (!empty($translatable_configs) && empty($untranslatable_configs)) {
|
Chris@0
|
399 $translation_join_type = 'language';
|
Chris@0
|
400 }
|
Chris@0
|
401 // If the field is translatable only on certain bundles, there will be a join
|
Chris@0
|
402 // on langcode OR bundle name.
|
Chris@0
|
403 elseif (!empty($translatable_configs) && !empty($untranslatable_configs)) {
|
Chris@0
|
404 foreach ($untranslatable_configs as $config) {
|
Chris@0
|
405 $untranslatable_config_bundles[] = $config->getTargetBundle();
|
Chris@0
|
406 }
|
Chris@0
|
407 $translation_join_type = 'language_bundle';
|
Chris@0
|
408 }
|
Chris@0
|
409
|
Chris@0
|
410 // Build the relationships between the field table and the entity tables.
|
Chris@0
|
411 $table_alias = $field_tables[EntityStorageInterface::FIELD_LOAD_CURRENT]['alias'];
|
Chris@0
|
412 if ($data_table) {
|
Chris@0
|
413 // Tell Views how to join to the base table, via the data table.
|
Chris@0
|
414 $data[$table_alias]['table']['join'][$data_table] = [
|
Chris@0
|
415 'table' => $table_mapping->getDedicatedDataTableName($field_storage),
|
Chris@0
|
416 'left_field' => $entity_type->getKey('id'),
|
Chris@0
|
417 'field' => 'entity_id',
|
Chris@0
|
418 'extra' => [
|
Chris@0
|
419 ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE],
|
Chris@0
|
420 ],
|
Chris@0
|
421 ];
|
Chris@0
|
422 }
|
Chris@0
|
423 else {
|
Chris@0
|
424 // If there is no data table, just join directly.
|
Chris@0
|
425 $data[$table_alias]['table']['join'][$base_table] = [
|
Chris@0
|
426 'table' => $table_mapping->getDedicatedDataTableName($field_storage),
|
Chris@0
|
427 'left_field' => $entity_type->getKey('id'),
|
Chris@0
|
428 'field' => 'entity_id',
|
Chris@0
|
429 'extra' => [
|
Chris@0
|
430 ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE],
|
Chris@0
|
431 ],
|
Chris@0
|
432 ];
|
Chris@0
|
433 }
|
Chris@0
|
434
|
Chris@0
|
435 if ($translation_join_type === 'language_bundle') {
|
Chris@0
|
436 $data[$table_alias]['table']['join'][$data_table]['join_id'] = 'field_or_language_join';
|
Chris@0
|
437 $data[$table_alias]['table']['join'][$data_table]['extra'][] = [
|
Chris@0
|
438 'left_field' => 'langcode',
|
Chris@0
|
439 'field' => 'langcode',
|
Chris@0
|
440 ];
|
Chris@0
|
441 $data[$table_alias]['table']['join'][$data_table]['extra'][] = [
|
Chris@0
|
442 'field' => 'bundle',
|
Chris@0
|
443 'value' => $untranslatable_config_bundles,
|
Chris@0
|
444 ];
|
Chris@0
|
445 }
|
Chris@0
|
446 elseif ($translation_join_type === 'language') {
|
Chris@0
|
447 $data[$table_alias]['table']['join'][$data_table]['extra'][] = [
|
Chris@0
|
448 'left_field' => 'langcode',
|
Chris@0
|
449 'field' => 'langcode',
|
Chris@0
|
450 ];
|
Chris@0
|
451 }
|
Chris@0
|
452
|
Chris@0
|
453 if ($supports_revisions) {
|
Chris@0
|
454 $table_alias = $field_tables[EntityStorageInterface::FIELD_LOAD_REVISION]['alias'];
|
Chris@0
|
455 if ($entity_revision_data_table) {
|
Chris@0
|
456 // Tell Views how to join to the revision table, via the data table.
|
Chris@0
|
457 $data[$table_alias]['table']['join'][$entity_revision_data_table] = [
|
Chris@0
|
458 'table' => $table_mapping->getDedicatedRevisionTableName($field_storage),
|
Chris@0
|
459 'left_field' => $entity_type->getKey('revision'),
|
Chris@0
|
460 'field' => 'revision_id',
|
Chris@0
|
461 'extra' => [
|
Chris@0
|
462 ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE],
|
Chris@0
|
463 ],
|
Chris@0
|
464 ];
|
Chris@0
|
465 }
|
Chris@0
|
466 else {
|
Chris@0
|
467 // If there is no data table, just join directly.
|
Chris@0
|
468 $data[$table_alias]['table']['join'][$entity_revision_table] = [
|
Chris@0
|
469 'table' => $table_mapping->getDedicatedRevisionTableName($field_storage),
|
Chris@0
|
470 'left_field' => $entity_type->getKey('revision'),
|
Chris@0
|
471 'field' => 'revision_id',
|
Chris@0
|
472 'extra' => [
|
Chris@0
|
473 ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE],
|
Chris@0
|
474 ],
|
Chris@0
|
475 ];
|
Chris@0
|
476 }
|
Chris@0
|
477 if ($translation_join_type === 'language_bundle') {
|
Chris@0
|
478 $data[$table_alias]['table']['join'][$entity_revision_data_table]['join_id'] = 'field_or_language_join';
|
Chris@0
|
479 $data[$table_alias]['table']['join'][$entity_revision_data_table]['extra'][] = [
|
Chris@0
|
480 'left_field' => 'langcode',
|
Chris@0
|
481 'field' => 'langcode',
|
Chris@0
|
482 ];
|
Chris@0
|
483 $data[$table_alias]['table']['join'][$entity_revision_data_table]['extra'][] = [
|
Chris@0
|
484 'value' => $untranslatable_config_bundles,
|
Chris@0
|
485 'field' => 'bundle',
|
Chris@0
|
486 ];
|
Chris@0
|
487 }
|
Chris@0
|
488 elseif ($translation_join_type === 'language') {
|
Chris@0
|
489 $data[$table_alias]['table']['join'][$entity_revision_data_table]['extra'][] = [
|
Chris@0
|
490 'left_field' => 'langcode',
|
Chris@0
|
491 'field' => 'langcode',
|
Chris@0
|
492 ];
|
Chris@0
|
493 }
|
Chris@0
|
494 }
|
Chris@0
|
495
|
Chris@0
|
496 $group_name = $entity_type->getLabel();
|
Chris@0
|
497 // Get the list of bundles the field appears in.
|
Chris@0
|
498 $bundles_names = $field_storage->getBundles();
|
Chris@0
|
499 // Build the list of additional fields to add to queries.
|
Chris@0
|
500 $add_fields = ['delta', 'langcode', 'bundle'];
|
Chris@0
|
501 foreach (array_keys($field_columns) as $column) {
|
Chris@0
|
502 $add_fields[] = $table_mapping->getFieldColumnName($field_storage, $column);
|
Chris@0
|
503 }
|
Chris@0
|
504 // Determine the label to use for the field. We don't have a label available
|
Chris@0
|
505 // at the field level, so we just go through all fields and take the one
|
Chris@0
|
506 // which is used the most frequently.
|
Chris@0
|
507 list($label, $all_labels) = views_entity_field_label($entity_type_id, $field_name);
|
Chris@0
|
508
|
Chris@0
|
509 // Expose data for the field as a whole.
|
Chris@0
|
510 foreach ($field_tables as $type => $table_info) {
|
Chris@0
|
511 $table = $table_info['table'];
|
Chris@0
|
512 $table_alias = $table_info['alias'];
|
Chris@0
|
513
|
Chris@0
|
514 if ($type == EntityStorageInterface::FIELD_LOAD_CURRENT) {
|
Chris@0
|
515 $group = $group_name;
|
Chris@0
|
516 $field_alias = $field_name;
|
Chris@0
|
517 }
|
Chris@0
|
518 else {
|
Chris@0
|
519 $group = t('@group (historical data)', ['@group' => $group_name]);
|
Chris@0
|
520 $field_alias = $field_name . '-revision_id';
|
Chris@0
|
521 }
|
Chris@0
|
522
|
Chris@0
|
523 $data[$table_alias][$field_alias] = [
|
Chris@0
|
524 'group' => $group,
|
Chris@0
|
525 'title' => $label,
|
Chris@0
|
526 'title short' => $label,
|
Chris@0
|
527 'help' => t('Appears in: @bundles.', ['@bundles' => implode(', ', $bundles_names)]),
|
Chris@0
|
528 ];
|
Chris@0
|
529
|
Chris@0
|
530 // Go through and create a list of aliases for all possible combinations of
|
Chris@0
|
531 // entity type + name.
|
Chris@0
|
532 $aliases = [];
|
Chris@0
|
533 $also_known = [];
|
Chris@0
|
534 foreach ($all_labels as $label_name => $true) {
|
Chris@0
|
535 if ($type == EntityStorageInterface::FIELD_LOAD_CURRENT) {
|
Chris@0
|
536 if ($label != $label_name) {
|
Chris@0
|
537 $aliases[] = [
|
Chris@0
|
538 'base' => $base_table,
|
Chris@0
|
539 'group' => $group_name,
|
Chris@0
|
540 'title' => $label_name,
|
Chris@0
|
541 'help' => t('This is an alias of @group: @field.', ['@group' => $group_name, '@field' => $label]),
|
Chris@0
|
542 ];
|
Chris@0
|
543 $also_known[] = t('@group: @field', ['@group' => $group_name, '@field' => $label_name]);
|
Chris@0
|
544 }
|
Chris@0
|
545 }
|
Chris@0
|
546 elseif ($supports_revisions && $label != $label_name) {
|
Chris@0
|
547 $aliases[] = [
|
Chris@0
|
548 'base' => $table,
|
Chris@0
|
549 'group' => t('@group (historical data)', ['@group' => $group_name]),
|
Chris@0
|
550 'title' => $label_name,
|
Chris@0
|
551 'help' => t('This is an alias of @group: @field.', ['@group' => $group_name, '@field' => $label]),
|
Chris@0
|
552 ];
|
Chris@0
|
553 $also_known[] = t('@group (historical data): @field', ['@group' => $group_name, '@field' => $label_name]);
|
Chris@0
|
554 }
|
Chris@0
|
555 }
|
Chris@0
|
556 if ($aliases) {
|
Chris@0
|
557 $data[$table_alias][$field_alias]['aliases'] = $aliases;
|
Chris@0
|
558 // The $also_known variable contains markup that is HTML escaped and that
|
Chris@0
|
559 // loses safeness when imploded. The help text is used in #description
|
Chris@0
|
560 // and therefore XSS admin filtered by default. Escaped HTML is not
|
Chris@0
|
561 // altered by XSS filtering, therefore it is safe to just concatenate the
|
Chris@0
|
562 // strings. Afterwards we mark the entire string as safe, so it won't be
|
Chris@0
|
563 // escaped, no matter where it is used.
|
Chris@0
|
564 // Considering the dual use of this help data (both as metadata and as
|
Chris@0
|
565 // help text), other patterns such as use of #markup would not be correct
|
Chris@0
|
566 // here.
|
Chris@0
|
567 $data[$table_alias][$field_alias]['help'] = Markup::create($data[$table_alias][$field_alias]['help'] . ' ' . t('Also known as:') . ' ' . implode(', ', $also_known));
|
Chris@0
|
568 }
|
Chris@0
|
569
|
Chris@0
|
570 $keys = array_keys($field_columns);
|
Chris@0
|
571 $real_field = reset($keys);
|
Chris@0
|
572 $data[$table_alias][$field_alias]['field'] = [
|
Chris@0
|
573 'table' => $table,
|
Chris@0
|
574 'id' => 'field',
|
Chris@0
|
575 'field_name' => $field_name,
|
Chris@0
|
576 'entity_type' => $entity_type_id,
|
Chris@0
|
577 // Provide a real field for group by.
|
Chris@0
|
578 'real field' => $field_alias . '_' . $real_field,
|
Chris@0
|
579 'additional fields' => $add_fields,
|
Chris@0
|
580 // Default the element type to div, let the UI change it if necessary.
|
Chris@0
|
581 'element type' => 'div',
|
Chris@0
|
582 'is revision' => $type == EntityStorageInterface::FIELD_LOAD_REVISION,
|
Chris@0
|
583 ];
|
Chris@0
|
584 }
|
Chris@0
|
585
|
Chris@0
|
586 // Expose data for each field property individually.
|
Chris@0
|
587 foreach ($field_columns as $column => $attributes) {
|
Chris@0
|
588 $allow_sort = TRUE;
|
Chris@0
|
589
|
Chris@0
|
590 // Identify likely filters and arguments for each column based on field type.
|
Chris@0
|
591 switch ($attributes['type']) {
|
Chris@0
|
592 case 'int':
|
Chris@0
|
593 case 'mediumint':
|
Chris@0
|
594 case 'tinyint':
|
Chris@0
|
595 case 'bigint':
|
Chris@0
|
596 case 'serial':
|
Chris@0
|
597 case 'numeric':
|
Chris@0
|
598 case 'float':
|
Chris@0
|
599 $filter = 'numeric';
|
Chris@0
|
600 $argument = 'numeric';
|
Chris@0
|
601 $sort = 'standard';
|
Chris@0
|
602 if ($field_storage->getType() == 'boolean') {
|
Chris@0
|
603 $filter = 'boolean';
|
Chris@0
|
604 }
|
Chris@0
|
605 break;
|
Chris@0
|
606 case 'blob':
|
Chris@18
|
607 // It does not make sense to sort by blob.
|
Chris@0
|
608 $allow_sort = FALSE;
|
Chris@0
|
609 default:
|
Chris@0
|
610 $filter = 'string';
|
Chris@0
|
611 $argument = 'string';
|
Chris@0
|
612 $sort = 'standard';
|
Chris@0
|
613 break;
|
Chris@0
|
614 }
|
Chris@0
|
615
|
Chris@0
|
616 if (count($field_columns) == 1 || $column == 'value') {
|
Chris@0
|
617 $title = t('@label (@name)', ['@label' => $label, '@name' => $field_name]);
|
Chris@0
|
618 $title_short = $label;
|
Chris@0
|
619 }
|
Chris@0
|
620 else {
|
Chris@0
|
621 $title = t('@label (@name:@column)', ['@label' => $label, '@name' => $field_name, '@column' => $column]);
|
Chris@0
|
622 $title_short = t('@label:@column', ['@label' => $label, '@column' => $column]);
|
Chris@0
|
623 }
|
Chris@0
|
624
|
Chris@0
|
625 // Expose data for the property.
|
Chris@0
|
626 foreach ($field_tables as $type => $table_info) {
|
Chris@0
|
627 $table = $table_info['table'];
|
Chris@0
|
628 $table_alias = $table_info['alias'];
|
Chris@0
|
629
|
Chris@0
|
630 if ($type == EntityStorageInterface::FIELD_LOAD_CURRENT) {
|
Chris@0
|
631 $group = $group_name;
|
Chris@0
|
632 }
|
Chris@0
|
633 else {
|
Chris@0
|
634 $group = t('@group (historical data)', ['@group' => $group_name]);
|
Chris@0
|
635 }
|
Chris@0
|
636 $column_real_name = $table_mapping->getFieldColumnName($field_storage, $column);
|
Chris@0
|
637
|
Chris@0
|
638 // Load all the fields from the table by default.
|
Chris@0
|
639 $additional_fields = $table_mapping->getAllColumns($table);
|
Chris@0
|
640
|
Chris@0
|
641 $data[$table_alias][$column_real_name] = [
|
Chris@0
|
642 'group' => $group,
|
Chris@0
|
643 'title' => $title,
|
Chris@0
|
644 'title short' => $title_short,
|
Chris@0
|
645 'help' => t('Appears in: @bundles.', ['@bundles' => implode(', ', $bundles_names)]),
|
Chris@0
|
646 ];
|
Chris@0
|
647
|
Chris@0
|
648 // Go through and create a list of aliases for all possible combinations of
|
Chris@0
|
649 // entity type + name.
|
Chris@0
|
650 $aliases = [];
|
Chris@0
|
651 $also_known = [];
|
Chris@0
|
652 foreach ($all_labels as $label_name => $true) {
|
Chris@0
|
653 if ($label != $label_name) {
|
Chris@0
|
654 if (count($field_columns) == 1 || $column == 'value') {
|
Chris@0
|
655 $alias_title = t('@label (@name)', ['@label' => $label_name, '@name' => $field_name]);
|
Chris@0
|
656 }
|
Chris@0
|
657 else {
|
Chris@0
|
658 $alias_title = t('@label (@name:@column)', ['@label' => $label_name, '@name' => $field_name, '@column' => $column]);
|
Chris@0
|
659 }
|
Chris@0
|
660 $aliases[] = [
|
Chris@0
|
661 'group' => $group_name,
|
Chris@0
|
662 'title' => $alias_title,
|
Chris@0
|
663 'help' => t('This is an alias of @group: @field.', ['@group' => $group_name, '@field' => $title]),
|
Chris@0
|
664 ];
|
Chris@0
|
665 $also_known[] = t('@group: @field', ['@group' => $group_name, '@field' => $title]);
|
Chris@0
|
666 }
|
Chris@0
|
667 }
|
Chris@0
|
668 if ($aliases) {
|
Chris@0
|
669 $data[$table_alias][$column_real_name]['aliases'] = $aliases;
|
Chris@0
|
670 // The $also_known variable contains markup that is HTML escaped and
|
Chris@0
|
671 // that loses safeness when imploded. The help text is used in
|
Chris@0
|
672 // #description and therefore XSS admin filtered by default. Escaped
|
Chris@0
|
673 // HTML is not altered by XSS filtering, therefore it is safe to just
|
Chris@0
|
674 // concatenate the strings. Afterwards we mark the entire string as
|
Chris@0
|
675 // safe, so it won't be escaped, no matter where it is used.
|
Chris@0
|
676 // Considering the dual use of this help data (both as metadata and as
|
Chris@0
|
677 // help text), other patterns such as use of #markup would not be
|
Chris@0
|
678 // correct here.
|
Chris@0
|
679 $data[$table_alias][$column_real_name]['help'] = Markup::create($data[$table_alias][$column_real_name]['help'] . ' ' . t('Also known as:') . ' ' . implode(', ', $also_known));
|
Chris@0
|
680 }
|
Chris@0
|
681
|
Chris@0
|
682 $data[$table_alias][$column_real_name]['argument'] = [
|
Chris@0
|
683 'field' => $column_real_name,
|
Chris@0
|
684 'table' => $table,
|
Chris@0
|
685 'id' => $argument,
|
Chris@0
|
686 'additional fields' => $additional_fields,
|
Chris@0
|
687 'field_name' => $field_name,
|
Chris@0
|
688 'entity_type' => $entity_type_id,
|
Chris@0
|
689 'empty field name' => t('- No value -'),
|
Chris@0
|
690 ];
|
Chris@0
|
691 $data[$table_alias][$column_real_name]['filter'] = [
|
Chris@0
|
692 'field' => $column_real_name,
|
Chris@0
|
693 'table' => $table,
|
Chris@0
|
694 'id' => $filter,
|
Chris@0
|
695 'additional fields' => $additional_fields,
|
Chris@0
|
696 'field_name' => $field_name,
|
Chris@0
|
697 'entity_type' => $entity_type_id,
|
Chris@0
|
698 'allow empty' => TRUE,
|
Chris@0
|
699 ];
|
Chris@0
|
700 if (!empty($allow_sort)) {
|
Chris@0
|
701 $data[$table_alias][$column_real_name]['sort'] = [
|
Chris@0
|
702 'field' => $column_real_name,
|
Chris@0
|
703 'table' => $table,
|
Chris@0
|
704 'id' => $sort,
|
Chris@0
|
705 'additional fields' => $additional_fields,
|
Chris@0
|
706 'field_name' => $field_name,
|
Chris@0
|
707 'entity_type' => $entity_type_id,
|
Chris@0
|
708 ];
|
Chris@0
|
709 }
|
Chris@0
|
710
|
Chris@0
|
711 // Set click sortable if there is a field definition.
|
Chris@0
|
712 if (isset($data[$table_alias][$field_name]['field'])) {
|
Chris@0
|
713 $data[$table_alias][$field_name]['field']['click sortable'] = $allow_sort;
|
Chris@0
|
714 }
|
Chris@0
|
715
|
Chris@0
|
716 // Expose additional delta column for multiple value fields.
|
Chris@0
|
717 if ($field_storage->isMultiple()) {
|
Chris@0
|
718 $title_delta = t('@label (@name:delta)', ['@label' => $label, '@name' => $field_name]);
|
Chris@0
|
719 $title_short_delta = t('@label:delta', ['@label' => $label]);
|
Chris@0
|
720
|
Chris@0
|
721 $data[$table_alias]['delta'] = [
|
Chris@0
|
722 'group' => $group,
|
Chris@0
|
723 'title' => $title_delta,
|
Chris@0
|
724 'title short' => $title_short_delta,
|
Chris@0
|
725 'help' => t('Delta - Appears in: @bundles.', ['@bundles' => implode(', ', $bundles_names)]),
|
Chris@0
|
726 ];
|
Chris@0
|
727 $data[$table_alias]['delta']['field'] = [
|
Chris@0
|
728 'id' => 'numeric',
|
Chris@0
|
729 ];
|
Chris@0
|
730 $data[$table_alias]['delta']['argument'] = [
|
Chris@0
|
731 'field' => 'delta',
|
Chris@0
|
732 'table' => $table,
|
Chris@0
|
733 'id' => 'numeric',
|
Chris@0
|
734 'additional fields' => $additional_fields,
|
Chris@0
|
735 'empty field name' => t('- No value -'),
|
Chris@0
|
736 'field_name' => $field_name,
|
Chris@0
|
737 'entity_type' => $entity_type_id,
|
Chris@0
|
738 ];
|
Chris@0
|
739 $data[$table_alias]['delta']['filter'] = [
|
Chris@0
|
740 'field' => 'delta',
|
Chris@0
|
741 'table' => $table,
|
Chris@0
|
742 'id' => 'numeric',
|
Chris@0
|
743 'additional fields' => $additional_fields,
|
Chris@0
|
744 'field_name' => $field_name,
|
Chris@0
|
745 'entity_type' => $entity_type_id,
|
Chris@0
|
746 'allow empty' => TRUE,
|
Chris@0
|
747 ];
|
Chris@0
|
748 $data[$table_alias]['delta']['sort'] = [
|
Chris@0
|
749 'field' => 'delta',
|
Chris@0
|
750 'table' => $table,
|
Chris@0
|
751 'id' => 'standard',
|
Chris@0
|
752 'additional fields' => $additional_fields,
|
Chris@0
|
753 'field_name' => $field_name,
|
Chris@0
|
754 'entity_type' => $entity_type_id,
|
Chris@0
|
755 ];
|
Chris@0
|
756 }
|
Chris@0
|
757 }
|
Chris@0
|
758 }
|
Chris@0
|
759
|
Chris@0
|
760 return $data;
|
Chris@0
|
761 }
|
Chris@0
|
762
|
Chris@0
|
763 /**
|
Chris@0
|
764 * Implements hook_field_views_data().
|
Chris@0
|
765 *
|
Chris@0
|
766 * The function implements the hook in behalf of 'core' because it adds a
|
Chris@0
|
767 * relationship and a reverse relationship to entity_reference field type, which
|
Chris@0
|
768 * is provided by core.
|
Chris@0
|
769 */
|
Chris@0
|
770 function core_field_views_data(FieldStorageConfigInterface $field_storage) {
|
Chris@0
|
771 $data = views_field_default_views_data($field_storage);
|
Chris@0
|
772
|
Chris@0
|
773 // The code below only deals with the Entity reference field type.
|
Chris@0
|
774 if ($field_storage->getType() != 'entity_reference') {
|
Chris@0
|
775 return $data;
|
Chris@0
|
776 }
|
Chris@0
|
777
|
Chris@0
|
778 $entity_manager = \Drupal::entityManager();
|
Chris@0
|
779 $entity_type_id = $field_storage->getTargetEntityTypeId();
|
Chris@0
|
780 /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
|
Chris@0
|
781 $table_mapping = $entity_manager->getStorage($entity_type_id)->getTableMapping();
|
Chris@0
|
782
|
Chris@0
|
783 foreach ($data as $table_name => $table_data) {
|
Chris@0
|
784 // Add a relationship to the target entity type.
|
Chris@0
|
785 $target_entity_type_id = $field_storage->getSetting('target_type');
|
Chris@0
|
786 $target_entity_type = $entity_manager->getDefinition($target_entity_type_id);
|
Chris@0
|
787 $entity_type_id = $field_storage->getTargetEntityTypeId();
|
Chris@0
|
788 $entity_type = $entity_manager->getDefinition($entity_type_id);
|
Chris@0
|
789 $target_base_table = $target_entity_type->getDataTable() ?: $target_entity_type->getBaseTable();
|
Chris@0
|
790 $field_name = $field_storage->getName();
|
Chris@0
|
791
|
Chris@0
|
792 // Provide a relationship for the entity type with the entity reference
|
Chris@0
|
793 // field.
|
Chris@0
|
794 $args = [
|
Chris@0
|
795 '@label' => $target_entity_type->getLabel(),
|
Chris@0
|
796 '@field_name' => $field_name,
|
Chris@0
|
797 ];
|
Chris@0
|
798 $data[$table_name][$field_name]['relationship'] = [
|
Chris@0
|
799 'title' => t('@label referenced from @field_name', $args),
|
Chris@0
|
800 'label' => t('@field_name: @label', $args),
|
Chris@0
|
801 'group' => $entity_type->getLabel(),
|
Chris@0
|
802 'help' => t('Appears in: @bundles.', ['@bundles' => implode(', ', $field_storage->getBundles())]),
|
Chris@0
|
803 'id' => 'standard',
|
Chris@0
|
804 'base' => $target_base_table,
|
Chris@0
|
805 'entity type' => $target_entity_type_id,
|
Chris@0
|
806 'base field' => $target_entity_type->getKey('id'),
|
Chris@0
|
807 'relationship field' => $field_name . '_target_id',
|
Chris@0
|
808 ];
|
Chris@0
|
809
|
Chris@0
|
810 // Provide a reverse relationship for the entity type that is referenced by
|
Chris@0
|
811 // the field.
|
Chris@0
|
812 $args['@entity'] = $entity_type->getLabel();
|
Chris@0
|
813 $args['@label'] = $target_entity_type->getLowercaseLabel();
|
Chris@0
|
814 $pseudo_field_name = 'reverse__' . $entity_type_id . '__' . $field_name;
|
Chris@0
|
815 $data[$target_base_table][$pseudo_field_name]['relationship'] = [
|
Chris@0
|
816 'title' => t('@entity using @field_name', $args),
|
Chris@0
|
817 'label' => t('@field_name', ['@field_name' => $field_name]),
|
Chris@0
|
818 'group' => $target_entity_type->getLabel(),
|
Chris@0
|
819 'help' => t('Relate each @entity with a @field_name set to the @label.', $args),
|
Chris@0
|
820 'id' => 'entity_reverse',
|
Chris@0
|
821 'base' => $entity_type->getDataTable() ?: $entity_type->getBaseTable(),
|
Chris@0
|
822 'entity_type' => $entity_type_id,
|
Chris@0
|
823 'base field' => $entity_type->getKey('id'),
|
Chris@0
|
824 'field_name' => $field_name,
|
Chris@0
|
825 'field table' => $table_mapping->getDedicatedDataTableName($field_storage),
|
Chris@0
|
826 'field field' => $field_name . '_target_id',
|
Chris@0
|
827 'join_extra' => [
|
Chris@0
|
828 [
|
Chris@0
|
829 'field' => 'deleted',
|
Chris@0
|
830 'value' => 0,
|
Chris@0
|
831 'numeric' => TRUE,
|
Chris@0
|
832 ],
|
Chris@0
|
833 ],
|
Chris@0
|
834 ];
|
Chris@0
|
835 }
|
Chris@0
|
836
|
Chris@0
|
837 return $data;
|
Chris@0
|
838 }
|