1<?php 2 3namespace Drupal\views; 4 5use Drupal\Core\Entity\ContentEntityType; 6use Drupal\Core\Entity\EntityFieldManagerInterface; 7use Drupal\Core\Entity\EntityHandlerInterface; 8use Drupal\Core\Entity\EntityTypeManagerInterface; 9use Drupal\Core\Entity\EntityTypeInterface; 10use Drupal\Core\Entity\Sql\SqlEntityStorageInterface; 11use Drupal\Core\Entity\Sql\TableMappingInterface; 12use Drupal\Core\Extension\ModuleHandlerInterface; 13use Drupal\Core\Field\FieldDefinitionInterface; 14use Drupal\Core\StringTranslation\StringTranslationTrait; 15use Drupal\Core\StringTranslation\TranslationInterface; 16use Symfony\Component\DependencyInjection\Container; 17use Symfony\Component\DependencyInjection\ContainerInterface; 18 19/** 20 * Provides generic views integration for entities. 21 */ 22class EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterface { 23 24 use StringTranslationTrait; 25 26 /** 27 * Entity type for this views data handler instance. 28 * 29 * @var \Drupal\Core\Entity\EntityTypeInterface 30 */ 31 protected $entityType; 32 33 /** 34 * The storage used for this entity type. 35 * 36 * @var \Drupal\Core\Entity\Sql\SqlEntityStorageInterface 37 */ 38 protected $storage; 39 40 /** 41 * The module handler. 42 * 43 * @var \Drupal\Core\Extension\ModuleHandlerInterface 44 */ 45 protected $moduleHandler; 46 47 /** 48 * The translation manager. 49 * 50 * @var \Drupal\Core\StringTranslation\TranslationInterface 51 */ 52 protected $translationManager; 53 54 /** 55 * The field storage definitions for all base fields of the entity type. 56 * 57 * @var \Drupal\Core\Field\FieldStorageDefinitionInterface[] 58 */ 59 protected $fieldStorageDefinitions; 60 61 /** 62 * The entity type manager. 63 * 64 * @var \Drupal\Core\Entity\EntityTypeManagerInterface 65 */ 66 protected $entityTypeManager; 67 68 /** 69 * The entity field manager. 70 * 71 * @var \Drupal\Core\Entity\EntityFieldManagerInterface 72 */ 73 protected $entityFieldManager; 74 75 /** 76 * Constructs an EntityViewsData object. 77 * 78 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type 79 * The entity type to provide views integration for. 80 * @param \Drupal\Core\Entity\Sql\SqlEntityStorageInterface $storage_controller 81 * The storage handler used for this entity type. 82 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager 83 * The entity type manager. 84 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler 85 * The module handler. 86 * @param \Drupal\Core\StringTranslation\TranslationInterface $translation_manager 87 * The translation manager. 88 * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager 89 * The entity field manager. 90 */ 91 public function __construct(EntityTypeInterface $entity_type, SqlEntityStorageInterface $storage_controller, EntityTypeManagerInterface $entity_type_manager, ModuleHandlerInterface $module_handler, TranslationInterface $translation_manager, EntityFieldManagerInterface $entity_field_manager) { 92 $this->entityType = $entity_type; 93 $this->entityTypeManager = $entity_type_manager; 94 $this->storage = $storage_controller; 95 $this->moduleHandler = $module_handler; 96 $this->setStringTranslation($translation_manager); 97 $this->entityFieldManager = $entity_field_manager; 98 } 99 100 /** 101 * {@inheritdoc} 102 */ 103 public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { 104 return new static( 105 $entity_type, 106 $container->get('entity_type.manager')->getStorage($entity_type->id()), 107 $container->get('entity_type.manager'), 108 $container->get('module_handler'), 109 $container->get('string_translation'), 110 $container->get('entity_field.manager') 111 ); 112 } 113 114 /** 115 * Gets the field storage definitions. 116 * 117 * @return \Drupal\Core\Field\FieldStorageDefinitionInterface[] 118 */ 119 protected function getFieldStorageDefinitions() { 120 if (!isset($this->fieldStorageDefinitions)) { 121 $this->fieldStorageDefinitions = $this->entityFieldManager->getFieldStorageDefinitions($this->entityType->id()); 122 } 123 return $this->fieldStorageDefinitions; 124 } 125 126 /** 127 * {@inheritdoc} 128 */ 129 public function getViewsData() { 130 $data = []; 131 132 $base_table = $this->entityType->getBaseTable() ?: $this->entityType->id(); 133 $views_revision_base_table = NULL; 134 $revisionable = $this->entityType->isRevisionable(); 135 $base_field = $this->entityType->getKey('id'); 136 137 $revision_table = ''; 138 if ($revisionable) { 139 $revision_table = $this->entityType->getRevisionTable() ?: $this->entityType->id() . '_revision'; 140 } 141 142 $translatable = $this->entityType->isTranslatable(); 143 $data_table = ''; 144 if ($translatable) { 145 $data_table = $this->entityType->getDataTable() ?: $this->entityType->id() . '_field_data'; 146 } 147 148 // Some entity types do not have a revision data table defined, but still 149 // have a revision table name set in 150 // \Drupal\Core\Entity\Sql\SqlContentEntityStorage::initTableLayout() so we 151 // apply the same kind of logic. 152 $revision_data_table = ''; 153 if ($revisionable && $translatable) { 154 $revision_data_table = $this->entityType->getRevisionDataTable() ?: $this->entityType->id() . '_field_revision'; 155 } 156 $revision_field = $this->entityType->getKey('revision'); 157 158 // Setup base information of the views data. 159 $data[$base_table]['table']['group'] = $this->entityType->getLabel(); 160 $data[$base_table]['table']['provider'] = $this->entityType->getProvider(); 161 162 $views_base_table = $base_table; 163 if ($data_table) { 164 $views_base_table = $data_table; 165 } 166 $data[$views_base_table]['table']['base'] = [ 167 'field' => $base_field, 168 'title' => $this->entityType->getLabel(), 169 'cache_contexts' => $this->entityType->getListCacheContexts(), 170 'access query tag' => $this->entityType->id() . '_access', 171 ]; 172 $data[$base_table]['table']['entity revision'] = FALSE; 173 174 if ($label_key = $this->entityType->getKey('label')) { 175 if ($data_table) { 176 $data[$views_base_table]['table']['base']['defaults'] = [ 177 'field' => $label_key, 178 'table' => $data_table, 179 ]; 180 } 181 else { 182 $data[$views_base_table]['table']['base']['defaults'] = [ 183 'field' => $label_key, 184 ]; 185 } 186 } 187 188 // Entity types must implement a list_builder in order to use Views' 189 // entity operations field. 190 if ($this->entityType->hasListBuilderClass()) { 191 $data[$base_table]['operations'] = [ 192 'field' => [ 193 'title' => $this->t('Operations links'), 194 'help' => $this->t('Provides links to perform entity operations.'), 195 'id' => 'entity_operations', 196 ], 197 ]; 198 if ($revision_table) { 199 $data[$revision_table]['operations'] = [ 200 'field' => [ 201 'title' => $this->t('Operations links'), 202 'help' => $this->t('Provides links to perform entity operations.'), 203 'id' => 'entity_operations', 204 ], 205 ]; 206 } 207 } 208 209 if ($this->entityType->hasViewBuilderClass()) { 210 $data[$base_table]['rendered_entity'] = [ 211 'field' => [ 212 'title' => $this->t('Rendered entity'), 213 'help' => $this->t('Renders an entity in a view mode.'), 214 'id' => 'rendered_entity', 215 ], 216 ]; 217 } 218 219 // Setup relations to the revisions/property data. 220 if ($data_table) { 221 $data[$base_table]['table']['join'][$data_table] = [ 222 'left_field' => $base_field, 223 'field' => $base_field, 224 'type' => 'INNER', 225 ]; 226 $data[$data_table]['table']['group'] = $this->entityType->getLabel(); 227 $data[$data_table]['table']['provider'] = $this->entityType->getProvider(); 228 $data[$data_table]['table']['entity revision'] = FALSE; 229 } 230 if ($revision_table) { 231 $data[$revision_table]['table']['group'] = $this->t('@entity_type revision', ['@entity_type' => $this->entityType->getLabel()]); 232 $data[$revision_table]['table']['provider'] = $this->entityType->getProvider(); 233 234 $views_revision_base_table = $revision_table; 235 if ($revision_data_table) { 236 $views_revision_base_table = $revision_data_table; 237 } 238 $data[$views_revision_base_table]['table']['entity revision'] = TRUE; 239 $data[$views_revision_base_table]['table']['base'] = [ 240 'field' => $revision_field, 241 'title' => $this->t('@entity_type revisions', ['@entity_type' => $this->entityType->getLabel()]), 242 ]; 243 // Join the revision table to the base table. 244 $data[$views_revision_base_table]['table']['join'][$views_base_table] = [ 245 'left_field' => $revision_field, 246 'field' => $revision_field, 247 'type' => 'INNER', 248 ]; 249 250 if ($revision_data_table) { 251 $data[$revision_data_table]['table']['group'] = $this->t('@entity_type revision', ['@entity_type' => $this->entityType->getLabel()]); 252 $data[$revision_data_table]['table']['entity revision'] = TRUE; 253 254 $data[$revision_table]['table']['join'][$revision_data_table] = [ 255 'left_field' => $revision_field, 256 'field' => $revision_field, 257 'type' => 'INNER', 258 ]; 259 } 260 261 // Add a filter for showing only the latest revisions of an entity. 262 $data[$revision_table]['latest_revision'] = [ 263 'title' => $this->t('Is Latest Revision'), 264 'help' => $this->t('Restrict the view to only revisions that are the latest revision of their entity.'), 265 'filter' => ['id' => 'latest_revision'], 266 ]; 267 if ($this->entityType->isTranslatable()) { 268 $data[$revision_table]['latest_translation_affected_revision'] = [ 269 'title' => $this->t('Is Latest Translation Affected Revision'), 270 'help' => $this->t('Restrict the view to only revisions that are the latest translation affected revision of their entity.'), 271 'filter' => ['id' => 'latest_translation_affected_revision'], 272 ]; 273 } 274 } 275 276 $this->addEntityLinks($data[$base_table]); 277 if ($views_revision_base_table) { 278 $this->addEntityLinks($data[$views_revision_base_table]); 279 } 280 281 // Load all typed data definitions of all fields. This should cover each of 282 // the entity base, revision, data tables. 283 $field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($this->entityType->id()); 284 /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ 285 if ($table_mapping = $this->storage->getTableMapping($field_definitions)) { 286 // Fetch all fields that can appear in both the base table and the data 287 // table. 288 $entity_keys = $this->entityType->getKeys(); 289 $duplicate_fields = array_intersect_key($entity_keys, array_flip(['id', 'revision', 'bundle'])); 290 // Iterate over each table we have so far and collect field data for each. 291 // Based on whether the field is in the field_definitions provided by the 292 // entity field manager. 293 // @todo We should better just rely on information coming from the entity 294 // storage. 295 // @todo https://www.drupal.org/node/2337511 296 foreach ($table_mapping->getTableNames() as $table) { 297 foreach ($table_mapping->getFieldNames($table) as $field_name) { 298 // To avoid confusing duplication in the user interface, for fields 299 // that are on both base and data tables, only add them on the data 300 // table (same for revision vs. revision data). 301 if ($data_table && ($table === $base_table || $table === $revision_table) && in_array($field_name, $duplicate_fields)) { 302 continue; 303 } 304 $this->mapFieldDefinition($table, $field_name, $field_definitions[$field_name], $table_mapping, $data[$table]); 305 } 306 } 307 308 foreach ($field_definitions as $field_definition) { 309 if ($table_mapping->requiresDedicatedTableStorage($field_definition->getFieldStorageDefinition())) { 310 $table = $table_mapping->getDedicatedDataTableName($field_definition->getFieldStorageDefinition()); 311 312 $data[$table]['table']['group'] = $this->entityType->getLabel(); 313 $data[$table]['table']['provider'] = $this->entityType->getProvider(); 314 $data[$table]['table']['join'][$views_base_table] = [ 315 'left_field' => $base_field, 316 'field' => 'entity_id', 317 'extra' => [ 318 ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE], 319 ], 320 ]; 321 322 if ($revisionable) { 323 $revision_table = $table_mapping->getDedicatedRevisionTableName($field_definition->getFieldStorageDefinition()); 324 325 $data[$revision_table]['table']['group'] = $this->t('@entity_type revision', ['@entity_type' => $this->entityType->getLabel()]); 326 $data[$revision_table]['table']['provider'] = $this->entityType->getProvider(); 327 $data[$revision_table]['table']['join'][$views_revision_base_table] = [ 328 'left_field' => $revision_field, 329 'field' => 'entity_id', 330 'extra' => [ 331 ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE], 332 ], 333 ]; 334 } 335 } 336 } 337 } 338 339 // Add the entity type key to each table generated. 340 $entity_type_id = $this->entityType->id(); 341 array_walk($data, function (&$table_data) use ($entity_type_id) { 342 $table_data['table']['entity type'] = $entity_type_id; 343 }); 344 345 return $data; 346 } 347 348 /** 349 * Sets the entity links in case corresponding link templates exist. 350 * 351 * @param array $data 352 * The views data of the base table. 353 */ 354 protected function addEntityLinks(array &$data) { 355 $entity_type_id = $this->entityType->id(); 356 $t_arguments = ['@entity_type_label' => $this->entityType->getLabel()]; 357 if ($this->entityType->hasLinkTemplate('canonical')) { 358 $data['view_' . $entity_type_id] = [ 359 'field' => [ 360 'title' => $this->t('Link to @entity_type_label', $t_arguments), 361 'help' => $this->t('Provide a view link to the @entity_type_label.', $t_arguments), 362 'id' => 'entity_link', 363 ], 364 ]; 365 } 366 if ($this->entityType->hasLinkTemplate('edit-form')) { 367 $data['edit_' . $entity_type_id] = [ 368 'field' => [ 369 'title' => $this->t('Link to edit @entity_type_label', $t_arguments), 370 'help' => $this->t('Provide an edit link to the @entity_type_label.', $t_arguments), 371 'id' => 'entity_link_edit', 372 ], 373 ]; 374 } 375 if ($this->entityType->hasLinkTemplate('delete-form')) { 376 $data['delete_' . $entity_type_id] = [ 377 'field' => [ 378 'title' => $this->t('Link to delete @entity_type_label', $t_arguments), 379 'help' => $this->t('Provide a delete link to the @entity_type_label.', $t_arguments), 380 'id' => 'entity_link_delete', 381 ], 382 ]; 383 } 384 } 385 386 /** 387 * Puts the views data for a single field onto the views data. 388 * 389 * @param string $table 390 * The table of the field to handle. 391 * @param string $field_name 392 * The name of the field to handle. 393 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition 394 * The field definition defined in Entity::baseFieldDefinitions() 395 * @param \Drupal\Core\Entity\Sql\TableMappingInterface $table_mapping 396 * The table mapping information 397 * @param array $table_data 398 * A reference to a specific entity table (for example data_table) inside 399 * the views data. 400 */ 401 protected function mapFieldDefinition($table, $field_name, FieldDefinitionInterface $field_definition, TableMappingInterface $table_mapping, &$table_data) { 402 // Create a dummy instance to retrieve property definitions. 403 $field_column_mapping = $table_mapping->getColumnNames($field_name); 404 $field_schema = $this->getFieldStorageDefinitions()[$field_name]->getSchema(); 405 406 $field_definition_type = $field_definition->getType(); 407 // Add all properties to views table data. We need an entry for each 408 // column of each field, with the first one given special treatment. 409 // @todo Introduce concept of the "main" column for a field, rather than 410 // assuming the first one is the main column. See also what the 411 // mapSingleFieldViewsData() method does with $first. 412 $first = TRUE; 413 foreach ($field_column_mapping as $field_column_name => $schema_field_name) { 414 $table_data[$schema_field_name] = $this->mapSingleFieldViewsData($table, $field_name, $field_definition_type, $field_column_name, $field_schema['columns'][$field_column_name]['type'], $first, $field_definition); 415 $table_data[$schema_field_name]['entity field'] = $field_name; 416 $first = FALSE; 417 } 418 } 419 420 /** 421 * Provides the views data for a given data type and schema field. 422 * 423 * @param string $table 424 * The table of the field to handle. 425 * @param string $field_name 426 * The machine name of the field being processed. 427 * @param string $field_type 428 * The type of field being handled. 429 * @param string $column_name 430 * For fields containing multiple columns, the column name being processed. 431 * @param string $column_type 432 * Within the field, the column type being handled. 433 * @param bool $first 434 * TRUE if this is the first column within the field. 435 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition 436 * The field definition. 437 * 438 * @return array 439 * The modified views data field definition. 440 */ 441 protected function mapSingleFieldViewsData($table, $field_name, $field_type, $column_name, $column_type, $first, FieldDefinitionInterface $field_definition) { 442 $views_field = []; 443 444 // Provide a nicer, less verbose label for the first column within a field. 445 // @todo Introduce concept of the "main" column for a field, rather than 446 // assuming the first one is the main column. 447 if ($first) { 448 $views_field['title'] = $field_definition->getLabel(); 449 } 450 else { 451 $views_field['title'] = $field_definition->getLabel() . " ($column_name)"; 452 } 453 454 if ($description = $field_definition->getDescription()) { 455 $views_field['help'] = $description; 456 } 457 458 // Set up the field, sort, argument, and filters, based on 459 // the column and/or field data type. 460 // @todo Allow field types to customize this. 461 // @see https://www.drupal.org/node/2337515 462 switch ($field_type) { 463 // Special case a few field types. 464 case 'timestamp': 465 case 'created': 466 case 'changed': 467 $views_field['field']['id'] = 'field'; 468 $views_field['argument']['id'] = 'date'; 469 $views_field['filter']['id'] = 'date'; 470 $views_field['sort']['id'] = 'date'; 471 break; 472 473 case 'language': 474 $views_field['field']['id'] = 'field'; 475 $views_field['argument']['id'] = 'language'; 476 $views_field['filter']['id'] = 'language'; 477 $views_field['sort']['id'] = 'standard'; 478 break; 479 480 case 'boolean': 481 $views_field['field']['id'] = 'field'; 482 $views_field['argument']['id'] = 'numeric'; 483 $views_field['filter']['id'] = 'boolean'; 484 $views_field['sort']['id'] = 'standard'; 485 break; 486 487 case 'uri': 488 // Let's render URIs as URIs by default, not links. 489 $views_field['field']['id'] = 'field'; 490 $views_field['field']['default_formatter'] = 'string'; 491 492 $views_field['argument']['id'] = 'string'; 493 $views_field['filter']['id'] = 'string'; 494 $views_field['sort']['id'] = 'standard'; 495 break; 496 497 case 'text': 498 case 'text_with_summary': 499 // Treat these three long text fields the same. 500 $field_type = 'text_long'; 501 // Intentional fall-through here to the default processing! 502 503 default: 504 // For most fields, the field type is generic enough to just use 505 // the column type to determine the filters etc. 506 switch ($column_type) { 507 508 case 'int': 509 case 'integer': 510 case 'smallint': 511 case 'tinyint': 512 case 'mediumint': 513 case 'float': 514 case 'double': 515 case 'decimal': 516 $views_field['field']['id'] = 'field'; 517 $views_field['argument']['id'] = 'numeric'; 518 $views_field['filter']['id'] = 'numeric'; 519 $views_field['sort']['id'] = 'standard'; 520 break; 521 522 case 'char': 523 case 'string': 524 case 'varchar': 525 case 'varchar_ascii': 526 case 'tinytext': 527 case 'text': 528 case 'mediumtext': 529 case 'longtext': 530 $views_field['field']['id'] = 'field'; 531 $views_field['argument']['id'] = 'string'; 532 $views_field['filter']['id'] = 'string'; 533 $views_field['sort']['id'] = 'standard'; 534 break; 535 536 default: 537 $views_field['field']['id'] = 'field'; 538 $views_field['argument']['id'] = 'standard'; 539 $views_field['filter']['id'] = 'standard'; 540 $views_field['sort']['id'] = 'standard'; 541 } 542 } 543 544 // Do post-processing for a few field types. 545 546 $process_method = 'processViewsDataFor' . Container::camelize($field_type); 547 if (method_exists($this, $process_method)) { 548 $this->{$process_method}($table, $field_definition, $views_field, $column_name); 549 } 550 551 return $views_field; 552 } 553 554 /** 555 * Processes the views data for a language field. 556 * 557 * @param string $table 558 * The table the language field is added to. 559 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition 560 * The field definition. 561 * @param array $views_field 562 * The views field data. 563 * @param string $field_column_name 564 * The field column being processed. 565 */ 566 protected function processViewsDataForLanguage($table, FieldDefinitionInterface $field_definition, array &$views_field, $field_column_name) { 567 // Apply special titles for the langcode field. 568 if ($field_definition->getName() == $this->entityType->getKey('langcode')) { 569 if ($table == $this->entityType->getDataTable() || $table == $this->entityType->getRevisionDataTable()) { 570 $views_field['title'] = $this->t('Translation language'); 571 } 572 if ($table == $this->entityType->getBaseTable() || $table == $this->entityType->getRevisionTable()) { 573 $views_field['title'] = $this->t('Original language'); 574 } 575 } 576 } 577 578 /** 579 * Processes the views data for an entity reference field. 580 * 581 * @param string $table 582 * The table the language field is added to. 583 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition 584 * The field definition. 585 * @param array $views_field 586 * The views field data. 587 * @param string $field_column_name 588 * The field column being processed. 589 */ 590 protected function processViewsDataForEntityReference($table, FieldDefinitionInterface $field_definition, array &$views_field, $field_column_name) { 591 592 // @todo Should the actual field handler respect that this just renders a 593 // number? 594 // @todo Create an optional entity field handler, that can render the 595 // entity. 596 // @see https://www.drupal.org/node/2322949 597 598 if ($entity_type_id = $field_definition->getItemDefinition()->getSetting('target_type')) { 599 $entity_type = $this->entityTypeManager->getDefinition($entity_type_id); 600 if ($entity_type instanceof ContentEntityType) { 601 $views_field['relationship'] = [ 602 'base' => $this->getViewsTableForEntityType($entity_type), 603 'base field' => $entity_type->getKey('id'), 604 'label' => $entity_type->getLabel(), 605 'title' => $entity_type->getLabel(), 606 'id' => 'standard', 607 ]; 608 $views_field['field']['id'] = 'field'; 609 $views_field['argument']['id'] = 'numeric'; 610 $views_field['filter']['id'] = 'numeric'; 611 $views_field['sort']['id'] = 'standard'; 612 } 613 else { 614 $views_field['field']['id'] = 'field'; 615 $views_field['argument']['id'] = 'string'; 616 $views_field['filter']['id'] = 'string'; 617 $views_field['sort']['id'] = 'standard'; 618 } 619 } 620 621 if ($field_definition->getName() == $this->entityType->getKey('bundle')) { 622 $views_field['filter']['id'] = 'bundle'; 623 } 624 } 625 626 /** 627 * Processes the views data for a text field with formatting. 628 * 629 * @param string $table 630 * The table the field is added to. 631 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition 632 * The field definition. 633 * @param array $views_field 634 * The views field data. 635 * @param string $field_column_name 636 * The field column being processed. 637 */ 638 protected function processViewsDataForTextLong($table, FieldDefinitionInterface $field_definition, array &$views_field, $field_column_name) { 639 // Connect the text field to its formatter. 640 if ($field_column_name == 'value') { 641 $views_field['field']['format'] = $field_definition->getName() . '__format'; 642 $views_field['field']['id'] = 'field'; 643 } 644 } 645 646 /** 647 * Processes the views data for a UUID field. 648 * 649 * @param string $table 650 * The table the field is added to. 651 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition 652 * The field definition. 653 * @param array $views_field 654 * The views field data. 655 * @param string $field_column_name 656 * The field column being processed. 657 */ 658 protected function processViewsDataForUuid($table, FieldDefinitionInterface $field_definition, array &$views_field, $field_column_name) { 659 // It does not make sense for UUID fields to be click sortable. 660 $views_field['field']['click sortable'] = FALSE; 661 } 662 663 /** 664 * {@inheritdoc} 665 */ 666 public function getViewsTableForEntityType(EntityTypeInterface $entity_type) { 667 return $entity_type->getDataTable() ?: $entity_type->getBaseTable(); 668 } 669 670} 671