Issue #2477899 by dawehner, kevin.dutra, borisson_, bojanz, catch, amateescu: Multiple valued Base fields won't work in Views

8.2.x
Nathaniel Catchpole 2016-06-06 12:20:41 +01:00
parent 3f52754e42
commit 445bc90175
6 changed files with 308 additions and 7 deletions

View File

@ -0,0 +1,39 @@
<?php
namespace Drupal\entity_test\Entity;
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Defines an entity type with a multivalue base field.
*
* @ContentEntityType(
* id = "entity_test_multivalue_basefield",
* label = @Translation("Entity Test with a multivalue base field"),
* base_table = "entity_test_multivalue_basefield",
* data_table = "entity_test_multivalue_basefield_field_data",
* handlers = {
* "views_data" = "Drupal\views\EntityViewsData",
* },
* entity_keys = {
* "id" = "id",
* "uuid" = "uuid",
* "bundle" = "type",
* "label" = "name",
* "langcode" = "langcode",
* }
* )
*/
class EntityTestMultiValueBasefield extends EntityTest {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = parent::baseFieldDefinitions($entity_type);
$fields['name']->setCardinality(2);
return $fields;
}
}

View File

@ -57,6 +57,13 @@ class EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterfac
*/
protected $fieldStorageDefinitions;
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* Constructs an EntityViewsData object.
*
@ -112,6 +119,7 @@ class EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterfac
$data = [];
$base_table = $this->entityType->getBaseTable() ?: $this->entityType->id();
$views_revision_base_table = NULL;
$revisionable = $this->entityType->isRevisionable();
$base_field = $this->entityType->getKey('id');
@ -235,6 +243,7 @@ class EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterfac
// Load all typed data definitions of all fields. This should cover each of
// the entity base, revision, data tables.
$field_definitions = $this->entityManager->getBaseFieldDefinitions($this->entityType->id());
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
if ($table_mapping = $this->storage->getTableMapping($field_definitions)) {
// Fetch all fields that can appear in both the base table and the data
// table.
@ -257,6 +266,36 @@ class EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterfac
$this->mapFieldDefinition($table, $field_name, $field_definitions[$field_name], $table_mapping, $data[$table]);
}
}
foreach ($field_definitions as $field_definition) {
if ($table_mapping->requiresDedicatedTableStorage($field_definition->getFieldStorageDefinition())) {
$table = $table_mapping->getDedicatedDataTableName($field_definition->getFieldStorageDefinition());
$data[$table]['table']['group'] = $this->entityType->getLabel();
$data[$table]['table']['provider'] = $this->entityType->getProvider();
$data[$table]['table']['join'][$views_base_table] = [
'left_field' => $base_field,
'field' => 'entity_id',
'extra' => [
['field' => 'deleted', 'value' => 0, 'numeric' => TRUE],
],
];
if ($revisionable) {
$revision_table = $table_mapping->getDedicatedRevisionTableName($field_definition->getFieldStorageDefinition());
$data[$revision_table]['table']['group'] = $this->t('@entity_type revision', ['@entity_type' => $this->entityType->getLabel()]);
$data[$revision_table]['table']['provider'] = $this->entityType->getProvider();
$data[$revision_table]['table']['join'][$views_revision_base_table] = [
'left_field' => $revision_field,
'field' => 'entity_id',
'extra' => [
['field' => 'deleted', 'value' => 0, 'numeric' => TRUE],
],
];
}
}
}
}
// Add the entity type key to each table generated.

View File

@ -0,0 +1,45 @@
langcode: en
status: true
dependencies:
module:
- entity_test
id: test_entity_multivalue_basefield
label: ''
module: views
description: ''
tag: ''
base_table: entity_test_multivalue_basefield
base_field: id
core: '8'
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
fields:
id:
id: id
table: entity_test_multivalue_basefield
field: nid
relationship: none
plugin_id: field
entity_type: entity_test_multivalue_basefield
entity_field: id
name:
id: name
table: entity_test_multivalue_basefield__name
field: name
plugin_id: field
entity_type: entity_test_multivalue_basefield
entity_field: name
defaults:
fields: false
filters: false
sorts:
id:
id: id
table: entity_test_multivalue_basefield
field: id
order: asc

View File

@ -0,0 +1,54 @@
<?php
namespace Drupal\Tests\views\Kernel\Entity;
use Drupal\entity_test\Entity\EntityTestMultiValueBasefield;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
use Drupal\views\Views;
/**
* Tests entity views with multivalue base fields.
*
* @group views
*/
class EntityViewsWithMultivalueBasefieldTest extends ViewsKernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['entity_test'];
/**
* {@inheritdoc}
*/
public static $testViews = ['test_entity_multivalue_basefield'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->installEntitySchema('entity_test_multivalue_basefield');
}
/**
* Tests entity views with multivalue base fields.
*/
public function testView() {
EntityTestMultiValueBasefield::create([
'name' => 'test',
])->save();
EntityTestMultiValueBasefield::create([
'name' => ['test2', 'test3'],
])->save();
$view = Views::getView('test_entity_multivalue_basefield');
$view->execute();
$this->assertIdenticalResultset($view, [
['name' => ['test']],
['name' => ['test2', 'test3']],
], ['name' => 'name']);
}
}

View File

@ -11,6 +11,7 @@ use Drupal\Core\Config\Entity\ConfigEntityType;
use Drupal\Core\Entity\ContentEntityType;
use Drupal\Core\Entity\EntityType;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Sql\DefaultTableMapping;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\Core\Field\Plugin\Field\FieldType\IntegerItem;
@ -160,6 +161,12 @@ class EntityViewsDataTest extends UnitTestCase {
->setTranslatable(TRUE)
->setSetting('max_length', 255);
// A base field with cardinality > 1
$base_fields['string'] = BaseFieldDefinition::create('string')
->setLabel('Strong')
->setTranslatable(TRUE)
->setCardinality(2);
foreach ($base_fields as $name => $base_field) {
$base_field->setName($name);
}
@ -376,6 +383,10 @@ class EntityViewsDataTest extends UnitTestCase {
$homepage_field_storage_definition->expects($this->any())
->method('getSchema')
->willReturn(UriItem::schema($homepage_field_storage_definition));
$string_field_storage_definition = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
$string_field_storage_definition->expects($this->any())
->method('getSchema')
->willReturn(StringItem::schema($string_field_storage_definition));
// Setup the user_id entity reference field.
$this->entityManager->expects($this->any())
@ -411,6 +422,7 @@ class EntityViewsDataTest extends UnitTestCase {
'name' => $name_field_storage_definition,
'description' => $description_field_storage_definition,
'homepage' => $homepage_field_storage_definition,
'string' => $string_field_storage_definition,
'user_id' => $user_id_field_storage_definition,
'revision_id' => $revision_id_field_storage_definition,
]);
@ -435,10 +447,12 @@ class EntityViewsDataTest extends UnitTestCase {
['entity_test', $base_field_definitions],
]));
// Setup the table mapping.
$table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
$table_mapping = $this->getMockBuilder(DefaultTableMapping::class)
->disableOriginalConstructor()
->getMock();
$table_mapping->expects($this->any())
->method('getTableNames')
->willReturn(['entity_test']);
->willReturn(['entity_test', 'entity_test__string']);
$table_mapping->expects($this->any())
->method('getColumnNames')
->willReturnMap([
@ -450,12 +464,26 @@ class EntityViewsDataTest extends UnitTestCase {
['description', ['value' => 'description__value', 'format' => 'description__format']],
['homepage', ['value' => 'homepage']],
['user_id', ['target_id' => 'user_id']],
['string', ['value' => 'value']],
]);
$table_mapping->expects($this->any())
->method('getFieldNames')
->willReturnMap([
['entity_test', ['id', 'uuid', 'type', 'langcode', 'name', 'description', 'homepage', 'user_id']]
['entity_test', ['id', 'uuid', 'type', 'langcode', 'name', 'description', 'homepage', 'user_id']],
['entity_test__string', ['string']],
]);
$table_mapping->expects($this->any())
->method('requiresDedicatedTableStorage')
->willReturnCallback(function (BaseFieldDefinition $base_field) {
return $base_field->getName() === 'string';
});
$table_mapping->expects($this->any())
->method('getDedicatedDataTableName')
->willReturnCallback(function (BaseFieldDefinition $base_field) {
if ($base_field->getName() === 'string') {
return 'entity_test__string';
}
});
$this->entityStorage->expects($this->once())
->method('getTableMapping')
@ -492,6 +520,18 @@ class EntityViewsDataTest extends UnitTestCase {
$relationship = $data['entity_test']['user_id']['relationship'];
$this->assertEquals('users_field_data', $relationship['base']);
$this->assertEquals('uid', $relationship['base field']);
$this->assertStringField($data['entity_test__string']['string']);
$this->assertField($data['entity_test__string']['string'], 'string');
$this->assertEquals([
'left_field' => 'id',
'field' => 'entity_id',
'extra' => [[
'field' => 'deleted',
'value' => 0,
'numeric' => TRUE,
]],
], $data['entity_test__string']['table']['join']['entity_test']);
}
/**
@ -529,10 +569,12 @@ class EntityViewsDataTest extends UnitTestCase {
$this->viewsData->setEntityType($entity_type);
// Setup the table mapping.
$table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
$table_mapping = $this->getMockBuilder(DefaultTableMapping::class)
->disableOriginalConstructor()
->getMock();
$table_mapping->expects($this->any())
->method('getTableNames')
->willReturn(['entity_test_mul', 'entity_test_mul_property_data']);
->willReturn(['entity_test_mul', 'entity_test_mul_property_data', 'entity_test_mul__string']);
$table_mapping->expects($this->any())
->method('getColumnNames')
->willReturnMap([
@ -544,12 +586,14 @@ class EntityViewsDataTest extends UnitTestCase {
['description', ['value' => 'description__value', 'format' => 'description__format']],
['homepage', ['value' => 'homepage']],
['user_id', ['target_id' => 'user_id']],
['string', ['value' => 'value']],
]);
$table_mapping->expects($this->any())
->method('getFieldNames')
->willReturnMap([
['entity_test_mul', ['uuid']],
['entity_test_mul_property_data', ['id', 'type', 'langcode', 'name', 'description', 'homepage', 'user_id']],
['entity_test_mul__string', ['string']],
]);
$table_mapping->expects($this->any())
@ -560,6 +604,18 @@ class EntityViewsDataTest extends UnitTestCase {
}
return 'entity_test_mul_property_data';
});
$table_mapping->expects($this->any())
->method('requiresDedicatedTableStorage')
->willReturnCallback(function (BaseFieldDefinition $base_field) {
return $base_field->getName() === 'string';
});
$table_mapping->expects($this->any())
->method('getDedicatedDataTableName')
->willReturnCallback(function (BaseFieldDefinition $base_field) {
if ($base_field->getName() === 'string') {
return 'entity_test_mul__string';
}
});
$this->entityStorage->expects($this->once())
->method('getTableMapping')
@ -619,6 +675,18 @@ class EntityViewsDataTest extends UnitTestCase {
$relationship = $data['entity_test_mul_property_data']['user_id']['relationship'];
$this->assertEquals('users_field_data', $relationship['base']);
$this->assertEquals('uid', $relationship['base field']);
$this->assertStringField($data['entity_test_mul__string']['string']);
$this->assertField($data['entity_test_mul__string']['string'], 'string');
$this->assertEquals([
'left_field' => 'id',
'field' => 'entity_id',
'extra' => [[
'field' => 'deleted',
'value' => 0,
'numeric' => TRUE,
]],
], $data['entity_test_mul__string']['table']['join']['entity_test_mul']);
}
/**
@ -650,10 +718,12 @@ class EntityViewsDataTest extends UnitTestCase {
$this->viewsData->setEntityType($entity_type);
// Setup the table mapping.
$table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
$table_mapping = $this->getMockBuilder(DefaultTableMapping::class)
->disableOriginalConstructor()
->getMock();
$table_mapping->expects($this->any())
->method('getTableNames')
->willReturn(['entity_test_mulrev', 'entity_test_mulrev_revision', 'entity_test_mulrev_property_data', 'entity_test_mulrev_property_revision']);
->willReturn(['entity_test_mulrev', 'entity_test_mulrev_revision', 'entity_test_mulrev_property_data', 'entity_test_mulrev_property_revision', 'entity_test_mulrev__string', 'entity_test_mulrev_revision__string']);
$table_mapping->expects($this->any())
->method('getColumnNames')
->willReturnMap([
@ -666,6 +736,7 @@ class EntityViewsDataTest extends UnitTestCase {
['homepage', ['value' => 'homepage']],
['user_id', ['target_id' => 'user_id']],
['revision_id', ['value' => 'id']],
['string', ['value' => 'value']],
]);
$table_mapping->expects($this->any())
->method('getFieldNames')
@ -674,7 +745,29 @@ class EntityViewsDataTest extends UnitTestCase {
['entity_test_mulrev_revision', ['id', 'revision_id', 'langcode']],
['entity_test_mulrev_property_data', ['id', 'revision_id', 'langcode', 'name', 'description', 'homepage', 'user_id']],
['entity_test_mulrev_property_revision', ['id', 'revision_id', 'langcode', 'name', 'description', 'homepage', 'user_id']],
['entity_test_mulrev__string', ['string']],
['entity_test_mulrev_revision__string', ['string']],
]);
$table_mapping->expects($this->any())
->method('requiresDedicatedTableStorage')
->willReturnCallback(function (BaseFieldDefinition $base_field) {
return $base_field->getName() === 'string';
});
$table_mapping->expects($this->any())
->method('getDedicatedDataTableName')
->willReturnCallback(function (BaseFieldDefinition $base_field) {
if ($base_field->getName() === 'string') {
return 'entity_test_mulrev__string';
}
});
$table_mapping->expects($this->any())
->method('getDedicatedRevisionTableName')
->willReturnCallback(function (BaseFieldDefinition $base_field) {
if ($base_field->getName() === 'string') {
return 'entity_test_mulrev_revision__string';
}
});
$table_mapping->expects($this->any())
->method('getFieldTableName')
@ -767,6 +860,30 @@ class EntityViewsDataTest extends UnitTestCase {
$relationship = $data['entity_test_mulrev_property_revision']['user_id']['relationship'];
$this->assertEquals('users_field_data', $relationship['base']);
$this->assertEquals('uid', $relationship['base field']);
$this->assertStringField($data['entity_test_mulrev__string']['string']);
$this->assertField($data['entity_test_mulrev__string']['string'], 'string');
$this->assertEquals([
'left_field' => 'id',
'field' => 'entity_id',
'extra' => [[
'field' => 'deleted',
'value' => 0,
'numeric' => TRUE,
]],
], $data['entity_test_mulrev__string']['table']['join']['entity_test_mulrev_property_data']);
$this->assertStringField($data['entity_test_mulrev_revision__string']['string']);
$this->assertField($data['entity_test_mulrev_revision__string']['string'], 'string');
$this->assertEquals([
'left_field' => 'revision_id',
'field' => 'entity_id',
'extra' => [[
'field' => 'deleted',
'value' => 0,
'numeric' => TRUE,
]],
], $data['entity_test_mulrev_revision__string']['table']['join']['entity_test_mulrev_property_revision']);
}
/**

View File

@ -360,6 +360,13 @@ function views_update_8005() {
// Empty update function to rebuild the views data.
}
/**
* Clear caches due to updated entity views data.
*/
function views_update_8100() {
// Empty update to cause a cache flush so that views data is rebuilt.
}
/**
* @} End of "addtogroup updates-8.1.0".
*/