Commit 61de3bdd authored by alexpott's avatar alexpott

Issue #2320743 by jhodgdon: Taxonomy views needs filter/field on original language.

parent 5385c228
......@@ -7,27 +7,21 @@
namespace Drupal\taxonomy;
use Drupal\views\EntityViewsDataInterface;
use Drupal\views\EntityViewsData;
/**
* Provides the views data for the taxonomy entity type.
*/
class TermViewsData implements EntityViewsDataInterface {
class TermViewsData extends EntityViewsData {
/**
* {@inheritdoc}
*/
public function getViewsData() {
$data = array();
$data['taxonomy_term_data']['table']['group'] = t('Taxonomy term');
$data['taxonomy_term_data']['table']['base'] = array(
'field' => 'tid',
'title' => t('Term'),
'help' => t('Taxonomy terms are attached to nodes.'),
'access query tag' => 'term_access',
);
$data['taxonomy_term_data']['table']['entity type'] = 'taxonomy_term';
$data = parent::getViewsData();
$data['taxonomy_term_data']['table']['base']['help'] = t('Taxonomy terms are attached to nodes.');
$data['taxonomy_term_data']['table']['base']['access query tag'] = 'term_access';
$data['taxonomy_term_data']['table']['wizard_id'] = 'taxonomy_term';
$data['taxonomy_term_data']['table']['join'] = array(
......@@ -38,36 +32,17 @@ public function getViewsData() {
),
);
$data['taxonomy_term_field_data']['table']['group'] = t('Taxonomy term');
$data['taxonomy_term_field_data']['table']['entity type'] = 'taxonomy_term';
$data['taxonomy_term_field_data']['table']['join']['taxonomy_term_data'] = array(
'type' => 'INNER',
'left_field' => 'tid',
'field' => 'tid',
);
$data['taxonomy_term_data']['tid']['help'] = t('The tid of a taxonomy term.');
$data['taxonomy_term_data']['tid'] = array(
'title' => t('Term ID'),
'help' => t('The tid of a taxonomy term.'),
'field' => array(
'id' => 'numeric',
),
'sort' => array(
'id' => 'standard',
),
'argument' => array(
'id' => 'taxonomy',
'name field' => 'name',
'zero is null' => TRUE,
),
'filter' => array(
'title' => t('Term'),
'help' => t('Taxonomy term chosen from autocomplete or select widget.'),
'id' => 'taxonomy_index_tid',
'hierarchy table' => 'taxonomy_term_hierarchy',
'numeric' => TRUE,
),
);
$data['taxonomy_term_data']['tid']['argument']['id'] = 'taxonomy';
$data['taxonomy_term_data']['tid']['argument']['name field'] = 'name';
$data['taxonomy_term_data']['tid']['argument']['zero is null'] = TRUE;
$data['taxonomy_term_data']['tid']['filter']['id'] = 'taxonomy_index_tid';
$data['taxonomy_term_data']['tid']['filter']['title'] = t('Term');
$data['taxonomy_term_data']['tid']['filter']['help'] = t('Taxonomy term chosen from autocomplete or select widget.');
$data['taxonomy_term_data']['tid']['filter']['hierarchy table'] = 'taxonomy_term_hierarchy';
$data['taxonomy_term_data']['tid']['filter']['numeric'] = TRUE;
$data['taxonomy_term_data']['tid_raw'] = array(
'title' => t('Term ID'),
......@@ -86,7 +61,7 @@ public function getViewsData() {
'help' => t('Obtains a single representative node for each term, according to a chosen sort criterion.'),
'id' => 'groupwise_max',
'relationship field' => 'tid',
'outer field' => 'taxonomy_term_data.tid',
'outer field' => 'taxonomy_term_field_data.tid',
'argument table' => 'taxonomy_term_field_data',
'argument field' => 'tid',
'base' => 'node',
......@@ -95,81 +70,10 @@ public function getViewsData() {
),
);
$data['taxonomy_term_field_data']['name'] = array(
'title' => t('Name'),
'help' => t('The taxonomy term name.'),
'field' => array(
'id' => 'taxonomy',
),
'sort' => array(
'id' => 'standard',
),
'filter' => array(
'id' => 'string',
'help' => t('Taxonomy term name.'),
),
'argument' => array(
'id' => 'string',
'help' => t('Taxonomy term name.'),
'many to one' => TRUE,
'empty field name' => t('Uncategorized'),
),
);
$data['taxonomy_term_field_data']['weight'] = array(
'title' => t('Weight'),
'help' => t('The term weight field'),
'field' => array(
'id' => 'numeric',
),
'sort' => array(
'id' => 'standard',
),
'filter' => array(
'id' => 'numeric',
),
'argument' => array(
'id' => 'numeric',
),
);
$data['taxonomy_term_field_data']['description__value'] = array(
'title' => t('Term description'),
'help' => t('The description associated with a taxonomy term.'),
'field' => array(
'id' => 'markup',
'format' => array('field' => 'description__format'),
'click sortable' => FALSE,
),
'filter' => array(
'id' => 'string',
),
);
$data['taxonomy_term_data']['vid'] = array(
'title' => t('Vocabulary'),
'help' => t('Filter the results of "Taxonomy: Term" to a particular vocabulary.'),
'filter' => array(
'id' => 'bundle',
),
);
$data['taxonomy_term_field_data']['langcode'] = array(
'title' => t('Language'), // The item it appears as on the UI,
'help' => t('Language of the term'),
'field' => array(
'id' => 'taxonomy_term_language',
),
'sort' => array(
'id' => 'standard',
),
'filter' => array(
'id' => 'language',
),
'argument' => array(
'id' => 'language',
),
);
$data['taxonomy_term_data']['vid']['help'] = t('Filter the results of "Taxonomy: Term" to a particular vocabulary.');
unset($data['taxonomy_term_data']['vid']['field']);
unset($data['taxonomy_term_data']['vid']['argument']);
unset($data['taxonomy_term_data']['vid']['sort']);
$data['taxonomy_term_data']['edit_term'] = array(
'field' => array(
......@@ -180,19 +84,26 @@ public function getViewsData() {
),
);
$data['taxonomy_term_field_data']['changed'] = array(
'title' => t('Updated date'),
'help' => t('The date the term was last updated.'),
'field' => array(
'id' => 'date',
),
'sort' => array(
'id' => 'date'
),
'filter' => array(
'id' => 'date',
),
);
if (\Drupal::moduleHandler()->moduleExists('content_translation')) {
$data['taxonomy_term_data']['translation_link'] = array(
'title' => t('Translation link'),
'help' => t('Provide a link to the translations overview for taxonomy terms.'),
'field' => array(
'id' => 'content_translation_link',
),
);
}
$data['taxonomy_term_field_data']['name']['field']['id'] = 'taxonomy';
$data['taxonomy_term_field_data']['name']['argument']['many to one'] = TRUE;
$data['taxonomy_term_field_data']['name']['argument']['empty field name'] = t('Uncategorized');
$data['taxonomy_term_field_data']['description__value']['field']['click sortable'] = FALSE;
$data['taxonomy_term_field_data']['langcode']['field']['id'] = 'taxonomy_term_language';
$data['taxonomy_term_field_data']['changed']['title'] = t('Updated date');
$data['taxonomy_term_field_data']['changed']['help'] = t('The date the term was last updated.');
$data['taxonomy_term_field_data']['changed_fulldate'] = array(
'title' => t('Updated date'),
......@@ -248,16 +159,6 @@ public function getViewsData() {
),
);
if (\Drupal::moduleHandler()->moduleExists('content_translation')) {
$data['taxonomy_term_data']['translation_link'] = array(
'title' => t('Translation link'),
'help' => t('Provide a link to the translations overview for taxonomy terms.'),
'field' => array(
'id' => 'content_translation_link',
),
);
}
$data['taxonomy_index']['table']['group'] = t('Taxonomy term');
$data['taxonomy_index']['table']['join'] = array(
......@@ -314,30 +215,30 @@ public function getViewsData() {
);
$data['taxonomy_index']['sticky'] = [
'title' => t('Sticky status'),
'help' => t('Whether or not the content related to a term is sticky.'),
'filter' => [
'id' => 'boolean',
'label' => t('Sticky status'),
'type' => 'yes-no',
],
'sort' => [
'id' => 'standard',
'help' => t('Whether or not the content related to a term is sticky. To list sticky content first, set this to descending.'),
],
];
$data['taxonomy_index']['created'] = [
'title' => t('Post date'),
'help' => t('The date the content related to a term was posted.'),
'sort' => [
'id' => 'date'
],
'filter' => [
'id' => 'date',
],
];
$data['taxonomy_index']['sticky'] = [
'title' => t('Sticky status'),
'help' => t('Whether or not the content related to a term is sticky.'),
'filter' => [
'id' => 'boolean',
'label' => t('Sticky status'),
'type' => 'yes-no',
],
'sort' => [
'id' => 'standard',
'help' => t('Whether or not the content related to a term is sticky. To list sticky content first, set this to descending.'),
],
];
$data['taxonomy_index']['created'] = [
'title' => t('Post date'),
'help' => t('The date the content related to a term was posted.'),
'sort' => [
'id' => 'date'
],
'filter' => [
'id' => 'date',
],
];
$data['taxonomy_term_hierarchy']['table']['group'] = t('Taxonomy term');
......@@ -377,4 +278,3 @@ public function getViewsData() {
}
}
......@@ -225,23 +225,17 @@ protected function mapFieldDefinition($table, $field_name, FieldDefinitionInterf
$field_schema = $this->getFieldStorageDefinitions()[$field_name]->getSchema();
$field_definition_type = $field_definition->getType();
// Add all properties to views table data.
// Add all properties to views table data. We need an entry for each
// column of each field, with the first one given special treatment.
// @todo Introduce concept of the "main" column for a field, rather than
// assuming the first one is the main column. See also what the
// mapSingleFieldViewsData() method does with $first.
$multiple = (count($field_column_mapping) > 1);
$first = TRUE;
foreach ($field_column_mapping as $field_column_name => $schema_field_name) {
$schema = $field_schema['columns'][$field_column_name];
// We want to both have an entry in the views data for the actual field,
// but also each additional schema field, for example the file
// description.
// @todo Introduce a concept of the "main" schema field for a field item.
// This would be the FID for a file reference for example.
// @see https://www.drupal.org/node/2337517
if ($first) {
$first = FALSE;
$table_data[$field_name] = $this->mapSingleFieldViewsData($table, $field_definition_type, $schema_field_name, $field_definition, TRUE);
}
else {
$table_data["$field_name.$field_column_name"] = $this->mapSingleFieldViewsData($table, $schema['type'], $schema_field_name, $field_definition, FALSE);
}
$views_field_name = ($multiple) ? $field_name . '__' . $field_column_name : $field_name;
$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);
$first = FALSE;
}
}
......@@ -250,113 +244,119 @@ protected function mapFieldDefinition($table, $field_name, FieldDefinitionInterf
*
* @param string $table
* The table of the field to handle.
* @param string $data_type
* The data type to generate views data for, for example "int". The data
* type comes directly from the schema definition of each field item.
* @param string $schema_field_name
* The schema field name.
* @param string $field_name
* The machine name of the field being processed.
* @param string $field_type
* The type of field being handled.
* @param string $column_name
* For fields containing multiple columns, the column name being processed.
* @param string $column_type
* Within the field, the column type being handled.
* @param bool $first
* TRUE if this is the first column within the field.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition.
* @param bool $first
* Is it the first column of the schema.
*
* @return array
* The modified views data field definition.
*/
protected function mapSingleFieldViewsData($table, $data_type, $schema_field_name, FieldDefinitionInterface $field_definition, $first) {
protected function mapSingleFieldViewsData($table, $field_name, $field_type, $column_name, $column_type, $first, FieldDefinitionInterface $field_definition) {
$views_field = array();
// Provide a nicer, less verbose label for the first field.
// Provide a nicer, less verbose label for the first column within a field.
// @todo Introduce concept of the "main" column for a field, rather than
// assuming the first one is the main column.
if ($first) {
$views_field['title'] = $field_definition->getLabel();
}
else {
$views_field['title'] = $field_definition->getLabel() . " ($schema_field_name)";
$views_field['title'] = $field_definition->getLabel() . " ($column_name)";
}
if ($description = $field_definition->getDescription()) {
$views_field['help'] = $description;
}
// Set up the field, sort, argument, and filters, based on
// the column and/or field data type.
// @todo Allow field types to customize this.
// @see https://www.drupal.org/node/2337515
switch ($data_type) {
case 'int':
case 'integer':
case 'smallint':
case 'tinyint':
case 'mediumint':
case 'float':
case 'double':
case 'decimal':
$views_field['field']['id'] = 'numeric';
$views_field['argument']['id'] = 'numeric';
$views_field['filter']['id'] = 'numeric';
$views_field['sort']['id'] = 'standard';
break;
case 'char':
case 'string':
case 'varchar':
case 'tinytext':
case 'text':
case 'mediumtext':
case 'longtext':
$views_field['field']['id'] = 'standard';
$views_field['argument']['id'] = 'string';
$views_field['filter']['id'] = 'string';
$views_field['sort']['id'] = 'standard';
break;
case 'boolean':
$views_field['field']['id'] = 'boolean';
$views_field['argument']['id'] = 'numeric';
$views_field['filter']['id'] = 'boolean';
$views_field['sort']['id'] = 'standard';
break;
case 'uuid':
$views_field['field']['id'] = 'standard';
$views_field['argument']['id'] = 'string';
$views_field['filter']['id'] = 'string';
$views_field['sort']['id'] = 'standard';
switch ($field_type) {
// Special case a few field types.
case 'timestamp':
case 'created':
case 'changed':
$views_field['field']['id'] = 'date';
$views_field['argument']['id'] = 'date';
$views_field['filter']['id'] = 'date';
$views_field['sort']['id'] = 'date';
break;
case 'language':
$views_field['field']['id'] = 'language';
$views_field['argument']['id'] = 'language';
$views_field['filter']['id'] = 'language';
$views_field['sort']['id'] = 'standard';
break;
case 'created':
case 'changed':
$views_field['field']['id'] = 'date';
$views_field['argument']['id'] = 'date';
$views_field['filter']['id'] = 'date';
$views_field['sort']['id'] = 'date';
break;
case 'entity_reference':
// @todo Should the actual field handler respect that this is just renders a number
// @todo Create an optional entity field handler, that can render the
// entity.
// @see https://www.drupal.org/node/2322949
$views_field['field']['id'] = 'standard';
$views_field['argument']['id'] = 'standard';
$views_field['filter']['id'] = 'standard';
$views_field['sort']['id'] = 'standard';
break;
case 'uri':
$views_field['field']['id'] = 'standard';
$views_field['argument']['id'] = 'string';
$views_field['filter']['id'] = 'string';
case 'boolean':
$views_field['field']['id'] = 'boolean';
$views_field['argument']['id'] = 'numeric';
$views_field['filter']['id'] = 'boolean';
$views_field['sort']['id'] = 'standard';
break;
case 'text':
case 'text_with_summary':
// Treat these three long text fields the same.
$field_type = 'text_long';
// Intentional fall-through here to the default processing!
default:
$views_field['field']['id'] = 'standard';
$views_field['argument']['id'] = 'standard';
$views_field['filter']['id'] = 'standard';
$views_field['sort']['id'] = 'standard';
// For most fields, the field type is generic enough to just use
// the column type to determine the filters etc.
switch ($column_type) {
case 'int':
case 'integer':
case 'smallint':
case 'tinyint':
case 'mediumint':
case 'float':
case 'double':
case 'decimal':
$views_field['field']['id'] = 'numeric';
$views_field['argument']['id'] = 'numeric';
$views_field['filter']['id'] = 'numeric';
$views_field['sort']['id'] = 'standard';
break;
case 'char':
case 'string':
case 'varchar':
case 'tinytext':
case 'text':
case 'mediumtext':
case 'longtext':
$views_field['field']['id'] = 'standard';
$views_field['argument']['id'] = 'string';
$views_field['filter']['id'] = 'string';
$views_field['sort']['id'] = 'standard';
break;
default:
$views_field['field']['id'] = 'standard';
$views_field['argument']['id'] = 'standard';
$views_field['filter']['id'] = 'standard';
$views_field['sort']['id'] = 'standard';
}
}
$process_method = 'processViewsDataFor' . Container::camelize($data_type);
// Do post-processing for a few field types.
$process_method = 'processViewsDataFor' . Container::camelize($field_type);
if (method_exists($this, $process_method)) {
$this->{$process_method}($table, $field_definition, $views_field);
$this->{$process_method}($table, $field_definition, $views_field, $column_name);
}
return $views_field;
......@@ -371,8 +371,10 @@ protected function mapSingleFieldViewsData($table, $data_type, $schema_field_nam
* The field definition.
* @param array $views_field
* The views field data.
* @param string $field_column_name
* The field column being processed.
*/
protected function processViewsDataForLanguage($table, FieldDefinitionInterface $field_definition, array &$views_field) {
protected function processViewsDataForLanguage($table, FieldDefinitionInterface $field_definition, array &$views_field, $field_column_name) {
// Apply special titles for the langcode field.
if ($field_definition->getName() == 'langcode') {
if ($table == $this->entityType->getDataTable() || $table == $this->entityType->getRevisionDataTable()) {
......@@ -393,8 +395,17 @@ protected function processViewsDataForLanguage($table, FieldDefinitionInterface
* The field definition.
* @param array $views_field
* The views field data.
* @param string $field_column_name
* The field column being processed.
*/
protected function processViewsDataForEntityReference($table, FieldDefinitionInterface $field_definition, array &$views_field) {
protected function processViewsDataForEntityReference($table, FieldDefinitionInterface $field_definition, array &$views_field, $field_column_name) {
// @todo Should the actual field handler respect that this just renders a
// number?
// @todo Create an optional entity field handler, that can render the
// entity.
// @see https://www.drupal.org/node/2322949
if ($entity_type_id = $field_definition->getItemDefinition()->getSetting('target_type')) {
$entity_type = $this->entityManager->getDefinition($entity_type_id);
if ($entity_type instanceof ContentEntityType) {
......@@ -425,6 +436,26 @@ protected function processViewsDataForEntityReference($table, FieldDefinitionInt
}
}
/**
* Processes the views data for a text field with formatting.
*
* @param string $table
* The table the field is added to.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition.
* @param array $views_field
* The views field data.
* @param string $field_column_name
* The field column being processed.
*/
protected function processViewsDataForTextLong($table, FieldDefinitionInterface $field_definition, array &$views_field, $field_column_name) {
// Connect the text field to its formatter.
if ($field_column_name == 'value') {
$views_field['field']['format'] = $field_definition->getName() . '__format';
$views_field['field']['id'] = 'markup';
}
}
/**
* Gets the table of an entity type to be used as base table in views.
*
......
......@@ -17,6 +17,7 @@
use Drupal\Core\Field\Plugin\Field\FieldType\LanguageItem;
use Drupal\Core\Field\Plugin\Field\FieldType\StringItem;
use Drupal\Core\Field\Plugin\Field\FieldType\UuidItem;
use Drupal\text\Plugin\Field\FieldType\TextLongItem;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\entity_test\Entity\EntityTestMul;
use Drupal\entity_test\Entity\EntityTestMulRev;
......@@ -119,6 +120,24 @@ protected function setUp() {
* The setup base fields.
*/
protected function setupBaseFields(array $base_fields) {
// Add a description field to the fields supplied by the EntityTest
// classes. This example comes from the taxonomy Term entity.
$base_fields['description'] = BaseFieldDefinition::create('text_long')
->setLabel(t('Description'))
->setDescription(t('A description of the term.'))
->setTranslatable(TRUE)
->setDisplayOptions('view', array(
'label' => 'hidden',
'type' => 'text_default',
'weight' => 0,
))
->setDisplayConfigurable('view', TRUE)
->setDisplayOptions('form', array(
'type' => 'text_textfield',
'weight' => 0,
))
->setDisplayConfigurable('form', TRUE);
foreach ($base_fields as $name => $base_field) {
$base_field->setName($name);
}
......@@ -223,6 +242,10 @@ protected function setupFieldStorageDefinition() {
$name_field_storage_definition->expects($this->any())
->method('getSchema')
->willReturn(StringItem::schema($name_field_storage_definition));
$description_field_storage_definition = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
$description_field_storage_definition->expects($this->any())
->method('getSchema')
->willReturn(TextLongItem::schema($description_field_storage_definition));
// Setup the user_id entity reference field.
$this->entityManager->expects($this->any())
......@@ -253,6 +276,7 @@ protected function setupFieldStorageDefinition() {
'type' => $type_field_storage_definition,
'langcode' => $langcode_field_storage_definition,
'name' => $name_field_storage_definition,
'description' => $description_field_storage_definition,
'user_id' => $user_id_field_storage_definition,
'revision_id' => $revision_id_field_storage_definition,
]);
......@@ -263,6 +287,7 @@ protected function setupFieldStorageDefinition() {
*/
public function testBaseTableFields() {
$base_field_definitions = $this->setupBaseFields(EntityTest::baseFieldDefinitions($this->baseEntityType));
$this->entityManager->expects($this->once())
->method('getBaseFieldDefinitions')
->with('entity_test')
......@@ -281,12 +306,13 @@ public function testBaseTableFields() {
['type', ['value' => 'type']],
['langcode', ['value' => 'langcode']],
['name', ['value' => 'name']],
['description', ['value' => 'description__value', 'format' => 'description__format']],
['user_id', ['target_id' => 'user_id']],
]);
$table_mapping->expects($this->any())
->method('getFieldNames')
->willReturnMap([
['entity_test', ['id', 'uuid', 'type', 'langcode', 'name', 'user_id']]
['entity_test', ['id', 'uuid', 'type', 'langcode', 'name', 'description', 'user_id']]
]);
$this->entityStorage->expects($this->once())
......@@ -295,7 +321,6 @@ public function testBaseTableFields() {
$this->setupFieldStorageDefinition();
$this->viewsData->setSchemaFields(['entity_test' => ['id', 'uuid', 'type', 'langcode', 'name', 'user_id']]);
$data = $this->viewsData->getViewsData();
$this->assertNumericField($data['entity_test']['id']);
......@@ -307,6 +332,8 @@ public function testBaseTableFields() {
$this->assertStringField($data['entity_test']['name']);
$this->assertLongTextField($data['entity_test'], 'description');
$this->assertEntityReferenceField($data['entity_test']['user_id']);
$relationship = $data['entity_test']['user_id']['relationship'];
$this->assertEquals('users', $relationship['base']);
......@@ -343,12 +370,6 @@ public function testDataTableFields() {
->with('entity_test_mul')
->willReturn($base_field_definitions);
$this->viewsData->setSchemaFields([
'entity_test_mul' => ['id'