1. 8.2.x core/modules/views/src/EntityViewsData.php
  2. 8.0.x core/modules/views/src/EntityViewsData.php
  3. 8.1.x core/modules/views/src/EntityViewsData.php
  4. 8.3.x core/modules/views/src/EntityViewsData.php

Namespace

Drupal\views

File

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

Classes

Namesort descending Description
EntityViewsData Provides generic views integration for entities.