diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php
index 37ed343..5d7145e 100644
--- a/core/lib/Drupal/Core/Entity/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/EntityType.php
@@ -306,6 +306,14 @@ public function hasKey($key) {
/**
* {@inheritdoc}
*/
+ public function setKey($key, $value) {
+ $this->entity_keys[$key] = $value;
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function id() {
return $this->id;
}
diff --git a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
index a1c1bc9..d23e15c 100644
--- a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
@@ -141,6 +141,18 @@ public function getKey($key);
public function hasKey($key);
/**
+ * Sets a specific entity key.
+ *
+ * @param string $key
+ * The name of the entity key.
+ * @param string $value
+ * The new value of the key.
+ *
+ * @return $this
+ */
+ public function setKey($key, $value);
+
+ /**
* Indicates whether entities should be statically cached.
*
* @return bool
diff --git a/core/lib/Drupal/Core/Entity/Schema/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Schema/SqlContentEntityStorageSchema.php
index 830d2ab..9bbde56 100644
--- a/core/lib/Drupal/Core/Entity/Schema/SqlContentEntityStorageSchema.php
+++ b/core/lib/Drupal/Core/Entity/Schema/SqlContentEntityStorageSchema.php
@@ -26,7 +26,7 @@ class SqlContentEntityStorageSchema implements EntitySchemaHandlerInterface {
/**
* The storage field definitions for this entity type.
*
- * @var \Drupal\Core\Field\FieldDefinitionInterface[]
+ * @var \Drupal\Core\Field\FieldStorageDefinitionInterface[]
*/
protected $fieldStorageDefinitions;
diff --git a/core/modules/comment/src/Tests/CommentTestBase.php b/core/modules/comment/src/Tests/CommentTestBase.php
index 05f139d..bb4e55e 100644
--- a/core/modules/comment/src/Tests/CommentTestBase.php
+++ b/core/modules/comment/src/Tests/CommentTestBase.php
@@ -82,7 +82,7 @@ protected function setUp() {
$this->container->get('comment.manager')->addDefaultField('node', 'article');
// Create a test node authored by the web user.
- $this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'uid' => $this->web_user->id()));
+ $this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'uid' => $this->web_user->id(), 'langcode' => 'en'));
}
/**
diff --git a/core/modules/contextual/src/Tests/ContextualDynamicContextTest.php b/core/modules/contextual/src/Tests/ContextualDynamicContextTest.php
index 61fa85e..c4150f3 100644
--- a/core/modules/contextual/src/Tests/ContextualDynamicContextTest.php
+++ b/core/modules/contextual/src/Tests/ContextualDynamicContextTest.php
@@ -50,9 +50,9 @@ function testDifferentPermissions() {
// - An article, which should be user-editable.
// - A page, which should not be user-editable.
// - A second article, which should also be user-editable.
- $node1 = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1));
- $node2 = $this->drupalCreateNode(array('type' => 'page', 'promote' => 1));
- $node3 = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1));
+ $node1 = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'langcode' => 'en'));
+ $node2 = $this->drupalCreateNode(array('type' => 'page', 'promote' => 1, 'langcode' => 'en'));
+ $node3 = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'langcode' => 'en'));
// Now, on the front page, all article nodes should have contextual links
// placeholders, as should the view that contains them.
diff --git a/core/modules/file/src/FileViewsData.php b/core/modules/file/src/FileViewsData.php
index 1c6e524..5a977a2 100644
--- a/core/modules/file/src/FileViewsData.php
+++ b/core/modules/file/src/FileViewsData.php
@@ -7,118 +7,47 @@
namespace Drupal\file;
-use Drupal\views\EntityViewsDataInterface;
+use Drupal\views\EntityViewsData;
/**
* Provides views data for the file entity type.
*/
-class FileViewsData implements EntityViewsDataInterface {
+class FileViewsData extends EntityViewsData {
/**
* {@inheritdoc}
*/
public function getViewsData() {
- $data = array();
- // Sets 'group' index for file_managed table.
- $data['file_managed']['table']['group'] = t('File');
+ $data = parent::getViewsData();
- // Advertise this table as a possible base table.
- $data['file_managed']['table']['base'] = array(
- 'field' => 'fid',
- 'title' => t('File'),
- 'help' => t("Files maintained by Drupal and various modules."),
- 'defaults' => array(
- 'field' => 'filename'
- ),
- );
- $data['file_managed']['table']['entity type'] = 'file';
+ // @TODO There is no corresponding information in entity metadata.
+ $data['file_managed']['table']['base']['help'] = t('Files maintained by Drupal and various modules.');
+ $data['file_managed']['table']['base']['defaults']['field'] = 'filename';
$data['file_managed']['table']['wizard_id'] = 'file_managed';
- // Describes fid field in file_managed table.
- $data['file_managed']['fid'] = array(
- 'title' => t('File ID'),
- 'help' => t('The ID of the file.'),
- 'field' => array(
- 'id' => 'file',
- ),
- 'argument' => array(
- 'id' => 'file_fid',
- // The field to display in the summary.
- 'name field' => 'filename',
- 'numeric' => TRUE,
- ),
- 'filter' => array(
- 'id' => 'numeric',
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- 'relationship' => array(
- 'title' => t('File usage'),
- 'help' => t('Relate file entities to their usage.'),
- 'id' => 'standard',
- 'base' => 'file_usage',
- 'base field' => 'fid',
- 'field' => 'fid',
- 'label' => t('File usage'),
- ),
+ $data['file_managed']['fid']['field']['id'] ='file';
+ $data['file_managed']['fid']['argument'] = array(
+ 'id' => 'file_fid',
+ // The field to display in the summary.
+ 'name field' => 'filename',
+ 'numeric' => TRUE,
);
-
- // Describes filename field in file_managed table.
- $data['file_managed']['filename'] = array(
- 'title' => t('Name'),
- 'help' => t('The name of the file.'),
- 'field' => array(
- 'id' => 'file',
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- 'filter' => array(
- 'id' => 'string',
- ),
- 'argument' => array(
- 'id' => 'string',
- ),
+ $data['file_managed']['fid']['relationship'] = array(
+ 'title' => t('File usage'),
+ 'help' => t('Relate file entities to their usage.'),
+ 'id' => 'standard',
+ 'base' => 'file_usage',
+ 'base field' => 'fid',
+ 'field' => 'fid',
+ 'label' => t('File usage'),
);
- // Describes uri field in file_managed table.
- $data['file_managed']['uri'] = array(
- 'title' => t('Path'),
- 'help' => t('The path of the file.'),
- 'field' => array(
- 'id' => 'file_uri',
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- 'filter' => array(
- 'id' => 'string',
- ),
- 'argument' => array(
- 'id' => 'string',
- ),
- );
+ $data['file_managed']['filename']['field']['id'] = 'file';
- // Describes filemime field in file_managed table.
- $data['file_managed']['filemime'] = array(
- 'title' => t('Mime type'),
- 'help' => t('The mime type of the file.'),
- 'field' => array(
- 'id' => 'file_filemime',
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- 'filter' => array(
- 'id' => 'string',
- ),
- 'argument' => array(
- 'id' => 'string',
- ),
- );
+ $data['file_managed']['uri']['field']['id'] = 'file_uri';
+
+ $data['file_managed']['filemime']['field']['id'] = 'file_filemime';
- // Describes extension field in file_managed table.
$data['file_managed']['extension'] = array(
'title' => t('Extension'),
'help' => t('The extension of the file.'),
@@ -129,79 +58,14 @@ public function getViewsData() {
),
);
- // Describes filesize field in file_managed table.
- $data['file_managed']['filesize'] = array(
- 'title' => t('Size'),
- 'help' => t('The size of the file.'),
- 'field' => array(
- 'id' => 'file_size',
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- 'filter' => array(
- 'id' => 'numeric',
- ),
- );
-
- // Describes status field in file_managed table.
- $data['file_managed']['status'] = array(
- 'title' => t('Status'),
- 'help' => t('The status of the file.'),
- 'field' => array(
- 'id' => 'file_status',
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- 'filter' => array(
- 'id' => 'file_status',
- ),
- );
-
- // Describes created field in file_managed table.
- $data['file_managed']['created'] = array(
- 'title' => t('Upload date'),
- 'help' => t('The date the file was uploaded.'),
- 'field' => array(
- 'id' => 'date',
- ),
- 'sort' => array(
- 'id' => 'date',
- ),
- 'filter' => array(
- 'id' => 'date',
- ),
- );
+ $data['file_managed']['filesize']['field']['id'] = 'file_size';
- // Describes changed field in file_managed table.
- $data['file_managed']['changed'] = array(
- 'title' => t('Modified date'),
- 'help' => t('The date the file was last changed.'),
- 'field' => array(
- 'id' => 'date',
- ),
- 'sort' => array(
- 'id' => 'date',
- ),
- 'filter' => array(
- 'id' => 'date',
- ),
- );
+ $data['file_managed']['status']['field']['id'] = 'file_status';
+ $data['file_managed']['status']['filter']['id'] = 'file_status';
- // Describes uid field in file_managed table.
- $data['file_managed']['uid'] = array(
- 'title' => t('User who uploaded'),
- 'help' => t('The user that uploaded the file.'),
- 'relationship' => array(
- 'title' => t('User who uploaded'),
- 'label' => t('User who uploaded'),
- 'base' => 'users',
- 'base field' => 'uid',
- ),
- );
+ $data['file_managed']['uid']['relationship']['title'] = t('User who uploaded');
+ $data['file_managed']['uid']['relationship']['label'] = t('User who uploaded');
- // Sets 'group' index for file_usage table.
$data['file_usage']['table']['group'] = t('File Usage');
// Provide field-type-things to several base tables; on the core files table
@@ -499,4 +363,3 @@ public function getViewsData() {
}
}
-
diff --git a/core/modules/file/src/Tests/FileFieldRSSContentTest.php b/core/modules/file/src/Tests/FileFieldRSSContentTest.php
index db265c2..42b88ce 100644
--- a/core/modules/file/src/Tests/FileFieldRSSContentTest.php
+++ b/core/modules/file/src/Tests/FileFieldRSSContentTest.php
@@ -51,7 +51,7 @@ function testFileFieldRSSContent() {
// Create a new node with a file field set. Promote to frontpage
// needs to be set so this node will appear in the RSS feed.
- $node = $this->drupalCreateNode(array('type' => $type_name, 'promote' => 1));
+ $node = $this->drupalCreateNode(array('type' => $type_name, 'promote' => 1, 'langcode' => 'en'));
$test_file = $this->getTestFile('text');
// Create a new node with the uploaded file.
diff --git a/core/modules/node/src/NodeViewsData.php b/core/modules/node/src/NodeViewsData.php
index 6d69a58..008d175 100644
--- a/core/modules/node/src/NodeViewsData.php
+++ b/core/modules/node/src/NodeViewsData.php
@@ -7,169 +7,48 @@
namespace Drupal\node;
+use Drupal\views\EntityViewsData;
use Drupal\views\EntityViewsDataInterface;
/**
* Provides the views data for the node entity type.
*/
-class NodeViewsData implements EntityViewsDataInterface {
+class NodeViewsData extends EntityViewsData implements EntityViewsDataInterface {
/**
* {@inheritdoc}
*/
public function getViewsData() {
- // Define the base group of this table. Fields that don't have a group defined
- // will go into this field by default.
- $data['node']['table']['group'] = t('Content');
+ $data = parent::getViewsData();
- // Advertise this table as a possible base table.
- $data['node']['table']['base'] = array(
- 'field' => 'nid',
- 'title' => t('Content'),
- 'weight' => -10,
- 'access query tag' => 'node_access',
- 'defaults' => array(
- 'field' => 'title',
- ),
- );
- $data['node']['table']['entity type'] = 'node';
+ $data['node']['table']['base']['weight'] = -10;
+ $data['node']['table']['base']['access query tag'] = 'node_access';
$data['node']['table']['wizard_id'] = 'node';
- $data['node_field_data']['table']['group'] = t('Content');
- $data['node_field_data']['table']['entity type'] = 'node';
- $data['node_field_data']['table']['join']['node'] = array(
- 'type' => 'INNER',
- 'left_field' => 'nid',
- 'field' => 'nid',
- );
+ $data['node']['nid']['field']['id'] = 'node';
+ $data['node']['nid']['field']['argument'] = [
+ 'id' => 'node_nid',
+ 'name field' => 'title',
+ 'numeric' => TRUE,
+ 'validate type' => 'nid',
+ ];
- $data['node']['nid'] = array(
- 'title' => t('Nid'),
- 'help' => t('The node ID.'),
- 'field' => array(
- 'id' => 'node',
- ),
- 'argument' => array(
- 'id' => 'node_nid',
- 'name field' => 'title',
- 'numeric' => TRUE,
- 'validate type' => 'nid',
- ),
- 'filter' => array(
- 'id' => 'numeric',
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- );
+ $data['node_field_data']['title']['field']['id'] = 'node';
+ $data['node_field_data']['title']['field']['link_to_node default'] = TRUE;
- // This definition has more items in it than it needs to as an example.
- $data['node_field_data']['title'] = array(
- 'title' => t('Title'),
- 'help' => t('The content title.'),
- 'field' => array(
- // This is the real field which could be left out since it is the same.
- 'field' => 'title',
- // This is the UI group which could be left out since it is the same.
- 'group' => t('Content'),
- 'id' => 'node',
- 'link_to_node default' => TRUE,
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- 'filter' => array(
- 'id' => 'string',
- ),
- 'argument' => array(
- 'id' => 'string',
- ),
- );
+ $data['node_field_data']['type']['field']['id'] = 'node_type';
+ $data['node_field_data']['type']['argument']['id'] = 'node_type';
- $data['node_field_data']['created'] = array(
- 'title' => t('Post date'),
- 'help' => t('The date the content was posted.'),
- 'field' => array(
- 'id' => 'date',
- ),
- 'sort' => array(
- 'id' => 'date'
- ),
- 'filter' => array(
- 'id' => 'date',
- ),
- );
+ $data['node_field_data']['langcode']['help'] = t('The language of the content or translation.');
+ $data['node_field_data']['langcode']['field']['id'] = 'node_language';
- $data['node_field_data']['changed'] = array(
- 'title' => t('Updated date'),
- 'help' => t('The date the content was last updated.'),
- 'field' => array(
- 'id' => 'date',
- ),
- 'sort' => array(
- 'id' => 'date'
- ),
- 'filter' => array(
- 'id' => 'date',
- ),
- );
-
- $data['node_field_data']['type'] = array(
- 'title' => t('Type'),
- 'help' => t('The content type (for example, "blog entry", "forum post", "story", etc).'),
- 'field' => array(
- 'id' => 'node_type',
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- 'filter' => array(
- 'id' => 'bundle',
- ),
- 'argument' => array(
- 'id' => 'node_type',
- ),
- );
-
- if (\Drupal::moduleHandler()->moduleExists('language')) {
- $data['node_field_data']['langcode'] = array(
- 'title' => t('Translation language'),
- 'help' => t('The language of the content or translation.'),
- 'field' => array(
- 'id' => 'node_language',
- ),
- 'filter' => array(
- 'id' => 'language',
- ),
- 'argument' => array(
- 'id' => 'language',
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- );
- }
-
- $data['node_field_data']['status'] = array(
- 'title' => t('Published status'),
- 'help' => t('Whether or not the content is published.'),
- 'field' => array(
- 'id' => 'boolean',
- 'output formats' => array(
- 'published-notpublished' => array(t('Published'), t('Not published')),
- ),
- ),
- 'filter' => array(
- 'id' => 'boolean',
- 'label' => t('Published status'),
- 'type' => 'yes-no',
- // Use status = 1 instead of status <> 0 in WHERE statement.
- 'use_equal' => TRUE,
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- );
+ $data['node_field_data']['status']['field']['output formats'] = [
+ 'published-notpublished' => array(t('Published'), t('Not published')),
+ ];
+ $data['node_field_data']['status']['filter']['label'] = t('Published status');
+ $data['node_field_data']['status']['filter']['type'] = 'yes-no';
+ // Use status = 1 instead of status <> 0 in WHERE statement.
+ $data['node_field_data']['status']['filter']['use_equal'] = TRUE;
$data['node_field_data']['status_extra'] = array(
'title' => t('Published status or admin user'),
@@ -181,55 +60,27 @@ public function getViewsData() {
),
);
- $data['node_field_data']['promote'] = array(
- 'title' => t('Promoted to front page status'),
- 'help' => t('Whether or not the content is promoted to the front page.'),
- 'field' => array(
- 'id' => 'boolean',
- 'output formats' => array(
- 'promoted-notpromoted' => array(t('Promoted'), t('Not promoted')),
- ),
- ),
- 'filter' => array(
- 'id' => 'boolean',
- 'label' => t('Promoted to front page status'),
- 'type' => 'yes-no',
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- );
+ $data['node_field_data']['promote']['field']['output formats'] = [
+ 'promoted-notpromoted' => array(t('Promoted'), t('Not promoted')),
+ ];
+ $data['node_field_data']['promote']['filter']['label'] = t('Promoted to front page status');
+ $data['node_field_data']['promote']['filter']['type'] = 'yes-no';
+
+ $data['node_field_data']['sticky']['field']['output formats'] = [
+ 'sticky' => array(t('Sticky'), t('Not sticky')),
+ ];
+ $data['node_field_data']['sticky']['filter']['label'] = t('Sticky status');
+ $data['node_field_data']['sticky']['filter']['type'] = 'yes-no';
+ $data['node_field_data']['sticky']['sort']['help'] = t('Whether or not the content is sticky. To list sticky content first, set this to descending.');
- $data['node_field_data']['sticky'] = array(
- 'title' => t('Sticky status'),
- 'help' => t('Whether or not the content is sticky.'),
+ $data['node']['translation_link'] = array(
+ 'title' => t('Translation link'),
+ 'help' => t('Provide a link to the translations overview for nodes.'),
'field' => array(
- 'id' => 'boolean',
- 'output formats' => array(
- 'sticky' => array(t('Sticky'), t('Not sticky')),
- ),
- ),
- 'filter' => array(
- 'id' => 'boolean',
- 'label' => t('Sticky status'),
- 'type' => 'yes-no',
- ),
- 'sort' => array(
- 'id' => 'standard',
- 'help' => t('Whether or not the content is sticky. To list sticky content first, set this to descending.'),
+ 'id' => 'content_translation_link',
),
);
- if (\Drupal::moduleHandler()->moduleExists('content_translation')) {
- $data['node']['translation_link'] = array(
- 'title' => t('Translation link'),
- 'help' => t('Provide a link to the translations overview for nodes.'),
- 'field' => array(
- 'id' => 'content_translation_link',
- ),
- );
- }
-
$data['node']['view_node'] = array(
'field' => array(
'title' => t('Link to content'),
@@ -264,6 +115,7 @@ public function getViewsData() {
// Bogus fields for aliasing purposes.
+ // @Todo Add similar support to any date field.
$data['node_field_data']['created_fulldate'] = array(
'title' => t('Created date'),
'help' => t('Date in the form of CCYYMMDD.'),
@@ -372,27 +224,12 @@ public function getViewsData() {
),
);
- $data['node_field_data']['uid'] = array(
- 'title' => t('Author uid'),
- 'help' => t('The user authoring the content. If you need more fields than the uid add the content: author relationship'),
- 'relationship' => array(
- 'title' => t('Content author'),
- 'help' => t('Relate content to the user who created it.'),
- 'id' => 'standard',
- 'base' => 'users',
- 'field' => 'uid',
- 'label' => t('author'),
- ),
- 'filter' => array(
- 'id' => 'user_name',
- ),
- 'argument' => array(
- 'id' => 'numeric',
- ),
- 'field' => array(
- 'id' => 'user',
- ),
- );
+ $data['node_field_data']['uid']['help'] = t('The user authoring the content. If you need more fields than the uid add the content: author relationship');
+ $data['node_field_data']['uid']['filter']['id'] = 'user_name';
+ $data['node_field_data']['uid']['field']['id'] = 'user';
+ $data['node_field_data']['uid']['relationship']['title'] = t('Content author');
+ $data['node_field_data']['uid']['relationship']['help'] = t('Relate content to the user who created it.');
+ $data['node_field_data']['uid']['relationship']['label'] = t('author');
$data['node']['node_listing_empty'] = array(
'title' => t('Empty Node Frontpage behavior'),
@@ -402,83 +239,36 @@ public function getViewsData() {
),
);
- $data['node_field_data']['uid_revision'] = array(
- 'title' => t('User has a revision'),
- 'help' => t('All nodes where a certain user has a revision'),
- 'real field' => 'nid',
- 'filter' => array(
- 'id' => 'node_uid_revision',
- ),
- 'argument' => array(
- 'id' => 'node_uid_revision',
- ),
- );
+ $data['node_field_data']['uid_revision']['title'] = t('User has a revision');
+ $data['node_field_data']['uid_revision']['help'] = t('All nodes where a certain user has a revision');
+ $data['node_field_data']['uid_revision']['real field'] = 'nid';
+ $data['node_field_data']['uid_revision']['filter']['id'] = 'node_uid_revision';
+ $data['node_field_data']['uid_revision']['argument']['id'] = 'node_uid_revision';
- $data['node_revision']['table']['entity type'] = 'node';
- // Define the base group of this table. Fields that don't have a group defined
- // will go into this field by default.
- $data['node_revision']['table']['group'] = t('Content revision');
$data['node_revision']['table']['wizard_id'] = 'node_revision';
// Advertise this table as a possible base table.
- $data['node_revision']['table']['base'] = array(
- 'field' => 'vid',
- 'title' => t('Content revision'),
- 'help' => t('Content revision is a history of changes to content.'),
- 'defaults' => array(
- 'field' => 'title',
- ),
- );
-
- // For other base tables, explain how we join.
- $data['node_revision']['table']['join'] = array(
- 'node' => array(
- 'left_field' => 'vid',
- 'field' => 'vid',
- ),
- );
-
- $data['node_revision']['nid'] = array(
- 'title' => t('Nid'),
- 'help' => t('The revision NID of the content revision.'),
- 'field' => array(
- 'id' => 'standard',
- ),
- 'argument' => array(
- 'id' => 'node_nid',
- 'numeric' => TRUE,
- ),
- 'filter' => array(
- 'id' => 'numeric',
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- 'relationship' => array(
- 'id' => 'standard',
- 'base' => 'node',
- 'base field' => 'nid',
- 'title' => t('Content'),
- 'label' => t('Get the actual content from a content revision.'),
- ),
- );
+ $data['node_revision']['table']['base']['help'] = t('Content revision is a history of changes to content.');
+ $data['node_revision']['table']['base']['defaults']['title'] = 'title';
+
+ $data['node_revision']['nid']['argument'] = [
+ 'id' => 'node_nid',
+ 'numeric' => TRUE,
+ ];
+ // @todo the NID field needs different behaviour on revision/non-revision
+ // tables. It would be neat if this could be encoded in the base field
+ // definition.
+ $data['node_revision']['nid']['relationship']['id'] = 'standard';
+ $data['node_revision']['nid']['relationship']['base'] = 'node';
+ $data['node_revision']['nid']['relationship']['base field'] = 'nid';
+ $data['node_revision']['nid']['relationship']['title'] = t('Content');
+ $data['node_revision']['nid']['relationship']['label'] = t('Get the actual content from a content revision.');
$data['node_revision']['vid'] = array(
- 'title' => t('Vid'),
- 'help' => t('The revision ID of the content revision.'),
- 'field' => array(
- 'id' => 'standard',
- ),
'argument' => array(
'id' => 'node_vid',
'numeric' => TRUE,
),
- 'filter' => array(
- 'id' => 'numeric',
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
'relationship' => array(
'id' => 'standard',
'base' => 'node',
@@ -486,119 +276,29 @@ public function getViewsData() {
'title' => t('Content'),
'label' => t('Get the actual content from a content revision.'),
),
- );
+ ) + $data['node_revision']['vid'];
- if (\Drupal::moduleHandler()->moduleExists('language')) {
- $data['node_revision']['langcode'] = array(
- 'title' => t('Original language'),
- 'help' => t('The language the original content is in.'),
- 'field' => array(
- 'id' => 'node_language',
- ),
- 'filter' => array(
- 'id' => 'language',
- ),
- 'argument' => array(
- 'id' => 'language',
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- );
- }
+ $data['node_revision']['langcode']['help'] = t('The language the original content is in.');
+ $data['node_revision']['langcode']['field']['id'] = 'node_language';
- $data['node_revision']['revision_log'] = array(
- 'title' => t('Log message'),
- 'help' => t('The log message entered when the revision was created.'),
- 'field' => array(
- 'id' => 'xss',
- ),
- 'filter' => array(
- 'id' => 'string',
- ),
- );
+ $data['node_revision']['revision_log']['field']['id'] = 'xss';
- $data['node_revision']['revision_uid'] = array(
- 'title' => t('User'),
- 'help' => t('Relate a content revision to the user who created the revision.'),
- 'relationship' => array(
- 'id' => 'standard',
- 'base' => 'users',
- 'base field' => 'uid',
- 'label' => t('revision user'),
- ),
- );
+ $data['node_revision']['revision_uid']['help'] = t('Relate a content revision to the user who created the revision.');
+ $data['node_revision']['revision_uid']['relationship']['label'] = t('revision user');
- $data['node_field_revision']['table']['entity type'] = 'node';
- // Define the base group of this table. Fields that don't have a group defined
- // will go into this field by default.
- $data['node_field_revision']['table']['group'] = t('Content revision');
$data['node_field_revision']['table']['wizard_id'] = 'node_field_revision';
- // For other base tables, explain how we join.
- $data['node_field_revision']['table']['join'] = array(
- 'node' => array(
- 'left_field' => 'vid',
- 'field' => 'vid',
- ),
- 'node_revision' => array(
- 'left_field' => 'vid',
- 'field' => 'vid',
- ),
- );
+ $data['node_field_revision']['table']['join']['node']['left_field'] = 'vid';
+ $data['node_field_revision']['table']['join']['node']['field'] = 'vid';
- $data['node_field_revision']['status'] = array(
- 'title' => t('Published'),
- 'help' => t('Whether or not the content is published.'),
- 'field' => array(
- 'id' => 'boolean',
- 'output formats' => array(
- 'published-notpublished' => array(t('Published'), t('Not published')),
- ),
- ),
- 'filter' => array(
- 'id' => 'boolean',
- 'label' => t('Published'),
- 'type' => 'yes-no',
- // Use status = 1 instead of status <> 0 in WHERE statement.
- 'use_equal' => TRUE,
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- );
-
- $data['node_field_revision']['title'] = array(
- 'title' => t('Title'),
- 'help' => t('The content title.'),
- 'field' => array(
- 'field' => 'title',
- 'id' => 'node_revision',
- ),
- 'sort' => array(
- 'id' => 'standard',
- ),
- 'filter' => array(
- 'id' => 'string',
- ),
- 'argument' => array(
- 'id' => 'string',
- ),
- );
+ $data['node_field_revision']['status']['field']['output formats'] = [
+ 'published-notpublished' => [t('Published'), t('Not published')],
+ ];
+ $data['node_field_revision']['status']['filter']['label'] = t('Published');
+ $data['node_field_revision']['status']['filter']['type'] = 'yes-no';
+ $data['node_field_revision']['status']['filter']['use_equal'] = TRUE;
- $data['node_field_revision']['changed'] = array(
- 'title' => t('Updated date'),
- 'help' => t('The date the content was last updated.'),
- 'field' => array(
- 'id' => 'date',
- ),
- 'sort' => array(
- 'id' => 'date'
- ),
- 'filter' => array(
- 'id' => 'date',
- ),
- );
+ $data['node_field_revision']['title']['field']['id'] = 'node_revision';
$data['node_revision']['link_to_revision'] = array(
'field' => array(
@@ -727,6 +427,4 @@ public function getViewsData() {
return $data;
}
-
}
-
diff --git a/core/modules/node/src/Tests/NodeLoadMultipleTest.php b/core/modules/node/src/Tests/NodeLoadMultipleTest.php
index 3cea6a1..fbeb16b 100644
--- a/core/modules/node/src/Tests/NodeLoadMultipleTest.php
+++ b/core/modules/node/src/Tests/NodeLoadMultipleTest.php
@@ -31,10 +31,10 @@ protected function setUp() {
* Creates four nodes and ensures that they are loaded correctly.
*/
function testNodeMultipleLoad() {
- $node1 = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1));
- $node2 = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1));
- $node3 = $this->drupalCreateNode(array('type' => 'article', 'promote' => 0));
- $node4 = $this->drupalCreateNode(array('type' => 'page', 'promote' => 0));
+ $node1 = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'langcode' => 'en'));
+ $node2 = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'langcode' => 'en'));
+ $node3 = $this->drupalCreateNode(array('type' => 'article', 'promote' => 0, 'langcode' => 'en'));
+ $node4 = $this->drupalCreateNode(array('type' => 'page', 'promote' => 0, 'langcode' => 'en'));
// Confirm that promoted nodes appear in the default node listing.
$this->drupalGet('node');
diff --git a/core/modules/node/src/Tests/NodeRSSContentTest.php b/core/modules/node/src/Tests/NodeRSSContentTest.php
index e4b7cb3..6a1c830 100644
--- a/core/modules/node/src/Tests/NodeRSSContentTest.php
+++ b/core/modules/node/src/Tests/NodeRSSContentTest.php
@@ -40,7 +40,7 @@ protected function setUp() {
*/
function testNodeRSSContent() {
// Create a node.
- $node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1));
+ $node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'langcode' => 'en'));
$this->drupalGet('rss.xml');
diff --git a/core/modules/node/src/Tests/NodeTitleTest.php b/core/modules/node/src/Tests/NodeTitleTest.php
index d692be5..470465c 100644
--- a/core/modules/node/src/Tests/NodeTitleTest.php
+++ b/core/modules/node/src/Tests/NodeTitleTest.php
@@ -41,6 +41,7 @@ function testNodeTitle() {
$settings = array(
'title' => $this->randomMachineName(8),
'promote' => 1,
+ 'langcode' => 'en',
);
$node = $this->drupalCreateNode($settings);
@@ -64,6 +65,7 @@ function testNodeTitle() {
// Test edge case where node title is set to 0.
$settings = array(
'title' => 0,
+ 'langcode' => 'en',
);
$node = $this->drupalCreateNode($settings);
// Test that 0 appears as
.
diff --git a/core/modules/system/entity.api.php b/core/modules/system/entity.api.php
index f4bbfd2..f7411c6 100644
--- a/core/modules/system/entity.api.php
+++ b/core/modules/system/entity.api.php
@@ -310,7 +310,8 @@
* config entities will use \Drupal\Core\Config\Entity\ConfigEntityStorage.
* You can extend one of these classes to provide custom behavior.
* - views_data: A class implementing \Drupal\views\EntityViewsDataInterface
- * to provide views data for the entity type.
+ * to provide views data for the entity type. You can autogenerate most of
+ * the views data by extending \Drupal\views\EntityViewsData.
* - For content entities, the annotation will refer to a number of database
* tables and their fields. These annotation properties, such as 'base_table',
* 'data_table', 'entity_keys', etc., are documented on
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
index 3993dcf..1b065af 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
@@ -29,7 +29,8 @@
* "default" = "Drupal\entity_test\EntityTestForm",
* "delete" = "Drupal\entity_test\EntityTestDeleteForm"
* },
- * "translation" = "Drupal\content_translation\ContentTranslationHandler"
+ * "translation" = "Drupal\content_translation\ContentTranslationHandler",
+ * "views_data" = "Drupal\views\EntityViewsData"
* },
* base_table = "entity_test",
* fieldable = TRUE,
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
index fa66635..4e03fd1 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
@@ -24,7 +24,8 @@
* "default" = "Drupal\entity_test\EntityTestForm",
* "delete" = "Drupal\entity_test\EntityTestDeleteForm"
* },
- * "translation" = "Drupal\content_translation\ContentTranslationHandler"
+ * "translation" = "Drupal\content_translation\ContentTranslationHandler",
+ * "views_data" = "Drupal\views\EntityViewsData"
* },
* base_table = "entity_test_mul",
* data_table = "entity_test_mul_property_data",
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
index 50afa32..13aca32 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
@@ -23,7 +23,8 @@
* "default" = "Drupal\entity_test\EntityTestForm",
* "delete" = "Drupal\entity_test\EntityTestDeleteForm"
* },
- * "translation" = "Drupal\content_translation\ContentTranslationHandler"
+ * "translation" = "Drupal\content_translation\ContentTranslationHandler",
+ * "views_data" = "Drupal\views\EntityViewsData"
* },
* base_table = "entity_test_mulrev",
* data_table = "entity_test_mulrev_property_data",
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
index ebb39f2..f97d2fd 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
@@ -23,7 +23,8 @@
* "default" = "Drupal\entity_test\EntityTestForm",
* "delete" = "Drupal\entity_test\EntityTestDeleteForm"
* },
- * "translation" = "Drupal\content_translation\ContentTranslationHandler"
+ * "translation" = "Drupal\content_translation\ContentTranslationHandler",
+ * "views_data" = "Drupal\views\EntityViewsData"
* },
* base_table = "entity_test_rev",
* revision_table = "entity_test_rev_revision",
diff --git a/core/modules/views/src/EntityViewsData.php b/core/modules/views/src/EntityViewsData.php
new file mode 100644
index 0000000..9a86f77
--- /dev/null
+++ b/core/modules/views/src/EntityViewsData.php
@@ -0,0 +1,444 @@
+entityType = $entity_type;
+ $this->entityManager = $entity_manager;
+ $this->storage = $storage_controller;
+ $this->moduleHandler = $module_handler;
+ $this->setStringTranslation($translation_manager);
+ $this->typedDataManager = $typed_data_manager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+ return new static(
+ $entity_type,
+ $container->get('entity.manager')->getStorage($entity_type->id()),
+ $container->get('entity.manager'),
+ $container->get('module_handler'),
+ $container->get('string_translation'),
+ $container->get('typed_data_manager')
+ );
+ }
+
+ /**
+ * Gets the field storage definitions.
+ *
+ * @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
+ */
+ protected function getFieldStorageDefinitions() {
+ if (!isset($this->fieldStorageDefinitions)) {
+ $this->fieldStorageDefinitions = $this->entityManager->getFieldStorageDefinitions($this->entityType->id());
+ }
+ return $this->fieldStorageDefinitions;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getViewsData() {
+ $data = [];
+
+ // @todo In theory we should use the data table as base table, as this would
+ // save one pointless join (and one more for every relationship).
+ $base_table = $this->entityType->getBaseTable();
+ $base_field = $this->entityType->getKey('id');
+ $data_table = $this->entityType->getDataTable();
+ $revision_table = $this->entityType->getRevisionTable();
+ $revision_data_table = $this->entityType->getRevisionDataTable();
+ $revision_field = $this->entityType->getKey('revision');
+
+ // Setup base information of the views data.
+ $data[$base_table]['table']['entity type'] = $this->entityType->id();
+ $data[$base_table]['table']['group'] = $this->entityType->getLabel();
+ $data[$base_table]['table']['base'] = [
+ 'field' => $base_field,
+ 'title' => $this->entityType->getLabel(),
+ ];
+
+ if ($label_key = $this->entityType->getKey('label')) {
+ if ($data_table) {
+ $data[$base_table]['table']['base']['defaults'] = array(
+ 'field' => $label_key,
+ 'table' => $data_table,
+ );
+ }
+ else {
+ $data[$base_table]['table']['base']['defaults'] = array(
+ 'field' => $label_key,
+ );
+ }
+ }
+
+ // Setup relations to the revisions/property data.
+ if ($data_table) {
+ $data[$data_table]['table']['join'][$base_table] = [
+ 'left_field' => $base_field,
+ 'field' => $base_field,
+ 'type' => 'INNER'
+ ];
+ $data[$data_table]['table']['entity type'] = $this->entityType->id();
+ $data[$data_table]['table']['group'] = $this->entityType->getLabel();
+ }
+ if ($revision_table) {
+ $data[$revision_table]['table']['entity type'] = $this->entityType->id();
+ $data[$revision_table]['table']['group'] = $this->t('@entity_type revision', ['@entity_type' => $this->entityType->getLabel()]);
+ $data[$revision_table]['table']['base'] = array(
+ 'field' => $revision_field,
+ 'title' => $this->t('@entity_type revisions', array('@entity_type' => $this->entityType->getLabel())),
+ );
+ // Join the revision table to the base table.
+ $data[$revision_table]['table']['join'][$base_table] = array(
+ 'left_field' => $revision_field,
+ 'field' => $revision_field,
+ 'type' => 'INNER',
+ );
+
+ if ($revision_data_table) {
+ $data[$revision_data_table]['table']['entity type'] = $this->entityType->id();
+ $data[$revision_data_table]['table']['group'] = $this->t('@entity_type revision', ['@entity_type' => $this->entityType->getLabel()]);
+
+ $data[$revision_data_table]['table']['join'][$revision_table] = array(
+ 'left_field' => $revision_field,
+ 'field' => $revision_field,
+ 'type' => 'INNER',
+ );
+ }
+ }
+
+ // 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());
+ if ($table_mapping = $this->storage->getTableMapping()) {
+ // Iterate over each table we have so far and collect field data for each.
+ // Based on whether the field is in the field_definitions provided by the
+ // entity manager.
+ // @todo We should better just rely on information coming from the entity
+ // storage.
+ foreach ($table_mapping->getTableNames() as $table) {
+ foreach ($table_mapping->getFieldNames($table) as $field_name) {
+ $this->mapFieldDefinition($table, $field_name, $field_definitions[$field_name], $table_mapping, $data[$table]);
+ }
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Puts the views data for a single field onto the views data.
+ *
+ * @param string $field_name
+ * The name of the field to handle.
+ * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+ * The field definition defined in Entity::baseFieldDefinitions()
+ * @param \Drupal\Core\Entity\Sql\TableMappingInterface $table_mapping
+ * The table mapping information
+ * @param array $table_data
+ * A reference to a specific entity table (for example data_table) inside
+ * the views data.
+ */
+ protected function mapFieldDefinition($table, $field_name, FieldDefinitionInterface $field_definition, TableMappingInterface $table_mapping, &$table_data) {
+ // Create a dummy instance to retrieve property definitions.
+ $field_column_mapping = $table_mapping->getColumnNames($field_name);
+ $field_schema = $this->getFieldStorageDefinitions()[$field_name]->getSchema();
+
+ $field_definition_type = $field_definition->getType();
+ // Add all properties to views table data.
+ $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.
+ if ($first) {
+ $first = FALSE;
+ $table_data[$field_name] = $this->mapSingleFieldViewsData($table, $field_name, $field_definition_type, $schema_field_name, $field_definition, TRUE);
+ }
+ else {
+ $table_data["$field_name.$field_column_name"] = $this->mapSingleFieldViewsData($table, $field_name, $schema['type'], $schema_field_name, $field_definition, FALSE);
+ }
+ }
+ }
+
+ /**
+ * Provides the views data for a given data type and schema field.
+ *
+ * @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 bool $first
+ * Is it the first column of the schema.
+ *
+ * @return array
+ * The modified views data field definition.
+ */
+ protected function mapSingleFieldViewsData($table, $field_name, $data_type, $schema_field_name, FieldDefinitionInterface $field_definition, $first) {
+ $views_field = array();
+
+ // Provide a nicer, less verbose label for the first field.
+ if ($first) {
+ $views_field['title'] = $field_definition->getLabel();
+ }
+ else {
+ $views_field['title'] = $field_definition->getLabel() . " ($schema_field_name)";
+ }
+
+ if ($description = $field_definition->getDescription()) {
+ $views_field['help'] = $description;
+ }
+
+ // @todo Allow field types to customize this.
+ 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';
+ 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.
+ $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';
+ $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);
+ if (method_exists($this, $process_method)) {
+ $this->{$process_method}($table, $field_name, $field_definition, $views_field);
+ }
+
+ return $views_field;
+ }
+
+ /**
+ * Processes the views data for a language field.
+ *
+ * @param string $table
+ * The table the language field is added to.
+ * @param string $field_name
+ * The field name of the language field.
+ * @param array $views_field
+ * The views field data.
+ */
+ protected function processViewsDataForLanguage($table, $field_name, FieldDefinitionInterface $field_definition, array &$views_field) {
+ // Apply special titles for the langcode field.
+ if ($field_name == 'langcode') {
+ if ($table == $this->entityType->getDataTable() || $table == $this->entityType->getBaseTable()) {
+ $views_field['title'] = $this->t('Translation language');
+ }
+ if ($table == $this->entityType->getRevisionDataTable() || $table == $this->entityType->getRevisionTable()) {
+ $views_field['title'] = $this->t('Original language');
+ }
+ }
+ }
+
+ /**
+ * Processes the views data for an entity reference field.
+ *
+ * @param string $table
+ * The table the language field is added to.
+ * @param string $field_name
+ * The field name of the language field.
+ * @param array $views_field
+ * The views field data.
+ */
+ protected function processViewsDataForEntityReference($table, $field_name, FieldDefinitionInterface $field_definition, array &$views_field) {
+ if ($entity_type_id = $field_definition->getItemDefinition()->getSetting('target_type')) {
+ $entity_type = $this->entityManager->getDefinition($entity_type_id);
+ if ($entity_type instanceof ContentEntityType) {
+ $views_field['relationship'] = [
+ 'base' => $this->getViewsTableForEntityType($entity_type),
+ 'base field' => $entity_type->getKey('id'),
+ 'label' => $entity_type->getLabel(),
+ 'title' => $entity_type->getLabel(),
+ 'id' => 'standard',
+ ];
+ $views_field['field']['id'] = 'numeric';
+ $views_field['argument']['id'] = 'numeric';
+ $views_field['filter']['id'] = 'numeric';
+ $views_field['sort']['id'] = 'standard';
+ }
+ else {
+ $views_field['field']['id'] = 'standard';
+ $views_field['argument']['id'] = 'string';
+ $views_field['filter']['id'] = 'string';
+ $views_field['sort']['id'] = 'standard';
+ }
+ }
+
+ if ($field_name == $this->entityType->getKey('bundle')) {
+ // @todo Use the other bundle handlers, once
+ // https://www.drupal.org/node/2322949 is in.
+ $views_field['filter']['id'] = 'bundle';
+ }
+ }
+
+ /**
+ * Gets the table of an entity type to be used as base table in views.
+ *
+ * @todo Given that the base_table is pretty much useless as you often have to
+ * join to the data table anyway, it could make a lot of sense to start with
+ * the data table right from the beginning.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+ * The entity type.
+ *
+ * @return string
+ * The name of the base table in views.
+ */
+ protected function getViewsTableForEntityType(EntityTypeInterface $entity_type) {
+ return $entity_type->getBaseTable();
+ }
+
+}
diff --git a/core/modules/views/tests/Drupal/views/Tests/EntityViewsDataTest.php b/core/modules/views/tests/Drupal/views/Tests/EntityViewsDataTest.php
new file mode 100644
index 0000000..4d6c8c8
--- /dev/null
+++ b/core/modules/views/tests/Drupal/views/Tests/EntityViewsDataTest.php
@@ -0,0 +1,626 @@
+entityStorage = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityDatabaseStorage')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
+
+ $this->baseEntityType = new EntityType([
+ 'base_table' => 'entity_test',
+ 'id' => 'entity_test',
+ 'label' => 'Entity test',
+ 'entity_keys' => ['id' => 'id'],
+ ]);
+
+ $this->translationManager = $this->getStringTranslationStub();
+ $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
+ $this->typedDataManager = $this->getMockBuilder('Drupal\Core\TypedData\TypedDataManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->viewsData = new TestEntityViewsData($this->baseEntityType, $this->entityStorage, $this->entityManager, $this->moduleHandler, $this->translationManager, $this->typedDataManager);
+
+ $field_type_manager = $this->getMockBuilder('Drupal\Core\Field\FieldTypePluginManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $field_type_manager->expects($this->any())
+ ->method('getDefaultSettings')
+ ->willReturn([]);
+ $field_type_manager->expects($this->any())
+ ->method('getDefaultInstanceSettings')
+ ->willReturn([]);
+
+ $container = new ContainerBuilder();
+ $container->set('plugin.manager.field.field_type', $field_type_manager);
+ $container->set('entity.manager', $this->entityManager);
+ \Drupal::setContainer($container);
+ }
+
+ /**
+ * Tests base tables.
+ */
+ public function testBaseTables() {
+ $data = $this->viewsData->getViewsData();
+
+ $this->assertEquals('entity_test', $data['entity_test']['table']['entity type']);
+ $this->assertEquals('Entity test', $data['entity_test']['table']['group']);
+
+ $this->assertEquals('id', $data['entity_test']['table']['base']['field']);
+ $this->assertEquals('Entity test', $data['entity_test']['table']['base']['title']);
+
+ $this->assertFalse(isset($data['entity_test']['table']['defaults']));
+
+ $this->assertFalse(isset($data['entity_test_mul_property_data']));
+ $this->assertFalse(isset($data['revision_table']));
+ $this->assertFalse(isset($data['revision_data_table']));
+ }
+
+
+ /**
+ * Tests data_table support.
+ */
+ public function testDataTable() {
+ $entity_type = $this->baseEntityType->set('data_table', 'entity_test_mul_property_data')
+ ->set('id', 'entity_test_mul')
+ ->setKey('label', 'label');
+
+ $this->viewsData->setEntityType($entity_type);
+
+ // Tests the join definition between the base and the data table.
+ $data = $this->viewsData->getViewsData();
+ $field_views_data = $data['entity_test_mul_property_data'];
+
+ $this->assertEquals('entity_test_mul', $data['entity_test_mul_property_data']['table']['entity type']);
+ $this->assertEquals('Entity test', $data['entity_test_mul_property_data']['table']['group']);
+ $this->assertEquals(['field' => 'label', 'table' => 'entity_test_mul_property_data'], $data['entity_test']['table']['base']['defaults']);
+
+ // Ensure the join information is set up properly.
+ $this->assertCount(1, $field_views_data['table']['join']);
+ $this->assertEquals(['entity_test' => ['left_field' => 'id', 'field' => 'id', 'type' => 'INNER']], $field_views_data['table']['join']);
+ $this->assertFalse(isset($data['revision_table']));
+ $this->assertFalse(isset($data['revision_data_table']));
+ }
+
+ /**
+ * Tests revision table support.
+ */
+ public function testRevisionTable() {
+ $entity_type = $this->baseEntityType
+ ->set('revision_table', 'entity_test_mulrev_revision')
+ ->set('revision_data_table', 'entity_test_mulrev_property_revision')
+ ->set('id', 'entity_test_mulrev')
+ ->setKey('revision', 'revision_id')
+ ;
+ $this->viewsData->setEntityType($entity_type);
+
+ $data = $this->viewsData->getViewsData();
+
+ $this->assertEquals('entity_test_mulrev', $data['entity_test_mulrev_revision']['table']['entity type']);
+ $this->assertEquals('entity_test_mulrev', $data['entity_test_mulrev_property_revision']['table']['entity type']);
+ $this->assertEquals('Entity test revision', $data['entity_test_mulrev_revision']['table']['group']);
+
+ // Ensure the join information is set up properly.
+ // Tests the join definition between the base and the revision table.
+ $revision_data = $data['entity_test_mulrev_revision'];
+ $this->assertCount(1, $revision_data['table']['join']);
+ $this->assertEquals(['entity_test' => ['left_field' => 'revision_id', 'field' => 'revision_id', 'type' => 'INNER']], $revision_data['table']['join']);
+ $revision_data = $data['entity_test_mulrev_property_revision'];
+ $this->assertCount(1, $revision_data['table']['join']);
+ $this->assertEquals(['entity_test_mulrev_revision' => ['left_field' => 'revision_id', 'field' => 'revision_id', 'type' => 'INNER']], $revision_data['table']['join']);
+ $this->assertFalse(isset($data['data_table']));
+ }
+
+ /**
+ * Helper method to mock all store definitions.
+ */
+ protected function setupFieldStorageDefinition() {
+ $id_field_storage_definition = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
+ $id_field_storage_definition->expects($this->any())
+ ->method('getSchema')
+ ->willReturn(IntegerItem::schema($id_field_storage_definition));
+ $uuid_field_storage_definition = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
+ $uuid_field_storage_definition->expects($this->any())
+ ->method('getSchema')
+ ->willReturn(UuidItem::schema($uuid_field_storage_definition));
+ $type_field_storage_definition = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
+ $type_field_storage_definition->expects($this->any())
+ ->method('getSchema')
+ ->willReturn(StringItem::schema($type_field_storage_definition));
+ $langcode_field_storage_definition = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
+ $langcode_field_storage_definition->expects($this->any())
+ ->method('getSchema')
+ ->willReturn(LanguageItem::schema($langcode_field_storage_definition));
+ $name_field_storage_definition = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
+ $name_field_storage_definition->expects($this->any())
+ ->method('getSchema')
+ ->willReturn(StringItem::schema($name_field_storage_definition));
+
+ // Setup the user_id entity reference field.
+ $this->entityManager->expects($this->any())
+ ->method('getDefinition')
+ ->willReturnMap([
+ ['user', TRUE, static::userEntityInfo()],
+ ]
+ );
+ $user_id_field_storage_definition = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
+ $user_id_field_storage_definition->expects($this->any())
+ ->method('getSetting')
+ ->with('target_type')
+ ->willReturn('user');
+ $user_id_field_storage_definition->expects($this->any())
+ ->method('getSchema')
+ ->willReturn(EntityReferenceItem::schema($user_id_field_storage_definition));
+
+ $revision_id_field_storage_definition = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
+ $revision_id_field_storage_definition->expects($this->any())
+ ->method('getSchema')
+ ->willReturn(IntegerItem::schema($revision_id_field_storage_definition));
+
+ $this->entityManager->expects($this->any())
+ ->method('getFieldStorageDefinitions')
+ ->willReturn([
+ 'id' => $id_field_storage_definition,
+ 'uuid' => $uuid_field_storage_definition,
+ 'type' => $type_field_storage_definition,
+ 'langcode' => $langcode_field_storage_definition,
+ 'name' => $name_field_storage_definition,
+ 'user_id' => $user_id_field_storage_definition,
+ 'revision_id' => $revision_id_field_storage_definition,
+ ]);
+ }
+
+ /**
+ * Tests fields on the base table.
+ */
+ public function testBaseTableFields() {
+ $base_field_definitions = EntityTest::baseFieldDefinitions($this->baseEntityType);
+ $this->entityManager->expects($this->once())
+ ->method('getBaseFieldDefinitions')
+ ->with('entity_test')
+ ->willReturn($base_field_definitions);
+
+ // Setup the table mapping.
+ $table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
+ $table_mapping->expects($this->any())
+ ->method('getTableNames')
+ ->willReturn(['entity_test']);
+ $table_mapping->expects($this->any())
+ ->method('getColumnNames')
+ ->willReturnMap([
+ ['id', ['value' => 'id']],
+ ['uuid', ['value' => 'uuid']],
+ ['type', ['value' => 'type']],
+ ['langcode', ['value' => 'langcode']],
+ ['name', ['value' => 'name']],
+ ['user_id', ['target_id' => 'user_id']],
+ ]);
+ $table_mapping->expects($this->any())
+ ->method('getFieldNames')
+ ->willReturnMap([
+ ['entity_test', ['id', 'uuid', 'type', 'langcode', 'name', 'user_id']]
+ ]);
+
+ $this->entityStorage->expects($this->once())
+ ->method('getTableMapping')
+ ->willReturn($table_mapping);
+
+ $this->setupFieldStorageDefinition();
+
+ $this->viewsData->setSchemaFields(['entity_test' => ['id', 'uuid', 'type', 'langcode', 'name', 'user_id']]);
+ $data = $this->viewsData->getViewsData();
+
+ $this->assertNumericField($data['entity_test']['id']);
+ $this->assertUuidField($data['entity_test']['uuid']);
+ $this->assertStringField($data['entity_test']['type']);
+
+ $this->assertLanguageField($data['entity_test']['langcode']);
+ $this->assertEquals('Translation language', $data['entity_test']['langcode']['title']);
+
+ $this->assertStringField($data['entity_test']['name']);
+
+ $this->assertEntityReferenceField($data['entity_test']['user_id']);
+ $relationship = $data['entity_test']['user_id']['relationship'];
+ $this->assertEquals('users', $relationship['base']);
+ $this->assertEquals('uid', $relationship['base field']);
+ }
+
+ /**
+ * Tests fields on the data table.
+ */
+ public function testDataTableFields() {
+ $entity_type = $this->baseEntityType
+ ->set('data_table', 'entity_test_mul_property_data')
+ ->set('base_table', 'entity_test_mul')
+ ->set('id', 'entity_test_mul')
+ ->setKey('bundle', 'type')
+ ;
+ $base_field_definitions = EntityTestMul::baseFieldDefinitions($this->baseEntityType);
+ $base_field_definitions['type'] = BaseFieldDefinition::create('entity_reference')
+ ->setLabel('entity test type')
+ ->setSettings(array('target_type' => 'entity_test_bundle'))
+ ->setTranslatable(TRUE);
+ $entity_test_type = new ConfigEntityType(['id' => 'entity_test_bundle']);
+ $user_entity_type = new ContentEntityType(['id' => 'user', 'base_table' => 'users', 'entity_keys' => ['id' => 'uid']]);
+ $this->entityManager->expects($this->any())
+ ->method('getDefinition')
+ ->willReturnMap([
+ ['entity_test_bundle', TRUE, $entity_test_type],
+ ['user', TRUE, $user_entity_type],
+ ]);
+
+ $this->entityManager->expects($this->once())
+ ->method('getBaseFieldDefinitions')
+ ->with('entity_test_mul')
+ ->willReturn($base_field_definitions);
+
+ $this->viewsData->setSchemaFields([
+ 'entity_test_mul' => ['id', 'uuid', 'type', 'langcode'],
+ 'entity_test_mul_property_data' => ['id', 'langcode', 'name', 'user_id'],
+ ]);
+
+ ;
+ $this->viewsData->setEntityType($entity_type);
+
+ // Setup the table mapping.
+ $table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
+ $table_mapping->expects($this->any())
+ ->method('getTableNames')
+ ->willReturn(['entity_test_mul', 'entity_test_mul_property_data']);
+ $table_mapping->expects($this->any())
+ ->method('getColumnNames')
+ ->willReturnMap([
+ ['id', ['value' => 'id']],
+ ['uuid', ['value' => 'uuid']],
+ ['type', ['value' => 'type']],
+ ['langcode', ['value' => 'langcode']],
+ ['name', ['value' => 'name']],
+ ['user_id', ['target_id' => 'user_id']],
+ ]);
+ $table_mapping->expects($this->any())
+ ->method('getFieldNames')
+ ->willReturnMap([
+ ['entity_test_mul', ['id', 'uuid', 'type', 'langcode']],
+ ['entity_test_mul_property_data', ['id', 'langcode', 'name', 'user_id']],
+ ]);
+
+ $this->entityStorage->expects($this->once())
+ ->method('getTableMapping')
+ ->willReturn($table_mapping);
+
+ $this->setupFieldStorageDefinition();
+
+ $data = $this->viewsData->getViewsData();
+
+ // Check the base fields.
+ $this->assertNumericField($data['entity_test_mul']['id']);
+ $this->assertUuidField($data['entity_test_mul']['uuid']);
+
+ $this->assertBundleField($data['entity_test_mul']['type']);
+ $this->assertFalse(isset($data['entity_test_mul']['type']['relationship']));
+
+ $this->assertLanguageField($data['entity_test_mul']['langcode']);
+ // Also ensure that field_data only fields don't appear on the base table.
+ $this->assertFalse(isset($data['entity_test_mul']['name']));
+ $this->assertFalse(isset($data['entity_test_mul']['user_id']));
+
+ // Check the data fields.
+ $this->assertNumericField($data['entity_test_mul_property_data']['id']);
+
+ $this->assertLanguageField($data['entity_test_mul_property_data']['langcode']);
+ $this->assertEquals('Translation language', $data['entity_test_mul_property_data']['langcode']['title']);
+
+ $this->assertStringField($data['entity_test_mul_property_data']['name']);
+
+ $this->assertEntityReferenceField($data['entity_test_mul_property_data']['user_id']);
+ $relationship = $data['entity_test_mul_property_data']['user_id']['relationship'];
+ $this->assertEquals('users', $relationship['base']);
+ $this->assertEquals('uid', $relationship['base field']);
+ }
+
+ /**
+ * Tests fields on the revision table.
+ */
+ public function testRevisionTableFields() {
+ $entity_type = $this->baseEntityType
+ ->set('base_table', 'entity_test_mulrev')
+ ->set('revision_table', 'entity_test_mulrev_revision')
+ ->set('data_table', 'entity_test_mulrev_property_data')
+ ->set('revision_data_table', 'entity_test_mulrev_property_revision')
+ ->set('id', 'entity_test_mulrev');
+ $base_field_definitions = EntityTestMulRev::baseFieldDefinitions($this->baseEntityType);
+ $this->entityManager->expects($this->once())
+ ->method('getBaseFieldDefinitions')
+ ->with('entity_test_mulrev')
+ ->willReturn($base_field_definitions);
+
+ $this->viewsData->setSchemaFields([
+ 'entity_test_mulrev' => ['id', 'revision_id', 'uuid', 'type'],
+ 'entity_test_mulrev_revision' => ['id', 'revision_id', 'langcode'],
+ 'entity_test_mulrev_property_data' => ['id', 'revision_id', 'langcode', 'name', 'user_id'],
+ 'entity_test_mulrev_property_revision' => ['id', 'revision_id', 'langcode', 'name', 'user_id'],
+ ]);
+
+ ;
+ $this->viewsData->setEntityType($entity_type);
+
+ // Setup the table mapping.
+ $table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
+ $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']);
+ $table_mapping->expects($this->any())
+ ->method('getColumnNames')
+ ->willReturnMap([
+ ['id', ['value' => 'id']],
+ ['uuid', ['value' => 'uuid']],
+ ['type', ['value' => 'type']],
+ ['langcode', ['value' => 'langcode']],
+ ['name', ['value' => 'name']],
+ ['user_id', ['target_id' => 'user_id']],
+ ['revision_id', ['value' => 'id']],
+ ]);
+ $table_mapping->expects($this->any())
+ ->method('getFieldNames')
+ ->willReturnMap([
+ ['entity_test_mulrev', ['id', 'revision_id', 'uuid', 'type']],
+ ['entity_test_mulrev_revision', ['id', 'revision_id', 'langcode']],
+ ['entity_test_mulrev_property_data', ['id', 'revision_id', 'langcode', 'name', 'user_id']],
+ ['entity_test_mulrev_property_revision', ['id', 'revision_id', 'langcode', 'name', 'user_id']],
+ ]);
+
+ $this->entityStorage->expects($this->once())
+ ->method('getTableMapping')
+ ->willReturn($table_mapping);
+
+ $this->setupFieldStorageDefinition();
+
+ $data = $this->viewsData->getViewsData();
+
+ // Check the base fields.
+ $this->assertNumericField($data['entity_test_mulrev']['id']);
+ $this->assertNumericField($data['entity_test_mulrev']['revision_id']);
+ $this->assertUuidField($data['entity_test_mulrev']['uuid']);
+ $this->assertStringField($data['entity_test_mulrev']['type']);
+
+ // Also ensure that field_data only fields don't appear on the base table.
+ $this->assertFalse(isset($data['entity_test_mulrev']['name']));
+ $this->assertFalse(isset($data['entity_test_mulrev']['langcode']));
+ $this->assertFalse(isset($data['entity_test_mulrev']['user_id']));
+
+ // Check the revision fields.
+ $this->assertNumericField($data['entity_test_mulrev_revision']['id']);
+ $this->assertNumericField($data['entity_test_mulrev_revision']['revision_id']);
+
+ $this->assertLanguageField($data['entity_test_mulrev_revision']['langcode']);
+ $this->assertEquals('Original language', $data['entity_test_mulrev_revision']['langcode']['title']);
+
+ // Also ensure that field_data only fields don't appear on the revision table.
+ $this->assertFalse(isset($data['entity_test_mulrev_revision']['name']));
+ $this->assertFalse(isset($data['entity_test_mulrev_revision']['user_id']));
+
+ // Check the data fields.
+ $this->assertNumericField($data['entity_test_mulrev_property_data']['id']);
+ $this->assertLanguageField($data['entity_test_mulrev_property_data']['langcode']);
+ $this->assertStringField($data['entity_test_mulrev_property_data']['name']);
+
+ $this->assertEntityReferenceField($data['entity_test_mulrev_property_data']['user_id']);
+ $relationship = $data['entity_test_mulrev_property_data']['user_id']['relationship'];
+ $this->assertEquals('users', $relationship['base']);
+ $this->assertEquals('uid', $relationship['base field']);
+
+ // Check the property data fields.
+ $this->assertNumericField($data['entity_test_mulrev_property_revision']['id']);
+
+ $this->assertLanguageField($data['entity_test_mulrev_property_revision']['langcode']);
+ $this->assertEquals('Original language', $data['entity_test_mulrev_property_revision']['langcode']['title']);
+
+ $this->assertStringField($data['entity_test_mulrev_property_revision']['name']);
+
+ $this->assertEntityReferenceField($data['entity_test_mulrev_property_revision']['user_id']);
+ $relationship = $data['entity_test_mulrev_property_revision']['user_id']['relationship'];
+ $this->assertEquals('users', $relationship['base']);
+ $this->assertEquals('uid', $relationship['base field']);
+ }
+
+ /**
+ * Tests views data for a string field.
+ *
+ * @param $data
+ * The views data to check.
+ */
+ protected function assertStringField($data) {
+ $this->assertEquals('standard', $data['field']['id']);
+ $this->assertEquals('string', $data['filter']['id']);
+ $this->assertEquals('string', $data['argument']['id']);
+ $this->assertEquals('standard', $data['sort']['id']);
+ }
+
+ /**
+ * Tests views data for a UUID field.
+ *
+ * @param array $data
+ * The views data to check.
+ */
+ protected function assertUuidField($data) {
+ // @todo Can we provide additional support for UUIDs in views?
+ $this->assertEquals('standard', $data['field']['id']);
+ $this->assertEquals('string', $data['filter']['id']);
+ $this->assertEquals('string', $data['argument']['id']);
+ $this->assertEquals('standard', $data['sort']['id']);
+ }
+
+ /**
+ * Tests views data for a numeric field.
+ *
+ * @param array $data
+ * The views data to check.
+ */
+ protected function assertNumericField($data) {
+ $this->assertEquals('numeric', $data['field']['id']);
+ $this->assertEquals('numeric', $data['filter']['id']);
+ $this->assertEquals('numeric', $data['argument']['id']);
+ $this->assertEquals('standard', $data['sort']['id']);
+ }
+
+ /**
+ * Tests views data for a language field.
+ *
+ * @param array $data
+ * The views data to check.
+ */
+ protected function assertLanguageField($data) {
+ $this->assertEquals('language', $data['field']['id']);
+ $this->assertEquals('language', $data['filter']['id']);
+ $this->assertEquals('language', $data['argument']['id']);
+ $this->assertEquals('standard', $data['sort']['id']);
+ }
+
+ /**
+ * Tests views data for a entity reference field.
+ */
+ protected function assertEntityReferenceField($data) {
+ $this->assertEquals('numeric', $data['field']['id']);
+ $this->assertEquals('numeric', $data['filter']['id']);
+ $this->assertEquals('numeric', $data['argument']['id']);
+ $this->assertEquals('standard', $data['sort']['id']);
+ }
+
+ /**
+ * Tests views data for a bundle field.
+ */
+ protected function assertBundleField($data) {
+ $this->assertEquals('standard', $data['field']['id']);
+ $this->assertEquals('bundle', $data['filter']['id']);
+ $this->assertEquals('string', $data['argument']['id']);
+ $this->assertEquals('standard', $data['sort']['id']);
+ }
+
+ /**
+ * Returns entity info for the user entity.
+ *
+ * @return array
+ */
+ protected static function userEntityInfo() {
+ return new ContentEntityType([
+ 'id' => 'user',
+ 'class' => 'Drupal\user\Entity\User',
+ 'label' => 'User',
+ 'base_table' => 'users',
+ 'entity_keys' => [
+ 'id' => 'uid',
+ 'uuid' => 'uuid',
+ ],
+ ]);
+ }
+
+}
+
+class TestEntityViewsData extends EntityViewsData {
+
+ protected $schemaFields = [];
+
+ public function setSchemaFields($fields) {
+ $this->schemaFields = $fields;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function drupalSchemaFieldsSql($table) {
+ return isset($this->schemaFields[$table]) ? $this->schemaFields[$table] : [];
+ }
+
+ public function setEntityType(EntityTypeInterface $entity_type) {
+ $this->entityType = $entity_type;
+ }
+
+}
+
+}
+
+namespace {
+ use Drupal\Component\Utility\String;
+
+ if (!function_exists('t')) {
+ function t($string, array $args = []) {
+ return String::format($string, $args);
+ }
+ }
+}
diff --git a/core/modules/views/views.api.php b/core/modules/views/views.api.php
index c7e5133..5534df6 100644
--- a/core/modules/views/views.api.php
+++ b/core/modules/views/views.api.php
@@ -26,9 +26,10 @@
* by implementing hook_views_data_alter(). To provide views data for an
* entity, create a class implementing
* \Drupal\views\EntityViewsDataInterface and reference this in the
- * "views_data" annotation in the entity class. See the
- * @link entity_api Entity API topic @endlink for more information about
- * entities.
+ * "views_data" annotation in the entity class. You can autogenerate big parts
+ * of the ingration if you extend the \Drupal\views\EntityViewsData base
+ * class. See the @link entity_api Entity API topic @endlink for more
+ * information about entities.
* - Implement hooks: A few operations in Views can be influenced by hooks.
* See the @link Views hooks topic @endlink for a list.
* - Theming: See the @link views_templates Views templates topic @endlink
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php
index 4ff2f23..7109a62 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php
@@ -76,6 +76,19 @@ public function providerTestGetKeys() {
}
/**
+ * Tests the setKey() method.
+ *
+ * @covers ::setKey
+ */
+ public function testSetKey() {
+ $entity_type = $this->setUpEntityType([]);
+
+ $this->assertFalse($entity_type->getKey('id'));
+ $this->assertSame($entity_type, $entity_type->setKey('id', 'new_value'));
+ $this->assertEquals('new_value', $entity_type->getKey('id'));
+ }
+
+ /**
* Tests the isRevisionable() method.
*/
public function testIsRevisionable() {