2009-05-17 11:16:51 +00:00
< ? php
/**
* @ file
2013-11-20 18:38:33 +00:00
* Provide views data that isn ' t tied to any other module .
2009-05-17 11:16:51 +00:00
*/
2014-08-14 19:06:59 +00:00
use Drupal\Component\Utility\NestedArray ;
2020-07-13 12:12:25 +00:00
use Drupal\Core\Entity\ContentEntityTypeInterface ;
2015-01-06 17:20:24 +00:00
use Drupal\Core\Entity\EntityStorageInterface ;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage ;
2015-10-01 23:25:03 +00:00
use Drupal\Core\Render\Markup ;
Issue #2451657 by Upchuk, tstoeckler, k4v, lokapujya, dawehner, willwh, holist, yobottehg, Peacog, Sharique, plach, Berdir, deepakaryan1988, Xano, geertvd, pminf, alexpott, AkshayKalose, lauriii, mkernel, pritish.kumar, jibran: Views should not condition joins on the langcode of fields that are not translatable
2017-07-03 20:45:44 +00:00
use Drupal\field\Entity\FieldConfig ;
2015-01-06 17:20:24 +00:00
use Drupal\field\FieldConfigInterface ;
use Drupal\field\FieldStorageConfigInterface ;
2014-02-02 20:06:53 +00:00
use Drupal\system\ActionConfigEntityInterface ;
2009-05-17 11:16:51 +00:00
/**
* Implements hook_views_data () .
*/
2012-08-19 13:19:00 +00:00
function views_views_data () {
$data [ 'views' ][ 'table' ][ 'group' ] = t ( 'Global' );
2017-03-04 01:20:24 +00:00
$data [ 'views' ][ 'table' ][ 'join' ] = [
2014-06-09 14:50:45 +00:00
// #global is a special flag which allows a table to appear all the time.
2017-03-04 01:20:24 +00:00
'#global' => [],
];
2009-05-17 11:16:51 +00:00
2017-03-04 01:20:24 +00:00
$data [ 'views' ][ 'random' ] = [
2012-08-19 13:19:00 +00:00
'title' => t ( 'Random' ),
'help' => t ( 'Randomize the display order.' ),
2017-03-04 01:20:24 +00:00
'sort' => [
2012-08-19 13:19:00 +00:00
'id' => 'random' ,
2017-03-04 01:20:24 +00:00
],
];
2009-05-17 11:16:51 +00:00
2017-03-04 01:20:24 +00:00
$data [ 'views' ][ 'null' ] = [
2009-05-17 11:16:51 +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-03-04 01:20:24 +00:00
'argument' => [
2012-08-11 11:46:07 +00:00
'id' => 'null' ,
2017-03-04 01:20:24 +00:00
],
];
2009-05-17 11:16:51 +00:00
2017-03-04 01:20:24 +00:00
$data [ 'views' ][ 'nothing' ] = [
2009-05-17 11:16:51 +00:00
'title' => t ( 'Custom text' ),
'help' => t ( 'Provide custom text or link.' ),
2017-03-04 01:20:24 +00:00
'field' => [
2012-08-11 11:46:07 +00:00
'id' => 'custom' ,
2017-03-04 01:20:24 +00:00
],
];
2009-05-17 11:16:51 +00:00
2017-03-04 01:20:24 +00:00
$data [ 'views' ][ 'counter' ] = [
2009-05-17 11:16:51 +00:00
'title' => t ( 'View result counter' ),
'help' => t ( 'Displays the actual position of the view result' ),
2017-03-04 01:20:24 +00:00
'field' => [
2012-08-11 11:46:07 +00:00
'id' => 'counter' ,
2017-03-04 01:20:24 +00:00
],
];
2009-05-17 11:16:51 +00:00
2017-03-04 01:20:24 +00:00
$data [ 'views' ][ 'area' ] = [
2009-05-17 11:16:51 +00:00
'title' => t ( 'Text area' ),
'help' => t ( 'Provide markup text for the area.' ),
2017-03-04 01:20:24 +00:00
'area' => [
2012-08-11 11:46:07 +00:00
'id' => 'text' ,
2017-03-04 01:20:24 +00:00
],
];
2009-05-17 11:16:51 +00:00
2017-03-04 01:20:24 +00:00
$data [ 'views' ][ 'area_text_custom' ] = [
2012-07-29 21:44:28 +00:00
'title' => t ( 'Unfiltered text' ),
'help' => t ( 'Add unrestricted, custom text or markup. This is similar to the custom text field.' ),
2017-03-04 01:20:24 +00:00
'area' => [
2012-08-11 11:46:07 +00:00
'id' => 'text_custom' ,
2017-03-04 01:20:24 +00:00
],
];
2012-07-17 16:56:36 +00:00
2017-03-04 01:20:24 +00:00
$data [ 'views' ][ 'title' ] = [
2012-10-10 18:01:37 +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-03-04 01:20:24 +00:00
'area' => [
2012-10-10 18:01:37 +00:00
'id' => 'title' ,
2012-10-17 16:31:40 +00:00
'sub_type' => 'empty' ,
2017-03-04 01:20:24 +00:00
],
];
2012-10-10 18:01:37 +00:00
2017-03-04 01:20:24 +00:00
$data [ 'views' ][ 'view' ] = [
2009-05-17 11:16:51 +00:00
'title' => t ( 'View area' ),
'help' => t ( 'Insert a view inside an area.' ),
2017-03-04 01:20:24 +00:00
'area' => [
2012-08-11 11:46:07 +00:00
'id' => 'view' ,
2017-03-04 01:20:24 +00:00
],
];
2009-05-17 11:16:51 +00:00
2017-03-04 01:20:24 +00:00
$data [ 'views' ][ 'result' ] = [
2009-05-17 11:16:51 +00:00
'title' => t ( 'Result summary' ),
'help' => t ( 'Shows result summary, for example the items per page.' ),
2017-03-04 01:20:24 +00:00
'area' => [
2012-08-11 11:46:07 +00:00
'id' => 'result' ,
2017-03-04 01:20:24 +00:00
],
];
2009-05-17 11:16:51 +00:00
2017-03-04 01:20:24 +00:00
$data [ 'views' ][ 'messages' ] = [
2014-04-25 22:09:06 +00:00
'title' => t ( 'Messages' ),
'help' => t ( 'Displays messages in an area.' ),
2017-03-04 01:20:24 +00:00
'area' => [
2014-04-25 22:09:06 +00:00
'id' => 'messages' ,
2017-03-04 01:20:24 +00:00
],
];
2014-04-25 22:09:06 +00:00
2017-03-04 01:20:24 +00:00
$data [ 'views' ][ 'http_status_code' ] = [
2013-08-02 13:26:16 +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-03-04 01:20:24 +00:00
'area' => [
2013-08-02 13:26:16 +00:00
'id' => 'http_status_code' ,
2017-03-04 01:20:24 +00:00
],
];
2013-08-02 13:26:16 +00:00
2017-03-04 01:20:24 +00:00
$data [ 'views' ][ 'combine' ] = [
2009-05-17 11:16:51 +00:00
'title' => t ( 'Combine fields filter' ),
2016-04-26 12:09:05 +00:00
'help' => t ( 'Combine multiple fields together and search by them.' ),
2017-03-04 01:20:24 +00:00
'filter' => [
2012-08-11 11:46:07 +00:00
'id' => 'combine' ,
2017-03-04 01:20:24 +00:00
],
];
2009-05-17 11:16:51 +00:00
2017-03-04 01:20:24 +00:00
$data [ 'views' ][ 'dropbutton' ] = [
2012-12-17 22:38:55 +00:00
'title' => t ( 'Dropbutton' ),
'help' => t ( 'Display fields in a dropbutton.' ),
2017-03-04 01:20:24 +00:00
'field' => [
2012-12-17 22:38:55 +00:00
'id' => 'dropbutton' ,
2017-03-04 01:20:24 +00:00
],
];
2012-12-17 22:38:55 +00:00
2019-02-06 17:21:17 +00:00
$data [ 'views' ][ 'display_link' ] = [
'title' => t ( 'Link to display' ),
'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.' ),
'area' => [
'id' => 'display_link' ,
],
];
2013-03-10 07:00:51 +00:00
// Registers an entity area handler per entity type.
2019-05-24 06:44:36 +00:00
foreach ( \Drupal :: entityTypeManager () -> getDefinitions () as $entity_type_id => $entity_type ) {
2014-06-09 14:50:45 +00:00
// Excludes entity types, which cannot be rendered.
2014-02-10 09:24:05 +00:00
if ( $entity_type -> hasViewBuilderClass ()) {
$label = $entity_type -> getLabel ();
2017-03-04 01:20:24 +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' => [
2014-02-10 09:24:05 +00:00
'entity_type' => $entity_type_id ,
2013-03-10 07:00:51 +00:00
'id' => 'entity' ,
2017-03-04 01:20:24 +00:00
],
];
2013-03-10 07:00:51 +00:00
}
}
2014-02-02 20:06:53 +00:00
// Registers an action bulk form per entity.
2019-05-24 06:44:36 +00:00
foreach ( \Drupal :: entityTypeManager () -> getDefinitions () as $entity_type => $entity_info ) {
$actions = array_filter ( \Drupal :: entityTypeManager () -> getStorage ( 'action' ) -> loadMultiple (), function ( ActionConfigEntityInterface $action ) use ( $entity_type ) {
2014-02-02 20:06:53 +00:00
return $action -> getType () == $entity_type ;
});
if ( empty ( $actions )) {
continue ;
}
2017-03-04 01:20:24 +00:00
$data [ $entity_info -> getBaseTable ()][ $entity_type . '_bulk_form' ] = [
2014-02-02 20:06:53 +00:00
'title' => t ( 'Bulk update' ),
'help' => t ( 'Allows users to apply an action to one or more items.' ),
2017-03-04 01:20:24 +00:00
'field' => [
2014-02-02 20:06:53 +00:00
'id' => 'bulk_form' ,
2017-03-04 01:20:24 +00:00
],
];
2014-02-02 20:06:53 +00:00
}
2014-08-14 19:06:59 +00:00
// Registers views data for the entity itself.
2019-05-24 06:44:36 +00:00
foreach ( \Drupal :: entityTypeManager () -> getDefinitions () as $entity_type_id => $entity_type ) {
2014-08-22 11:59:25 +00:00
if ( $entity_type -> hasHandlerClass ( 'views_data' )) {
2014-08-14 19:06:59 +00:00
/** @var \Drupal\views\EntityViewsDataInterface $views_data */
2019-05-24 06:44:36 +00:00
$views_data = \Drupal :: entityTypeManager () -> getHandler ( $entity_type_id , 'views_data' );
2014-08-14 19:06:59 +00:00
$data = NestedArray :: mergeDeep ( $data , $views_data -> getViewsData ());
}
}
2015-01-06 17:20:24 +00:00
// Field modules can implement hook_field_views_data() to override the default
// behavior for adding fields.
$module_handler = \Drupal :: moduleHandler ();
2019-05-24 06:44:36 +00:00
$entity_type_manager = \Drupal :: entityTypeManager ();
if ( $entity_type_manager -> hasDefinition ( 'field_storage_config' )) {
2015-02-10 13:39:31 +00:00
/** @var \Drupal\field\FieldStorageConfigInterface $field_storage */
2019-05-24 06:44:36 +00:00
foreach ( $entity_type_manager -> getStorage ( 'field_storage_config' ) -> loadMultiple () as $field_storage ) {
2015-01-06 17:20:24 +00:00
if ( _views_field_get_entity_type_storage ( $field_storage )) {
2017-03-04 01:20:24 +00:00
$result = ( array ) $module_handler -> invoke ( $field_storage -> getTypeProvider (), 'field_views_data' , [ $field_storage ]);
2015-01-06 17:20:24 +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 ) {
2019-11-14 10:49:28 +00:00
$entity_type_manager = \Drupal :: entityTypeManager ();
if ( ! $entity_type_manager -> hasDefinition ( 'field_storage_config' )) {
2015-01-06 17:20:24 +00:00
return ;
}
2015-02-10 13:39:31 +00:00
/** @var \Drupal\field\FieldStorageConfigInterface $field_storage */
2019-11-14 10:49:28 +00:00
foreach ( $entity_type_manager -> getStorage ( 'field_storage_config' ) -> loadMultiple () as $field_storage ) {
2015-01-06 17:20:24 +00:00
if ( _views_field_get_entity_type_storage ( $field_storage )) {
2015-02-10 13:39:31 +00:00
$function = $field_storage -> getTypeProvider () . '_field_views_data_views_data_alter' ;
2015-01-06 17:20:24 +00:00
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 ;
2019-11-14 10:49:28 +00:00
$entity_type_manager = \Drupal :: entityTypeManager ();
if ( $entity_type_manager -> hasDefinition ( $field_storage -> getTargetEntityTypeId ())) {
$storage = $entity_type_manager -> getStorage ( $field_storage -> getTargetEntityTypeId ());
2015-01-06 17:20:24 +00:00
$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-03-04 01:20:24 +00:00
$label_counter = [];
$all_labels = [];
2015-01-06 17:20:24 +00:00
// Count the amount of fields per label per field storage.
2019-04-26 07:09:28 +00:00
$entity_field_manager = \Drupal :: service ( 'entity_field.manager' );
2019-02-26 15:05:23 +00:00
foreach ( array_keys ( \Drupal :: service ( 'entity_type.bundle.info' ) -> getBundleInfo ( $entity_type )) as $bundle ) {
2019-04-26 07:09:28 +00:00
$bundle_fields = array_filter ( $entity_field_manager -> getFieldDefinitions ( $entity_type , $bundle ), function ( $field_definition ) {
2015-01-06 17:20:24 +00:00
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-03-04 01:20:24 +00:00
return [ $field_name , $all_labels ];
2015-01-06 17:20:24 +00:00
}
// Sort the field labels by it most used label and return the most used one.
2015-10-25 18:35:55 +00:00
// If the counts are equal, sort by the label to ensure the result is
// deterministic.
2017-10-10 14:43:06 +00:00
uksort ( $label_counter , function ( $a , $b ) use ( $label_counter ) {
2015-10-25 18:35:55 +00:00
if ( $label_counter [ $a ] === $label_counter [ $b ]) {
return strcmp ( $a , $b );
}
return $label_counter [ $a ] > $label_counter [ $b ] ? - 1 : 1 ;
});
2015-01-06 17:20:24 +00:00
$label_counter = array_keys ( $label_counter );
2017-03-04 01:20:24 +00:00
return [ $label_counter [ 0 ], $all_labels ];
2015-01-06 17:20:24 +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-03-04 01:20:24 +00:00
$data = [];
2015-01-06 17:20:24 +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-05-27 08:12:26 +00:00
// Ignore custom storage too.
if ( $field_storage -> hasCustomStorage ()) {
return $data ;
}
2015-01-06 17:20:24 +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.
2019-05-24 06:44:36 +00:00
$entity_type_manager = \Drupal :: entityTypeManager ();
2015-01-06 17:20:24 +00:00
$entity_type_id = $field_storage -> getTargetEntityTypeId ();
2019-05-24 06:44:36 +00:00
$entity_type = $entity_type_manager -> getDefinition ( $entity_type_id );
2015-01-06 17:20:24 +00:00
if ( ! $base_table = $entity_type -> getBaseTable ()) {
// We cannot do anything if for some reason there is no base table.
return $data ;
}
2017-03-04 01:20:24 +00:00
$entity_tables = [ $base_table => $entity_type_id ];
2015-01-06 17:20:24 +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
2015-05-24 20:08:46 +00:00
// https://www.drupal.org/node/2079019.
2015-01-06 17:20:24 +00:00
$table_mapping = $storage -> getTableMapping ();
2017-03-04 01:20:24 +00:00
$field_tables = [
EntityStorageInterface :: FIELD_LOAD_CURRENT => [
2015-01-06 17:20:24 +00:00
'table' => $table_mapping -> getDedicatedDataTableName ( $field_storage ),
'alias' => " { $entity_type_id } __ { $field_name } " ,
2017-03-04 01:20:24 +00:00
],
];
2015-01-06 17:20:24 +00:00
if ( $supports_revisions ) {
2017-03-04 01:20:24 +00:00
$field_tables [ EntityStorageInterface :: FIELD_LOAD_REVISION ] = [
2015-01-06 17:20:24 +00:00
'table' => $table_mapping -> getDedicatedRevisionTableName ( $field_storage ),
'alias' => " { $entity_type_id } _revision__ { $field_name } " ,
2017-03-04 01:20:24 +00:00
];
2015-01-06 17:20:24 +00:00
}
Issue #2451657 by Upchuk, tstoeckler, k4v, lokapujya, dawehner, willwh, holist, yobottehg, Peacog, Sharique, plach, Berdir, deepakaryan1988, Xano, geertvd, pminf, alexpott, AkshayKalose, lauriii, mkernel, pritish.kumar, jibran: Views should not condition joins on the langcode of fields that are not translatable
2017-07-03 20:45:44 +00:00
// Determine if the fields are translatable.
$bundles_names = $field_storage -> getBundles ();
$translation_join_type = FALSE ;
$fields = [];
$translatable_configs = [];
$untranslatable_configs = [];
$untranslatable_config_bundles = [];
foreach ( $bundles_names as $bundle ) {
$fields [ $bundle ] = FieldConfig :: loadByName ( $entity_type -> id (), $bundle , $field_name );
}
foreach ( $fields as $bundle => $config_entity ) {
if ( ! empty ( $config_entity )) {
if ( $config_entity -> isTranslatable ()) {
$translatable_configs [ $bundle ] = $config_entity ;
}
else {
$untranslatable_configs [ $bundle ] = $config_entity ;
}
}
else {
// https://www.drupal.org/node/2451657#comment-11462881
\Drupal :: logger ( 'views' ) -> error (
2018-10-12 08:24:57 +00:00
'A non-existent config entity name returned by FieldStorageConfigInterface::getBundles(): entity type: %entity_type, bundle: %bundle, field name: %field' ,
[
'%entity_type' => $entity_type -> id (),
'%bundle' => $bundle ,
'%field' => $field_name ,
]
);
Issue #2451657 by Upchuk, tstoeckler, k4v, lokapujya, dawehner, willwh, holist, yobottehg, Peacog, Sharique, plach, Berdir, deepakaryan1988, Xano, geertvd, pminf, alexpott, AkshayKalose, lauriii, mkernel, pritish.kumar, jibran: Views should not condition joins on the langcode of fields that are not translatable
2017-07-03 20:45:44 +00:00
}
}
// If the field is translatable on all the bundles, there will be a join on
// the langcode.
if ( ! empty ( $translatable_configs ) && empty ( $untranslatable_configs )) {
$translation_join_type = 'language' ;
}
// If the field is translatable only on certain bundles, there will be a join
// on langcode OR bundle name.
elseif ( ! empty ( $translatable_configs ) && ! empty ( $untranslatable_configs )) {
foreach ( $untranslatable_configs as $config ) {
$untranslatable_config_bundles [] = $config -> getTargetBundle ();
}
$translation_join_type = 'language_bundle' ;
}
2015-01-06 17:20:24 +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-03-04 01:20:24 +00:00
$data [ $table_alias ][ 'table' ][ 'join' ][ $data_table ] = [
2016-08-05 16:47:38 +00:00
'table' => $table_mapping -> getDedicatedDataTableName ( $field_storage ),
2015-01-06 17:20:24 +00:00
'left_field' => $entity_type -> getKey ( 'id' ),
'field' => 'entity_id' ,
2017-03-04 01:20:24 +00:00
'extra' => [
[ 'field' => 'deleted' , 'value' => 0 , 'numeric' => TRUE ],
],
];
2015-01-06 17:20:24 +00:00
}
else {
// If there is no data table, just join directly.
2017-03-04 01:20:24 +00:00
$data [ $table_alias ][ 'table' ][ 'join' ][ $base_table ] = [
2016-08-05 16:47:38 +00:00
'table' => $table_mapping -> getDedicatedDataTableName ( $field_storage ),
2015-01-06 17:20:24 +00:00
'left_field' => $entity_type -> getKey ( 'id' ),
'field' => 'entity_id' ,
2017-03-04 01:20:24 +00:00
'extra' => [
[ 'field' => 'deleted' , 'value' => 0 , 'numeric' => TRUE ],
],
];
2015-01-06 17:20:24 +00:00
}
Issue #2451657 by Upchuk, tstoeckler, k4v, lokapujya, dawehner, willwh, holist, yobottehg, Peacog, Sharique, plach, Berdir, deepakaryan1988, Xano, geertvd, pminf, alexpott, AkshayKalose, lauriii, mkernel, pritish.kumar, jibran: Views should not condition joins on the langcode of fields that are not translatable
2017-07-03 20:45:44 +00:00
if ( $translation_join_type === 'language_bundle' ) {
$data [ $table_alias ][ 'table' ][ 'join' ][ $data_table ][ 'join_id' ] = 'field_or_language_join' ;
$data [ $table_alias ][ 'table' ][ 'join' ][ $data_table ][ 'extra' ][] = [
'left_field' => 'langcode' ,
'field' => 'langcode' ,
];
$data [ $table_alias ][ 'table' ][ 'join' ][ $data_table ][ 'extra' ][] = [
'field' => 'bundle' ,
'value' => $untranslatable_config_bundles ,
];
}
elseif ( $translation_join_type === 'language' ) {
$data [ $table_alias ][ 'table' ][ 'join' ][ $data_table ][ 'extra' ][] = [
'left_field' => 'langcode' ,
'field' => 'langcode' ,
];
}
2015-01-06 17:20:24 +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-03-04 01:20:24 +00:00
$data [ $table_alias ][ 'table' ][ 'join' ][ $entity_revision_data_table ] = [
2016-08-05 16:47:38 +00:00
'table' => $table_mapping -> getDedicatedRevisionTableName ( $field_storage ),
2015-01-06 17:20:24 +00:00
'left_field' => $entity_type -> getKey ( 'revision' ),
'field' => 'revision_id' ,
2017-03-04 01:20:24 +00:00
'extra' => [
[ 'field' => 'deleted' , 'value' => 0 , 'numeric' => TRUE ],
],
];
2015-01-06 17:20:24 +00:00
}
else {
// If there is no data table, just join directly.
2017-03-04 01:20:24 +00:00
$data [ $table_alias ][ 'table' ][ 'join' ][ $entity_revision_table ] = [
2016-08-05 16:47:38 +00:00
'table' => $table_mapping -> getDedicatedRevisionTableName ( $field_storage ),
2015-01-06 17:20:24 +00:00
'left_field' => $entity_type -> getKey ( 'revision' ),
'field' => 'revision_id' ,
2017-03-04 01:20:24 +00:00
'extra' => [
[ 'field' => 'deleted' , 'value' => 0 , 'numeric' => TRUE ],
],
];
2015-01-06 17:20:24 +00:00
}
Issue #2451657 by Upchuk, tstoeckler, k4v, lokapujya, dawehner, willwh, holist, yobottehg, Peacog, Sharique, plach, Berdir, deepakaryan1988, Xano, geertvd, pminf, alexpott, AkshayKalose, lauriii, mkernel, pritish.kumar, jibran: Views should not condition joins on the langcode of fields that are not translatable
2017-07-03 20:45:44 +00:00
if ( $translation_join_type === 'language_bundle' ) {
$data [ $table_alias ][ 'table' ][ 'join' ][ $entity_revision_data_table ][ 'join_id' ] = 'field_or_language_join' ;
$data [ $table_alias ][ 'table' ][ 'join' ][ $entity_revision_data_table ][ 'extra' ][] = [
'left_field' => 'langcode' ,
'field' => 'langcode' ,
];
$data [ $table_alias ][ 'table' ][ 'join' ][ $entity_revision_data_table ][ 'extra' ][] = [
'value' => $untranslatable_config_bundles ,
'field' => 'bundle' ,
];
}
elseif ( $translation_join_type === 'language' ) {
$data [ $table_alias ][ 'table' ][ 'join' ][ $entity_revision_data_table ][ 'extra' ][] = [
'left_field' => 'langcode' ,
'field' => 'langcode' ,
];
}
2015-01-06 17:20:24 +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-03-04 01:20:24 +00:00
$add_fields = [ 'delta' , 'langcode' , 'bundle' ];
2015-01-06 17:20:24 +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-03-04 01:20:24 +00:00
$group = t ( '@group (historical data)' , [ '@group' => $group_name ]);
2015-01-06 17:20:24 +00:00
$field_alias = $field_name . '-revision_id' ;
}
2017-03-04 01:20:24 +00:00
$data [ $table_alias ][ $field_alias ] = [
2015-01-06 17:20:24 +00:00
'group' => $group ,
'title' => $label ,
'title short' => $label ,
2017-03-04 01:20:24 +00:00
'help' => t ( 'Appears in: @bundles.' , [ '@bundles' => implode ( ', ' , $bundles_names )]),
];
2015-01-06 17:20:24 +00:00
// Go through and create a list of aliases for all possible combinations of
// entity type + name.
2017-03-04 01:20:24 +00:00
$aliases = [];
$also_known = [];
2015-01-06 17:20:24 +00:00
foreach ( $all_labels as $label_name => $true ) {
if ( $type == EntityStorageInterface :: FIELD_LOAD_CURRENT ) {
if ( $label != $label_name ) {
2017-03-04 01:20:24 +00:00
$aliases [] = [
2015-01-06 17:20:24 +00:00
'base' => $base_table ,
'group' => $group_name ,
'title' => $label_name ,
2017-03-04 01:20:24 +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-01-06 17:20:24 +00:00
}
}
elseif ( $supports_revisions && $label != $label_name ) {
2017-03-04 01:20:24 +00:00
$aliases [] = [
2015-01-06 17:20:24 +00:00
'base' => $table ,
2017-03-04 01:20:24 +00:00
'group' => t ( '@group (historical data)' , [ '@group' => $group_name ]),
2015-01-06 17:20:24 +00:00
'title' => $label_name ,
2017-03-04 01:20:24 +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-01-06 17:20:24 +00:00
}
}
if ( $aliases ) {
$data [ $table_alias ][ $field_alias ][ 'aliases' ] = $aliases ;
2015-09-25 14:32:42 +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.
2015-10-01 23:25:03 +00:00
$data [ $table_alias ][ $field_alias ][ 'help' ] = Markup :: create ( $data [ $table_alias ][ $field_alias ][ 'help' ] . ' ' . t ( 'Also known as:' ) . ' ' . implode ( ', ' , $also_known ));
2015-01-06 17:20:24 +00:00
}
$keys = array_keys ( $field_columns );
$real_field = reset ( $keys );
2017-03-04 01:20:24 +00:00
$data [ $table_alias ][ $field_alias ][ 'field' ] = [
2015-01-06 17:20:24 +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-03-04 01:20:24 +00:00
];
2015-01-06 17:20:24 +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-02-16 11:17:31 +00:00
if ( $field_storage -> getType () == 'boolean' ) {
$filter = 'boolean' ;
}
2015-01-06 17:20:24 +00:00
break ;
2020-06-02 08:46:52 +00:00
2015-01-06 17:20:24 +00:00
case 'blob' :
2019-01-01 12:28:50 +00:00
// It does not make sense to sort by blob.
2015-01-06 17:20:24 +00:00
$allow_sort = FALSE ;
default :
$filter = 'string' ;
$argument = 'string' ;
$sort = 'standard' ;
break ;
}
if ( count ( $field_columns ) == 1 || $column == 'value' ) {
2017-03-04 01:20:24 +00:00
$title = t ( '@label (@name)' , [ '@label' => $label , '@name' => $field_name ]);
2015-01-06 17:20:24 +00:00
$title_short = $label ;
}
else {
2017-03-04 01:20:24 +00:00
$title = t ( '@label (@name:@column)' , [ '@label' => $label , '@name' => $field_name , '@column' => $column ]);
$title_short = t ( '@label:@column' , [ '@label' => $label , '@column' => $column ]);
2015-01-06 17:20:24 +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-03-04 01:20:24 +00:00
$group = t ( '@group (historical data)' , [ '@group' => $group_name ]);
2015-01-06 17:20:24 +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-03-04 01:20:24 +00:00
$data [ $table_alias ][ $column_real_name ] = [
2015-01-06 17:20:24 +00:00
'group' => $group ,
'title' => $title ,
'title short' => $title_short ,
2017-03-04 01:20:24 +00:00
'help' => t ( 'Appears in: @bundles.' , [ '@bundles' => implode ( ', ' , $bundles_names )]),
];
2015-01-06 17:20:24 +00:00
// Go through and create a list of aliases for all possible combinations of
// entity type + name.
2017-03-04 01:20:24 +00:00
$aliases = [];
$also_known = [];
2015-01-06 17:20:24 +00:00
foreach ( $all_labels as $label_name => $true ) {
if ( $label != $label_name ) {
if ( count ( $field_columns ) == 1 || $column == 'value' ) {
2017-03-04 01:20:24 +00:00
$alias_title = t ( '@label (@name)' , [ '@label' => $label_name , '@name' => $field_name ]);
2015-01-06 17:20:24 +00:00
}
else {
2017-03-04 01:20:24 +00:00
$alias_title = t ( '@label (@name:@column)' , [ '@label' => $label_name , '@name' => $field_name , '@column' => $column ]);
2015-01-06 17:20:24 +00:00
}
2017-03-04 01:20:24 +00:00
$aliases [] = [
2015-01-06 17:20:24 +00:00
'group' => $group_name ,
'title' => $alias_title ,
2017-03-04 01:20:24 +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-01-06 17:20:24 +00:00
}
}
if ( $aliases ) {
$data [ $table_alias ][ $column_real_name ][ 'aliases' ] = $aliases ;
2015-09-25 14:32:42 +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.
2015-10-01 23:25:03 +00:00
$data [ $table_alias ][ $column_real_name ][ 'help' ] = Markup :: create ( $data [ $table_alias ][ $column_real_name ][ 'help' ] . ' ' . t ( 'Also known as:' ) . ' ' . implode ( ', ' , $also_known ));
2015-01-06 17:20:24 +00:00
}
2017-03-04 01:20:24 +00:00
$data [ $table_alias ][ $column_real_name ][ 'argument' ] = [
2015-01-06 17:20:24 +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-03-04 01:20:24 +00:00
];
$data [ $table_alias ][ $column_real_name ][ 'filter' ] = [
2015-01-06 17:20:24 +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-03-04 01:20:24 +00:00
];
2015-01-06 17:20:24 +00:00
if ( ! empty ( $allow_sort )) {
2017-03-04 01:20:24 +00:00
$data [ $table_alias ][ $column_real_name ][ 'sort' ] = [
2015-01-06 17:20:24 +00:00
'field' => $column_real_name ,
'table' => $table ,
'id' => $sort ,
'additional fields' => $additional_fields ,
'field_name' => $field_name ,
'entity_type' => $entity_type_id ,
2017-03-04 01:20:24 +00:00
];
2015-01-06 17:20:24 +00:00
}
2015-01-12 03:50:28 +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 ;
}
2015-01-06 17:20:24 +00:00
// Expose additional delta column for multiple value fields.
if ( $field_storage -> isMultiple ()) {
2017-03-04 01:20:24 +00:00
$title_delta = t ( '@label (@name:delta)' , [ '@label' => $label , '@name' => $field_name ]);
$title_short_delta = t ( '@label:delta' , [ '@label' => $label ]);
2015-01-06 17:20:24 +00:00
2017-03-04 01:20:24 +00:00
$data [ $table_alias ][ 'delta' ] = [
2015-01-06 17:20:24 +00:00
'group' => $group ,
'title' => $title_delta ,
'title short' => $title_short_delta ,
2017-03-04 01:20:24 +00:00
'help' => t ( 'Delta - Appears in: @bundles.' , [ '@bundles' => implode ( ', ' , $bundles_names )]),
];
$data [ $table_alias ][ 'delta' ][ 'field' ] = [
2015-01-06 17:20:24 +00:00
'id' => 'numeric' ,
2017-03-04 01:20:24 +00:00
];
$data [ $table_alias ][ 'delta' ][ 'argument' ] = [
2015-01-06 17:20:24 +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-03-04 01:20:24 +00:00
];
$data [ $table_alias ][ 'delta' ][ 'filter' ] = [
2015-01-06 17:20:24 +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-03-04 01:20:24 +00:00
];
$data [ $table_alias ][ 'delta' ][ 'sort' ] = [
2015-01-06 17:20:24 +00:00
'field' => 'delta' ,
'table' => $table ,
'id' => 'standard' ,
'additional fields' => $additional_fields ,
'field_name' => $field_name ,
'entity_type' => $entity_type_id ,
2017-03-04 01:20:24 +00:00
];
2015-01-06 17:20:24 +00:00
}
}
}
return $data ;
}
2015-10-06 11:02:22 +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 ;
}
2019-05-24 06:44:36 +00:00
$entity_type_manager = \Drupal :: entityTypeManager ();
2015-10-06 11:02:22 +00:00
$entity_type_id = $field_storage -> getTargetEntityTypeId ();
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
2019-05-24 06:44:36 +00:00
$table_mapping = $entity_type_manager -> getStorage ( $entity_type_id ) -> getTableMapping ();
2015-10-06 11:02:22 +00:00
foreach ( $data as $table_name => $table_data ) {
// Add a relationship to the target entity type.
$target_entity_type_id = $field_storage -> getSetting ( 'target_type' );
2019-05-24 06:44:36 +00:00
$target_entity_type = $entity_type_manager -> getDefinition ( $target_entity_type_id );
2015-10-06 11:02:22 +00:00
$entity_type_id = $field_storage -> getTargetEntityTypeId ();
2019-05-24 06:44:36 +00:00
$entity_type = $entity_type_manager -> getDefinition ( $entity_type_id );
2015-10-06 11:02:22 +00:00
$target_base_table = $target_entity_type -> getDataTable () ? : $target_entity_type -> getBaseTable ();
$field_name = $field_storage -> getName ();
2020-07-13 12:12:25 +00:00
if ( $target_entity_type instanceof ContentEntityTypeInterface ) {
// Provide a relationship for the entity type with the entity reference
// field.
$args = [
'@label' => $target_entity_type -> getLabel (),
'@field_name' => $field_name ,
];
$data [ $table_name ][ $field_name ][ 'relationship' ] = [
'title' => t ( '@label referenced from @field_name' , $args ),
'label' => t ( '@field_name: @label' , $args ),
'group' => $entity_type -> getLabel (),
'help' => t ( 'Appears in: @bundles.' , [ '@bundles' => implode ( ', ' , $field_storage -> getBundles ())]),
'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' ,
];
2015-10-06 11:02:22 +00:00
2020-07-13 12:12:25 +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 -> getSingularLabel ();
$pseudo_field_name = 'reverse__' . $entity_type_id . '__' . $field_name ;
$data [ $target_base_table ][ $pseudo_field_name ][ 'relationship' ] = [
'title' => t ( '@entity using @field_name' , $args ),
'label' => t ( '@field_name' , [ '@field_name' => $field_name ]),
'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' ,
'join_extra' => [
[
'field' => 'deleted' ,
'value' => 0 ,
'numeric' => TRUE ,
],
2017-03-04 01:20:24 +00:00
],
2020-07-13 12:12:25 +00:00
];
}
2015-10-06 11:02:22 +00:00
}
return $data ;
}