2015-08-18 00:00:26 +00:00
< ? php
/**
* @ file
* Provide views data that isn ' t tied to any other module .
*/
use Drupal\Component\Utility\NestedArray ;
use Drupal\Core\Entity\EntityStorageInterface ;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage ;
2015-10-08 18:40:12 +00:00
use Drupal\Core\Render\Markup ;
2015-08-18 00:00:26 +00:00
use Drupal\field\FieldConfigInterface ;
use Drupal\field\FieldStorageConfigInterface ;
use Drupal\system\ActionConfigEntityInterface ;
/**
* Implements hook_views_data () .
*/
function views_views_data () {
$data [ 'views' ][ 'table' ][ 'group' ] = t ( 'Global' );
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'table' ][ 'join' ] = [
2015-08-18 00:00:26 +00:00
// #global is a special flag which allows a table to appear all the time.
2017-04-13 14:53:35 +00:00
'#global' => [],
];
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'random' ] = [
2015-08-18 00:00:26 +00:00
'title' => t ( 'Random' ),
'help' => t ( 'Randomize the display order.' ),
2017-04-13 14:53:35 +00:00
'sort' => [
2015-08-18 00:00:26 +00:00
'id' => 'random' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'null' ] = [
2015-08-18 00:00:26 +00:00
'title' => t ( 'Null' ),
'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.' ),
2017-04-13 14:53:35 +00:00
'argument' => [
2015-08-18 00:00:26 +00:00
'id' => 'null' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'nothing' ] = [
2015-08-18 00:00:26 +00:00
'title' => t ( 'Custom text' ),
'help' => t ( 'Provide custom text or link.' ),
2017-04-13 14:53:35 +00:00
'field' => [
2015-08-18 00:00:26 +00:00
'id' => 'custom' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'counter' ] = [
2015-08-18 00:00:26 +00:00
'title' => t ( 'View result counter' ),
'help' => t ( 'Displays the actual position of the view result' ),
2017-04-13 14:53:35 +00:00
'field' => [
2015-08-18 00:00:26 +00:00
'id' => 'counter' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'area' ] = [
2015-08-18 00:00:26 +00:00
'title' => t ( 'Text area' ),
'help' => t ( 'Provide markup text for the area.' ),
2017-04-13 14:53:35 +00:00
'area' => [
2015-08-18 00:00:26 +00:00
'id' => 'text' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'area_text_custom' ] = [
2015-08-18 00:00:26 +00:00
'title' => t ( 'Unfiltered text' ),
'help' => t ( 'Add unrestricted, custom text or markup. This is similar to the custom text field.' ),
2017-04-13 14:53:35 +00:00
'area' => [
2015-08-18 00:00:26 +00:00
'id' => 'text_custom' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'title' ] = [
2015-08-18 00:00:26 +00:00
'title' => t ( 'Title override' ),
'help' => t ( 'Override the default view title for this view. This is useful to display an alternative title when a view is empty.' ),
2017-04-13 14:53:35 +00:00
'area' => [
2015-08-18 00:00:26 +00:00
'id' => 'title' ,
'sub_type' => 'empty' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'view' ] = [
2015-08-18 00:00:26 +00:00
'title' => t ( 'View area' ),
'help' => t ( 'Insert a view inside an area.' ),
2017-04-13 14:53:35 +00:00
'area' => [
2015-08-18 00:00:26 +00:00
'id' => 'view' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'result' ] = [
2015-08-18 00:00:26 +00:00
'title' => t ( 'Result summary' ),
'help' => t ( 'Shows result summary, for example the items per page.' ),
2017-04-13 14:53:35 +00:00
'area' => [
2015-08-18 00:00:26 +00:00
'id' => 'result' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'messages' ] = [
2015-08-18 00:00:26 +00:00
'title' => t ( 'Messages' ),
'help' => t ( 'Displays messages in an area.' ),
2017-04-13 14:53:35 +00:00
'area' => [
2015-08-18 00:00:26 +00:00
'id' => 'messages' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'http_status_code' ] = [
2015-08-18 00:00:26 +00:00
'title' => t ( 'Response status code' ),
'help' => t ( 'Alter the HTTP response status code used by this view, mostly helpful for empty results.' ),
2017-04-13 14:53:35 +00:00
'area' => [
2015-08-18 00:00:26 +00:00
'id' => 'http_status_code' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'combine' ] = [
2015-08-18 00:00:26 +00:00
'title' => t ( 'Combine fields filter' ),
2016-10-06 22:16:20 +00:00
'help' => t ( 'Combine multiple fields together and search by them.' ),
2017-04-13 14:53:35 +00:00
'filter' => [
2015-08-18 00:00:26 +00:00
'id' => 'combine' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'dropbutton' ] = [
2015-08-18 00:00:26 +00:00
'title' => t ( 'Dropbutton' ),
'help' => t ( 'Display fields in a dropbutton.' ),
2017-04-13 14:53:35 +00:00
'field' => [
2015-08-18 00:00:26 +00:00
'id' => 'dropbutton' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
// Registers an entity area handler per entity type.
foreach ( \Drupal :: entityManager () -> getDefinitions () as $entity_type_id => $entity_type ) {
// Excludes entity types, which cannot be rendered.
if ( $entity_type -> hasViewBuilderClass ()) {
$label = $entity_type -> getLabel ();
2017-04-13 14:53:35 +00:00
$data [ 'views' ][ 'entity_' . $entity_type_id ] = [
'title' => t ( 'Rendered entity - @label' , [ '@label' => $label ]),
'help' => t ( 'Displays a rendered @label entity in an area.' , [ '@label' => $label ]),
'area' => [
2015-08-18 00:00:26 +00:00
'entity_type' => $entity_type_id ,
'id' => 'entity' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
}
}
// Registers an action bulk form per entity.
foreach ( \Drupal :: entityManager () -> getDefinitions () as $entity_type => $entity_info ) {
$actions = array_filter ( \Drupal :: entityManager () -> getStorage ( 'action' ) -> loadMultiple (), function ( ActionConfigEntityInterface $action ) use ( $entity_type ) {
return $action -> getType () == $entity_type ;
});
if ( empty ( $actions )) {
continue ;
}
2017-04-13 14:53:35 +00:00
$data [ $entity_info -> getBaseTable ()][ $entity_type . '_bulk_form' ] = [
2015-08-18 00:00:26 +00:00
'title' => t ( 'Bulk update' ),
'help' => t ( 'Allows users to apply an action to one or more items.' ),
2017-04-13 14:53:35 +00:00
'field' => [
2015-08-18 00:00:26 +00:00
'id' => 'bulk_form' ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
}
// Registers views data for the entity itself.
foreach ( \Drupal :: entityManager () -> getDefinitions () as $entity_type_id => $entity_type ) {
if ( $entity_type -> hasHandlerClass ( 'views_data' )) {
/** @var \Drupal\views\EntityViewsDataInterface $views_data */
$views_data = \Drupal :: entityManager () -> getHandler ( $entity_type_id , 'views_data' );
$data = NestedArray :: mergeDeep ( $data , $views_data -> getViewsData ());
}
}
// Field modules can implement hook_field_views_data() to override the default
// behavior for adding fields.
$module_handler = \Drupal :: moduleHandler ();
$entity_manager = \Drupal :: entityManager ();
if ( $entity_manager -> hasDefinition ( 'field_storage_config' )) {
/** @var \Drupal\field\FieldStorageConfigInterface $field_storage */
foreach ( $entity_manager -> getStorage ( 'field_storage_config' ) -> loadMultiple () as $field_storage ) {
if ( _views_field_get_entity_type_storage ( $field_storage )) {
2017-04-13 14:53:35 +00:00
$result = ( array ) $module_handler -> invoke ( $field_storage -> getTypeProvider (), 'field_views_data' , [ $field_storage ]);
2015-08-18 00:00:26 +00:00
if ( empty ( $result )) {
$result = views_field_default_views_data ( $field_storage );
}
$module_handler -> alter ( 'field_views_data' , $result , $field_storage );
if ( is_array ( $result )) {
$data = NestedArray :: mergeDeep ( $result , $data );
}
}
}
}
return $data ;
}
/**
* Implements hook_views_data_alter () .
*
* Field modules can implement hook_field_views_data_views_data_alter () to
* alter the views data on a per field basis . This is weirdly named so as
* not to conflict with the \Drupal :: moduleHandler () -> alter ( 'field_views_data' )
* in views_views_data () .
*/
function views_views_data_alter ( & $data ) {
$entity_manager = \Drupal :: entityManager ();
if ( ! $entity_manager -> hasDefinition ( 'field_storage_config' )) {
return ;
}
/** @var \Drupal\field\FieldStorageConfigInterface $field_storage */
foreach ( $entity_manager -> getStorage ( 'field_storage_config' ) -> loadMultiple () as $field_storage ) {
if ( _views_field_get_entity_type_storage ( $field_storage )) {
$function = $field_storage -> getTypeProvider () . '_field_views_data_views_data_alter' ;
if ( function_exists ( $function )) {
$function ( $data , $field_storage );
}
}
}
}
/**
* Determines whether the entity type the field appears in is SQL based .
*
* @ param \Drupal\field\FieldStorageConfigInterface $field_storage
* The field storage definition .
*
* @ return \Drupal\Core\Entity\Sql\SqlContentEntityStorage
* Returns the entity type storage if supported .
*/
function _views_field_get_entity_type_storage ( FieldStorageConfigInterface $field_storage ) {
$result = FALSE ;
$entity_manager = \Drupal :: entityManager ();
if ( $entity_manager -> hasDefinition ( $field_storage -> getTargetEntityTypeId ())) {
$storage = $entity_manager -> getStorage ( $field_storage -> getTargetEntityTypeId ());
$result = $storage instanceof SqlContentEntityStorage ? $storage : FALSE ;
}
return $result ;
}
/**
* Returns the label of a certain field .
*
* Therefore it looks up in all bundles to find the most used field .
*/
function views_entity_field_label ( $entity_type , $field_name ) {
2017-04-13 14:53:35 +00:00
$label_counter = [];
$all_labels = [];
2015-08-18 00:00:26 +00:00
// Count the amount of fields per label per field storage.
foreach ( array_keys ( \Drupal :: entityManager () -> getBundleInfo ( $entity_type )) as $bundle ) {
$bundle_fields = array_filter ( \Drupal :: entityManager () -> getFieldDefinitions ( $entity_type , $bundle ), function ( $field_definition ) {
return $field_definition instanceof FieldConfigInterface ;
});
if ( isset ( $bundle_fields [ $field_name ])) {
$field = $bundle_fields [ $field_name ];
$label = $field -> getLabel ();
$label_counter [ $label ] = isset ( $label_counter [ $label ]) ? ++ $label_counter [ $label ] : 1 ;
$all_labels [ $label ] = TRUE ;
}
}
if ( empty ( $label_counter )) {
2017-04-13 14:53:35 +00:00
return [ $field_name , $all_labels ];
2015-08-18 00:00:26 +00:00
}
// Sort the field labels by it most used label and return the most used one.
2015-11-04 19:11:27 +00:00
// If the counts are equal, sort by the label to ensure the result is
// deterministic.
uksort ( $label_counter , function ( $a , $b ) use ( $label_counter ) {
if ( $label_counter [ $a ] === $label_counter [ $b ]) {
return strcmp ( $a , $b );
}
return $label_counter [ $a ] > $label_counter [ $b ] ? - 1 : 1 ;
});
2015-08-18 00:00:26 +00:00
$label_counter = array_keys ( $label_counter );
2017-04-13 14:53:35 +00:00
return [ $label_counter [ 0 ], $all_labels ];
2015-08-18 00:00:26 +00:00
}
/**
* Default views data implementation for a field .
*
* @ param \Drupal\field\FieldStorageConfigInterface $field_storage
* The field definition .
*
* @ return array
* The default views data for the field .
*/
function views_field_default_views_data ( FieldStorageConfigInterface $field_storage ) {
2017-04-13 14:53:35 +00:00
$data = [];
2015-08-18 00:00:26 +00:00
// Check the field type is available.
if ( ! \Drupal :: service ( 'plugin.manager.field.field_type' ) -> hasDefinition ( $field_storage -> getType ())) {
return $data ;
}
// Check the field storage has fields.
if ( ! $field_storage -> getBundles ()) {
return $data ;
}
2016-06-02 22:56:09 +00:00
// Ignore custom storage too.
if ( $field_storage -> hasCustomStorage ()) {
return $data ;
}
2015-08-18 00:00:26 +00:00
// Check whether the entity type storage is supported.
$storage = _views_field_get_entity_type_storage ( $field_storage );
if ( ! $storage ) {
return $data ;
}
$field_name = $field_storage -> getName ();
$field_columns = $field_storage -> getColumns ();
// Grab information about the entity type tables.
// We need to join to both the base table and the data table, if available.
$entity_manager = \Drupal :: entityManager ();
$entity_type_id = $field_storage -> getTargetEntityTypeId ();
$entity_type = $entity_manager -> getDefinition ( $entity_type_id );
if ( ! $base_table = $entity_type -> getBaseTable ()) {
// We cannot do anything if for some reason there is no base table.
return $data ;
}
2017-04-13 14:53:35 +00:00
$entity_tables = [ $base_table => $entity_type_id ];
2015-08-18 00:00:26 +00:00
// Some entities may not have a data table.
$data_table = $entity_type -> getDataTable ();
if ( $data_table ) {
$entity_tables [ $data_table ] = $entity_type_id ;
}
$entity_revision_table = $entity_type -> getRevisionTable ();
$supports_revisions = $entity_type -> hasKey ( 'revision' ) && $entity_revision_table ;
if ( $supports_revisions ) {
$entity_tables [ $entity_revision_table ] = $entity_type_id ;
$entity_revision_data_table = $entity_type -> getRevisionDataTable ();
if ( $entity_revision_data_table ) {
$entity_tables [ $entity_revision_data_table ] = $entity_type_id ;
}
}
// Description of the field tables.
// @todo Generalize this code to make it work with any table layout. See
// https://www.drupal.org/node/2079019.
$table_mapping = $storage -> getTableMapping ();
2017-04-13 14:53:35 +00:00
$field_tables = [
EntityStorageInterface :: FIELD_LOAD_CURRENT => [
2015-08-18 00:00:26 +00:00
'table' => $table_mapping -> getDedicatedDataTableName ( $field_storage ),
'alias' => " { $entity_type_id } __ { $field_name } " ,
2017-04-13 14:53:35 +00:00
],
];
2015-08-18 00:00:26 +00:00
if ( $supports_revisions ) {
2017-04-13 14:53:35 +00:00
$field_tables [ EntityStorageInterface :: FIELD_LOAD_REVISION ] = [
2015-08-18 00:00:26 +00:00
'table' => $table_mapping -> getDedicatedRevisionTableName ( $field_storage ),
'alias' => " { $entity_type_id } _revision__ { $field_name } " ,
2017-04-13 14:53:35 +00:00
];
2015-08-18 00:00:26 +00:00
}
// Build the relationships between the field table and the entity tables.
$table_alias = $field_tables [ EntityStorageInterface :: FIELD_LOAD_CURRENT ][ 'alias' ];
if ( $data_table ) {
// Tell Views how to join to the base table, via the data table.
2017-04-13 14:53:35 +00:00
$data [ $table_alias ][ 'table' ][ 'join' ][ $data_table ] = [
2016-09-07 20:26:21 +00:00
'table' => $table_mapping -> getDedicatedDataTableName ( $field_storage ),
2015-08-18 00:00:26 +00:00
'left_field' => $entity_type -> getKey ( 'id' ),
'field' => 'entity_id' ,
2017-04-13 14:53:35 +00:00
'extra' => [
[ 'field' => 'deleted' , 'value' => 0 , 'numeric' => TRUE ],
[ 'left_field' => 'langcode' , 'field' => 'langcode' ],
],
];
2015-08-18 00:00:26 +00:00
}
else {
// If there is no data table, just join directly.
2017-04-13 14:53:35 +00:00
$data [ $table_alias ][ 'table' ][ 'join' ][ $base_table ] = [
2016-09-07 20:26:21 +00:00
'table' => $table_mapping -> getDedicatedDataTableName ( $field_storage ),
2015-08-18 00:00:26 +00:00
'left_field' => $entity_type -> getKey ( 'id' ),
'field' => 'entity_id' ,
2017-04-13 14:53:35 +00:00
'extra' => [
[ 'field' => 'deleted' , 'value' => 0 , 'numeric' => TRUE ],
],
];
2015-08-18 00:00:26 +00:00
}
if ( $supports_revisions ) {
$table_alias = $field_tables [ EntityStorageInterface :: FIELD_LOAD_REVISION ][ 'alias' ];
if ( $entity_revision_data_table ) {
// Tell Views how to join to the revision table, via the data table.
2017-04-13 14:53:35 +00:00
$data [ $table_alias ][ 'table' ][ 'join' ][ $entity_revision_data_table ] = [
2016-09-07 20:26:21 +00:00
'table' => $table_mapping -> getDedicatedRevisionTableName ( $field_storage ),
2015-08-18 00:00:26 +00:00
'left_field' => $entity_type -> getKey ( 'revision' ),
'field' => 'revision_id' ,
2017-04-13 14:53:35 +00:00
'extra' => [
[ 'field' => 'deleted' , 'value' => 0 , 'numeric' => TRUE ],
[ 'left_field' => 'langcode' , 'field' => 'langcode' ],
],
];
2015-08-18 00:00:26 +00:00
}
else {
// If there is no data table, just join directly.
2017-04-13 14:53:35 +00:00
$data [ $table_alias ][ 'table' ][ 'join' ][ $entity_revision_table ] = [
2016-09-07 20:26:21 +00:00
'table' => $table_mapping -> getDedicatedRevisionTableName ( $field_storage ),
2015-08-18 00:00:26 +00:00
'left_field' => $entity_type -> getKey ( 'revision' ),
'field' => 'revision_id' ,
2017-04-13 14:53:35 +00:00
'extra' => [
[ 'field' => 'deleted' , 'value' => 0 , 'numeric' => TRUE ],
],
];
2015-08-18 00:00:26 +00:00
}
}
$group_name = $entity_type -> getLabel ();
// Get the list of bundles the field appears in.
$bundles_names = $field_storage -> getBundles ();
// Build the list of additional fields to add to queries.
2017-04-13 14:53:35 +00:00
$add_fields = [ 'delta' , 'langcode' , 'bundle' ];
2015-08-18 00:00:26 +00:00
foreach ( array_keys ( $field_columns ) as $column ) {
$add_fields [] = $table_mapping -> getFieldColumnName ( $field_storage , $column );
}
// Determine the label to use for the field. We don't have a label available
// at the field level, so we just go through all fields and take the one
// which is used the most frequently.
list ( $label , $all_labels ) = views_entity_field_label ( $entity_type_id , $field_name );
// Expose data for the field as a whole.
foreach ( $field_tables as $type => $table_info ) {
$table = $table_info [ 'table' ];
$table_alias = $table_info [ 'alias' ];
if ( $type == EntityStorageInterface :: FIELD_LOAD_CURRENT ) {
$group = $group_name ;
$field_alias = $field_name ;
}
else {
2017-04-13 14:53:35 +00:00
$group = t ( '@group (historical data)' , [ '@group' => $group_name ]);
2015-08-18 00:00:26 +00:00
$field_alias = $field_name . '-revision_id' ;
}
2017-04-13 14:53:35 +00:00
$data [ $table_alias ][ $field_alias ] = [
2015-08-18 00:00:26 +00:00
'group' => $group ,
'title' => $label ,
'title short' => $label ,
2017-04-13 14:53:35 +00:00
'help' => t ( 'Appears in: @bundles.' , [ '@bundles' => implode ( ', ' , $bundles_names )]),
];
2015-08-18 00:00:26 +00:00
// Go through and create a list of aliases for all possible combinations of
// entity type + name.
2017-04-13 14:53:35 +00:00
$aliases = [];
$also_known = [];
2015-08-18 00:00:26 +00:00
foreach ( $all_labels as $label_name => $true ) {
if ( $type == EntityStorageInterface :: FIELD_LOAD_CURRENT ) {
if ( $label != $label_name ) {
2017-04-13 14:53:35 +00:00
$aliases [] = [
2015-08-18 00:00:26 +00:00
'base' => $base_table ,
'group' => $group_name ,
'title' => $label_name ,
2017-04-13 14:53:35 +00:00
'help' => t ( 'This is an alias of @group: @field.' , [ '@group' => $group_name , '@field' => $label ]),
];
$also_known [] = t ( '@group: @field' , [ '@group' => $group_name , '@field' => $label_name ]);
2015-08-18 00:00:26 +00:00
}
}
elseif ( $supports_revisions && $label != $label_name ) {
2017-04-13 14:53:35 +00:00
$aliases [] = [
2015-08-18 00:00:26 +00:00
'base' => $table ,
2017-04-13 14:53:35 +00:00
'group' => t ( '@group (historical data)' , [ '@group' => $group_name ]),
2015-08-18 00:00:26 +00:00
'title' => $label_name ,
2017-04-13 14:53:35 +00:00
'help' => t ( 'This is an alias of @group: @field.' , [ '@group' => $group_name , '@field' => $label ]),
];
$also_known [] = t ( '@group (historical data): @field' , [ '@group' => $group_name , '@field' => $label_name ]);
2015-08-18 00:00:26 +00:00
}
}
if ( $aliases ) {
$data [ $table_alias ][ $field_alias ][ 'aliases' ] = $aliases ;
2015-10-08 18:40:12 +00:00
// The $also_known variable contains markup that is HTML escaped and that
// loses safeness when imploded. The help text is used in #description
// and therefore XSS admin filtered by default. Escaped HTML is not
// altered by XSS filtering, therefore it is safe to just concatenate the
// strings. Afterwards we mark the entire string as safe, so it won't be
// escaped, no matter where it is used.
// Considering the dual use of this help data (both as metadata and as
// help text), other patterns such as use of #markup would not be correct
// here.
$data [ $table_alias ][ $field_alias ][ 'help' ] = Markup :: create ( $data [ $table_alias ][ $field_alias ][ 'help' ] . ' ' . t ( 'Also known as:' ) . ' ' . implode ( ', ' , $also_known ));
2015-08-18 00:00:26 +00:00
}
$keys = array_keys ( $field_columns );
$real_field = reset ( $keys );
2017-04-13 14:53:35 +00:00
$data [ $table_alias ][ $field_alias ][ 'field' ] = [
2015-08-18 00:00:26 +00:00
'table' => $table ,
'id' => 'field' ,
'field_name' => $field_name ,
'entity_type' => $entity_type_id ,
// Provide a real field for group by.
'real field' => $field_alias . '_' . $real_field ,
'additional fields' => $add_fields ,
// Default the element type to div, let the UI change it if necessary.
'element type' => 'div' ,
'is revision' => $type == EntityStorageInterface :: FIELD_LOAD_REVISION ,
2017-04-13 14:53:35 +00:00
];
2015-08-18 00:00:26 +00:00
}
// Expose data for each field property individually.
foreach ( $field_columns as $column => $attributes ) {
$allow_sort = TRUE ;
// Identify likely filters and arguments for each column based on field type.
switch ( $attributes [ 'type' ]) {
case 'int' :
case 'mediumint' :
case 'tinyint' :
case 'bigint' :
case 'serial' :
case 'numeric' :
case 'float' :
$filter = 'numeric' ;
$argument = 'numeric' ;
$sort = 'standard' ;
2016-03-02 20:40:24 +00:00
if ( $field_storage -> getType () == 'boolean' ) {
$filter = 'boolean' ;
}
2015-08-18 00:00:26 +00:00
break ;
case 'text' :
case 'blob' :
// It does not make sense to sort by blob or text.
$allow_sort = FALSE ;
default :
$filter = 'string' ;
$argument = 'string' ;
$sort = 'standard' ;
break ;
}
if ( count ( $field_columns ) == 1 || $column == 'value' ) {
2017-04-13 14:53:35 +00:00
$title = t ( '@label (@name)' , [ '@label' => $label , '@name' => $field_name ]);
2015-08-18 00:00:26 +00:00
$title_short = $label ;
}
else {
2017-04-13 14:53:35 +00:00
$title = t ( '@label (@name:@column)' , [ '@label' => $label , '@name' => $field_name , '@column' => $column ]);
$title_short = t ( '@label:@column' , [ '@label' => $label , '@column' => $column ]);
2015-08-18 00:00:26 +00:00
}
// Expose data for the property.
foreach ( $field_tables as $type => $table_info ) {
$table = $table_info [ 'table' ];
$table_alias = $table_info [ 'alias' ];
if ( $type == EntityStorageInterface :: FIELD_LOAD_CURRENT ) {
$group = $group_name ;
}
else {
2017-04-13 14:53:35 +00:00
$group = t ( '@group (historical data)' , [ '@group' => $group_name ]);
2015-08-18 00:00:26 +00:00
}
$column_real_name = $table_mapping -> getFieldColumnName ( $field_storage , $column );
// Load all the fields from the table by default.
$additional_fields = $table_mapping -> getAllColumns ( $table );
2017-04-13 14:53:35 +00:00
$data [ $table_alias ][ $column_real_name ] = [
2015-08-18 00:00:26 +00:00
'group' => $group ,
'title' => $title ,
'title short' => $title_short ,
2017-04-13 14:53:35 +00:00
'help' => t ( 'Appears in: @bundles.' , [ '@bundles' => implode ( ', ' , $bundles_names )]),
];
2015-08-18 00:00:26 +00:00
// Go through and create a list of aliases for all possible combinations of
// entity type + name.
2017-04-13 14:53:35 +00:00
$aliases = [];
$also_known = [];
2015-08-18 00:00:26 +00:00
foreach ( $all_labels as $label_name => $true ) {
if ( $label != $label_name ) {
if ( count ( $field_columns ) == 1 || $column == 'value' ) {
2017-04-13 14:53:35 +00:00
$alias_title = t ( '@label (@name)' , [ '@label' => $label_name , '@name' => $field_name ]);
2015-08-18 00:00:26 +00:00
}
else {
2017-04-13 14:53:35 +00:00
$alias_title = t ( '@label (@name:@column)' , [ '@label' => $label_name , '@name' => $field_name , '@column' => $column ]);
2015-08-18 00:00:26 +00:00
}
2017-04-13 14:53:35 +00:00
$aliases [] = [
2015-08-18 00:00:26 +00:00
'group' => $group_name ,
'title' => $alias_title ,
2017-04-13 14:53:35 +00:00
'help' => t ( 'This is an alias of @group: @field.' , [ '@group' => $group_name , '@field' => $title ]),
];
$also_known [] = t ( '@group: @field' , [ '@group' => $group_name , '@field' => $title ]);
2015-08-18 00:00:26 +00:00
}
}
if ( $aliases ) {
$data [ $table_alias ][ $column_real_name ][ 'aliases' ] = $aliases ;
2015-10-08 18:40:12 +00:00
// The $also_known variable contains markup that is HTML escaped and
// that loses safeness when imploded. The help text is used in
// #description and therefore XSS admin filtered by default. Escaped
// HTML is not altered by XSS filtering, therefore it is safe to just
// concatenate the strings. Afterwards we mark the entire string as
// safe, so it won't be escaped, no matter where it is used.
// Considering the dual use of this help data (both as metadata and as
// help text), other patterns such as use of #markup would not be
// correct here.
$data [ $table_alias ][ $column_real_name ][ 'help' ] = Markup :: create ( $data [ $table_alias ][ $column_real_name ][ 'help' ] . ' ' . t ( 'Also known as:' ) . ' ' . implode ( ', ' , $also_known ));
2015-08-18 00:00:26 +00:00
}
2017-04-13 14:53:35 +00:00
$data [ $table_alias ][ $column_real_name ][ 'argument' ] = [
2015-08-18 00:00:26 +00:00
'field' => $column_real_name ,
'table' => $table ,
'id' => $argument ,
'additional fields' => $additional_fields ,
'field_name' => $field_name ,
'entity_type' => $entity_type_id ,
'empty field name' => t ( '- No value -' ),
2017-04-13 14:53:35 +00:00
];
$data [ $table_alias ][ $column_real_name ][ 'filter' ] = [
2015-08-18 00:00:26 +00:00
'field' => $column_real_name ,
'table' => $table ,
'id' => $filter ,
'additional fields' => $additional_fields ,
'field_name' => $field_name ,
'entity_type' => $entity_type_id ,
'allow empty' => TRUE ,
2017-04-13 14:53:35 +00:00
];
2015-08-18 00:00:26 +00:00
if ( ! empty ( $allow_sort )) {
2017-04-13 14:53:35 +00:00
$data [ $table_alias ][ $column_real_name ][ 'sort' ] = [
2015-08-18 00:00:26 +00:00
'field' => $column_real_name ,
'table' => $table ,
'id' => $sort ,
'additional fields' => $additional_fields ,
'field_name' => $field_name ,
'entity_type' => $entity_type_id ,
2017-04-13 14:53:35 +00:00
];
2015-08-18 00:00:26 +00:00
}
// Set click sortable if there is a field definition.
if ( isset ( $data [ $table_alias ][ $field_name ][ 'field' ])) {
$data [ $table_alias ][ $field_name ][ 'field' ][ 'click sortable' ] = $allow_sort ;
}
// Expose additional delta column for multiple value fields.
if ( $field_storage -> isMultiple ()) {
2017-04-13 14:53:35 +00:00
$title_delta = t ( '@label (@name:delta)' , [ '@label' => $label , '@name' => $field_name ]);
$title_short_delta = t ( '@label:delta' , [ '@label' => $label ]);
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
$data [ $table_alias ][ 'delta' ] = [
2015-08-18 00:00:26 +00:00
'group' => $group ,
'title' => $title_delta ,
'title short' => $title_short_delta ,
2017-04-13 14:53:35 +00:00
'help' => t ( 'Delta - Appears in: @bundles.' , [ '@bundles' => implode ( ', ' , $bundles_names )]),
];
$data [ $table_alias ][ 'delta' ][ 'field' ] = [
2015-08-18 00:00:26 +00:00
'id' => 'numeric' ,
2017-04-13 14:53:35 +00:00
];
$data [ $table_alias ][ 'delta' ][ 'argument' ] = [
2015-08-18 00:00:26 +00:00
'field' => 'delta' ,
'table' => $table ,
'id' => 'numeric' ,
'additional fields' => $additional_fields ,
'empty field name' => t ( '- No value -' ),
'field_name' => $field_name ,
'entity_type' => $entity_type_id ,
2017-04-13 14:53:35 +00:00
];
$data [ $table_alias ][ 'delta' ][ 'filter' ] = [
2015-08-18 00:00:26 +00:00
'field' => 'delta' ,
'table' => $table ,
'id' => 'numeric' ,
'additional fields' => $additional_fields ,
'field_name' => $field_name ,
'entity_type' => $entity_type_id ,
'allow empty' => TRUE ,
2017-04-13 14:53:35 +00:00
];
$data [ $table_alias ][ 'delta' ][ 'sort' ] = [
2015-08-18 00:00:26 +00:00
'field' => 'delta' ,
'table' => $table ,
'id' => 'standard' ,
'additional fields' => $additional_fields ,
'field_name' => $field_name ,
'entity_type' => $entity_type_id ,
2017-04-13 14:53:35 +00:00
];
2015-08-18 00:00:26 +00:00
}
}
}
return $data ;
}
2015-10-08 18:40:12 +00:00
/**
* Implements hook_field_views_data () .
*
* The function implements the hook in behalf of 'core' because it adds a
* relationship and a reverse relationship to entity_reference field type , which
* is provided by core .
*/
function core_field_views_data ( FieldStorageConfigInterface $field_storage ) {
$data = views_field_default_views_data ( $field_storage );
// The code below only deals with the Entity reference field type.
if ( $field_storage -> getType () != 'entity_reference' ) {
return $data ;
}
$entity_manager = \Drupal :: entityManager ();
$entity_type_id = $field_storage -> getTargetEntityTypeId ();
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
$table_mapping = $entity_manager -> getStorage ( $entity_type_id ) -> getTableMapping ();
foreach ( $data as $table_name => $table_data ) {
// Add a relationship to the target entity type.
$target_entity_type_id = $field_storage -> getSetting ( 'target_type' );
$target_entity_type = $entity_manager -> getDefinition ( $target_entity_type_id );
$entity_type_id = $field_storage -> getTargetEntityTypeId ();
$entity_type = $entity_manager -> getDefinition ( $entity_type_id );
$target_base_table = $target_entity_type -> getDataTable () ? : $target_entity_type -> getBaseTable ();
$field_name = $field_storage -> getName ();
// Provide a relationship for the entity type with the entity reference
// field.
2017-04-13 14:53:35 +00:00
$args = [
2015-10-08 18:40:12 +00:00
'@label' => $target_entity_type -> getLabel (),
'@field_name' => $field_name ,
2017-04-13 14:53:35 +00:00
];
$data [ $table_name ][ $field_name ][ 'relationship' ] = [
2015-10-08 18:40:12 +00:00
'title' => t ( '@label referenced from @field_name' , $args ),
'label' => t ( '@field_name: @label' , $args ),
'group' => $entity_type -> getLabel (),
2017-04-13 14:53:35 +00:00
'help' => t ( 'Appears in: @bundles.' , [ '@bundles' => implode ( ', ' , $field_storage -> getBundles ())]),
2015-10-08 18:40:12 +00:00
'id' => 'standard' ,
'base' => $target_base_table ,
'entity type' => $target_entity_type_id ,
'base field' => $target_entity_type -> getKey ( 'id' ),
'relationship field' => $field_name . '_target_id' ,
2017-04-13 14:53:35 +00:00
];
2015-10-08 18:40:12 +00:00
// Provide a reverse relationship for the entity type that is referenced by
// the field.
$args [ '@entity' ] = $entity_type -> getLabel ();
$args [ '@label' ] = $target_entity_type -> getLowercaseLabel ();
$pseudo_field_name = 'reverse__' . $entity_type_id . '__' . $field_name ;
2017-04-13 14:53:35 +00:00
$data [ $target_base_table ][ $pseudo_field_name ][ 'relationship' ] = [
2015-10-08 18:40:12 +00:00
'title' => t ( '@entity using @field_name' , $args ),
2017-04-13 14:53:35 +00:00
'label' => t ( '@field_name' , [ '@field_name' => $field_name ]),
2015-10-08 18:40:12 +00:00
'group' => $target_entity_type -> getLabel (),
'help' => t ( 'Relate each @entity with a @field_name set to the @label.' , $args ),
'id' => 'entity_reverse' ,
'base' => $entity_type -> getDataTable () ? : $entity_type -> getBaseTable (),
'entity_type' => $entity_type_id ,
'base field' => $entity_type -> getKey ( 'id' ),
'field_name' => $field_name ,
'field table' => $table_mapping -> getDedicatedDataTableName ( $field_storage ),
'field field' => $field_name . '_target_id' ,
2017-04-13 14:53:35 +00:00
'join_extra' => [
[
2015-10-08 18:40:12 +00:00
'field' => 'deleted' ,
'value' => 0 ,
'numeric' => TRUE ,
2017-04-13 14:53:35 +00:00
],
],
];
2015-10-08 18:40:12 +00:00
}
return $data ;
}