diff --git a/core/modules/views/src/Entity/View.php b/core/modules/views/src/Entity/View.php index 88546ea..17c8077 100644 --- a/core/modules/views/src/Entity/View.php +++ b/core/modules/views/src/Entity/View.php @@ -100,6 +100,13 @@ class View extends ConfigEntityBase implements ViewStorageInterface { protected $executable; /** + * The views data. + * + * @var \Drupal\views\ViewsData + */ + protected $viewsData; + + /** * The module implementing this view. * * @var string @@ -122,6 +129,18 @@ public function getExecutable() { } /** + * Gets the views data. + * + * @return \Drupal\views\ViewsData + */ + protected function getViewsData() { + if (!isset($this->viewsData)) { + $this->viewsData = Views::viewsData(); + } + return $this->viewsData; + } + + /** * Overrides Drupal\Core\Config\Entity\ConfigEntityBase::createDuplicate(). */ public function createDuplicate() { @@ -248,20 +267,12 @@ public function calculateDependencies() { // Ensure that the view is dependant on the module that implements the view. $this->addDependency('module', $this->module); - // Ensure that the view is dependent on the module that provides the schema - // for the base table. - $schema = $this->drupalGetSchema($this->base_table); - // @todo Entity base tables are no longer registered in hook_schema(). Once - // we automate the views data for entity types add the entity type - // type provider as a dependency. See https://drupal.org/node/1740492. - if ($schema && $this->module != $schema['module']) { - $this->addDependency('module', $schema['module']); - } $handler_types = array(); foreach (Views::getHandlerTypes() as $type) { $handler_types[] = $type['plural']; } + foreach ($this->get('display') as $display) { // Add dependency for the display itself. if (isset($display['provider'])) { @@ -390,11 +401,4 @@ public function mergeDefaultDisplaysOptions() { $this->set('display', $displays); } - /** - * Wraps drupal_get_schema(). - */ - protected function drupalGetSchema($table = NULL, $rebuild = FALSE) { - return drupal_get_schema($table, $rebuild); - } - } diff --git a/core/modules/views/src/EntityViewsData.php b/core/modules/views/src/EntityViewsData.php index b2859d0..8593ce9 100644 --- a/core/modules/views/src/EntityViewsData.php +++ b/core/modules/views/src/EntityViewsData.php @@ -129,6 +129,7 @@ public function getViewsData() { // 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']['provider'] = $this->entityType->getProvider(); $data[$base_table]['table']['base'] = [ 'field' => $base_field, 'title' => $this->entityType->getLabel(), @@ -157,10 +158,12 @@ public function getViewsData() { ]; $data[$data_table]['table']['entity type'] = $this->entityType->id(); $data[$data_table]['table']['group'] = $this->entityType->getLabel(); + $data[$data_table]['table']['provider'] = $this->entityType->getProvider(); } 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']['provider'] = $this->entityType->getProvider(); $data[$revision_table]['table']['base'] = array( 'field' => $revision_field, 'title' => $this->t('@entity_type revisions', array('@entity_type' => $this->entityType->getLabel())), diff --git a/core/modules/views/src/Plugin/views/query/QueryPluginBase.php b/core/modules/views/src/Plugin/views/query/QueryPluginBase.php index a6dcb95..5d3d6bb 100644 --- a/core/modules/views/src/Plugin/views/query/QueryPluginBase.php +++ b/core/modules/views/src/Plugin/views/query/QueryPluginBase.php @@ -118,6 +118,22 @@ public function summaryTitle() { } /** + * {@inheritdoc} + */ + public function getDependencies() { + $dependencies = []; + + foreach ($this->getEntityTableInfo() as $entity_type => $info) { + if (!empty($info['provider'])) { + $dependencies['module'][] = $info['provider']; + $dependencies['entity'][] = $entity_type; + } + } + + return $dependencies; + } + + /** * Set a LIMIT on the query, specifying a maximum number of results. */ public function setLimit($limit) { @@ -260,7 +276,13 @@ public function getEntityTableInfo() { 'entity_type' => $base_table_data['table']['entity type'], 'revision' => FALSE, ); + + // Include the entity provider. + if (!empty($table_data['table']['provider'])) { + $entity_tables[$table_data['table']['entity type']]['provider'] = $table_data['table']['provider']; + } } + // Include all relationships. foreach ($this->view->relationship as $relationship_id => $relationship) { $table_data = $views_data->get($relationship->definition['base']); @@ -272,6 +294,11 @@ public function getEntityTableInfo() { 'entity_type' => $table_data['table']['entity type'], 'revision' => FALSE, ); + + // Include the entity provider. + if (!empty($table_data['table']['provider'])) { + $entity_tables[$table_data['table']['entity type']]['provider'] = $table_data['table']['provider']; + } } } @@ -286,15 +313,6 @@ public function getEntityTableInfo() { return $entity_tables; } - /** - * {@inheritdoc} - */ - public function getDependencies() { - // Return an array of all entity types used in the query including - // relationships. - return array('entity' => array_keys($this->getEntityTableInfo())); - } - } /** diff --git a/core/modules/views/src/ViewsData.php b/core/modules/views/src/ViewsData.php index bd65aa8..0fbe5fc 100644 --- a/core/modules/views/src/ViewsData.php +++ b/core/modules/views/src/ViewsData.php @@ -7,6 +7,7 @@ namespace Drupal\views; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Config\ConfigFactoryInterface; @@ -230,7 +231,19 @@ protected function getData() { return $data->data; } else { - $data = $this->moduleHandler->invokeAll('views_data'); + $modules = $this->moduleHandler->getImplementations('views_data'); + $data = array(); + foreach ($modules as $module) { + $views_data = $this->moduleHandler->invoke($module, 'views_data'); + + // Set the provider key for each base table. + foreach ($views_data as &$table) { + if (isset($table['table']) && !isset($table['table']['provider'])) { + $table['table']['provider'] = $module; + } + } + $data = NestedArray::mergeDeep($data, $views_data); + } $this->moduleHandler->alter('views_data', $data); $this->processEntityTypes($data); diff --git a/core/modules/views/tests/Drupal/views/Tests/EntityViewsDataTest.php b/core/modules/views/tests/Drupal/views/Tests/EntityViewsDataTest.php index 4618309..c9de1eb 100644 --- a/core/modules/views/tests/Drupal/views/Tests/EntityViewsDataTest.php +++ b/core/modules/views/tests/Drupal/views/Tests/EntityViewsDataTest.php @@ -34,7 +34,7 @@ class EntityViewsDataTest extends UnitTestCase { /** * Entity info to use in this test. * - * @var \Drupal\Core\Entity\EntityTypeInterface + * @var \Drupal\Core\Entity\EntityTypeInterface|\Drupal\views\Tests\TestEntityType */ protected $baseEntityType; @@ -87,6 +87,7 @@ protected function setUp() { 'id' => 'entity_test', 'label' => 'Entity test', 'entity_keys' => ['id' => 'id'], + 'provider' => 'entity_test', ]); $this->translationManager = $this->getStringTranslationStub(); @@ -134,6 +135,7 @@ public function testBaseTables() { $this->assertEquals('entity_test', $data['entity_test']['table']['entity type']); $this->assertEquals('Entity test', $data['entity_test']['table']['group']); + $this->assertEquals('entity_test', $data['entity_test']['table']['provider']); $this->assertEquals('id', $data['entity_test']['table']['base']['field']); $this->assertEquals('Entity test', $data['entity_test']['table']['base']['title']); @@ -162,6 +164,7 @@ public function testDataTable() { $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('entity_test', $data['entity_test']['table']['provider']); $this->assertEquals(['field' => 'label', 'table' => 'entity_test_mul_property_data'], $data['entity_test']['table']['base']['defaults']); // Ensure the join information is set up properly. @@ -188,6 +191,7 @@ public function testRevisionTable() { $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']); + $this->assertEquals('entity_test', $data['entity_test']['table']['provider']); // Ensure the join information is set up properly. // Tests the join definition between the base and the revision table. diff --git a/core/modules/views/tests/src/Unit/Entity/ViewTest.php b/core/modules/views/tests/src/Unit/Entity/ViewTest.php index c2c71e9..d7b6708 100644 --- a/core/modules/views/tests/src/Unit/Entity/ViewTest.php +++ b/core/modules/views/tests/src/Unit/Entity/ViewTest.php @@ -46,21 +46,29 @@ protected function setUp() { */ public function testCalculateDependencies($values, $deps) { $view = new TestView($values, 'view'); - $this->assertEquals(array('module' => $deps), $view->calculateDependencies()); + $views_deps = $view->calculateDependencies(); + $this->assertEquals(array('module' => $deps), $views_deps); } + /** + * Data provider for testCalculateDependencies. + */ public function calculateDependenciesProvider(){ + $handler = array(); $handler['display']['default']['provider'] = 'block'; $handler['display']['default']['display_options']['fields']['example']['dependencies'] = array(); $handler['display']['default']['display_options']['fields']['example2']['dependencies']['module'] = array('views', 'field'); $handler['display']['default']['display_options']['fields']['example3']['dependencies']['module'] = array('views', 'image'); + $handler['display']['default']['display_options']['query']['dependencies']['module'] = array('node'); + $plugin = array(); $plugin['display']['default']['display_options']['access']['dependencies'] = array(); $plugin['display']['default']['display_options']['row']['dependencies']['module'] = array('views', 'field'); $plugin['display']['default']['display_options']['style']['dependencies']['module'] = array('views', 'image'); + $plugin['display']['default']['display_options']['query']['dependencies']['module'] = array('node'); return array( - array(array(), array('node', 'views')), + array(array(), array('views')), array($handler, array('block', 'field', 'image', 'node', 'views')), array($plugin, array('field', 'image', 'node', 'views')), ); diff --git a/core/modules/views/tests/src/Unit/ViewsDataTest.php b/core/modules/views/tests/src/Unit/ViewsDataTest.php index 43528c3..0476ffd 100644 --- a/core/modules/views/tests/src/Unit/ViewsDataTest.php +++ b/core/modules/views/tests/src/Unit/ViewsDataTest.php @@ -109,20 +109,51 @@ protected function viewsData() { } /** - * Tests the fetchBaseTables() method. + * Returns the views data definition with the provider key. + * + * @return array + * + * @see static::viewsData() */ - public function testFetchBaseTables() { - $views_data = $this->viewsData(); + protected function viewsDataWithProvider() { + $views_data = static::viewsData(); + foreach (array_keys($views_data) as $table) { + $views_data[$table]['table']['provider'] = 'views_test_data'; + } + return $views_data; + } - $this->moduleHandler->expects($this->once()) - ->method('invokeAll') + /** + * Mocks the basic module handler used for the test. + * + * @return \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected function setupMockedModuleHandler() { + $views_data = $this->viewsData(); + $this->moduleHandler->expects($this->at(0)) + ->method('getImplementations') ->with('views_data') - ->will($this->returnValue($views_data)); + ->willReturn(array('views_test_data')); + $this->moduleHandler->expects($this->at(1)) + ->method('invoke') + ->with('views_test_data', 'views_data') + ->willReturn($views_data); + } + /** + * Tests the fetchBaseTables() method. + */ + public function testFetchBaseTables() { + $this->setupMockedModuleHandler(); $data = $this->viewsData->get(); $base_tables = $this->viewsData->fetchBaseTables(); + // Ensure that 'provider' is set for each base table. + foreach (array_keys($base_tables) as $base_table) { + $this->assertEquals('views_test_data', $data[$base_table]['table']['provider']); + } + // Test the number of tables returned and their order. $this->assertCount(6, $base_tables, 'The correct amount of base tables were returned.'); $base_tables_keys = array_keys($base_tables); @@ -151,24 +182,19 @@ public function testFetchBaseTables() { * Tests fetching all the views data without a static cache. */ public function testGetOnFirstCall() { - $views_data = $this->viewsData(); - // Ensure that the hooks are just invoked once. - $this->moduleHandler->expects($this->once()) - ->method('invokeAll') - ->with('views_data') - ->will($this->returnValue($views_data)); + $this->setupMockedModuleHandler(); - $this->moduleHandler->expects($this->once()) + $this->moduleHandler->expects($this->at(2)) ->method('alter') - ->with('views_data', $views_data); + ->with('views_data', $this->viewsDataWithProvider()); $this->cacheBackend->expects($this->once()) ->method('get') ->with("views_data:en") ->will($this->returnValue(FALSE)); - $expected_views_data = $this->viewsData(); + $expected_views_data = $this->viewsDataWithProvider(); $views_data = $this->viewsData->get(); $this->assertSame($expected_views_data, $views_data); } @@ -177,21 +203,37 @@ public function testGetOnFirstCall() { * Tests the cache of the full and single table data. */ public function testFullAndTableGetCache() { - $expected_views_data = $this->viewsData(); + $expected_views_data = $this->viewsDataWithProvider(); $table_name = 'views_test_data'; $table_name_2 = 'views_test_data_2'; $random_table_name = $this->randomMachineName(); // Views data should be invoked twice due to the clear call. - $this->moduleHandler->expects($this->exactly(2)) - ->method('invokeAll') + $this->moduleHandler->expects($this->at(0)) + ->method('getImplementations') ->with('views_data') - ->will($this->returnValue($expected_views_data)); + ->willReturn(array('views_test_data')); + $this->moduleHandler->expects($this->at(1)) + ->method('invoke') + ->with('views_test_data', 'views_data') + ->willReturn($this->viewsData()); + $this->moduleHandler->expects($this->at(2)) + ->method('alter') + ->with('views_data', $expected_views_data); - $this->moduleHandler->expects($this->exactly(2)) + $this->moduleHandler->expects($this->at(3)) + ->method('getImplementations') + ->with('views_data') + ->willReturn(array('views_test_data')); + $this->moduleHandler->expects($this->at(4)) + ->method('invoke') + ->with('views_test_data', 'views_data') + ->willReturn($this->viewsData()); + $this->moduleHandler->expects($this->at(5)) ->method('alter') ->with('views_data', $expected_views_data); + // The cache should only be called once (before the clear() call) as get // will get all table data in the first get(). $this->cacheBackend->expects($this->at(0)) @@ -252,13 +294,10 @@ public function testFullAndTableGetCache() { * Tests the caching of the full views data. */ public function testFullGetCache() { - $expected_views_data = $this->viewsData(); + $expected_views_data = $this->viewsDataWithProvider(); // Views data should be invoked once. - $this->moduleHandler->expects($this->once()) - ->method('invokeAll') - ->with('views_data') - ->will($this->returnValue($expected_views_data)); + $this->setupMockedModuleHandler(); $this->moduleHandler->expects($this->once()) ->method('alter') @@ -281,17 +320,14 @@ public function testFullGetCache() { */ public function testSingleTableGetCache() { $table_name = 'views_test_data'; - $expected_views_data = $this->viewsData(); + $expected_views_data = $this->viewsDataWithProvider(); // Views data should be invoked once. - $this->moduleHandler->expects($this->once()) - ->method('invokeAll') - ->with('views_data') - ->will($this->returnValue($expected_views_data)); + $this->setupMockedModuleHandler(); $this->moduleHandler->expects($this->once()) ->method('alter') - ->with('views_data', $expected_views_data); + ->with('views_data', $this->viewsDataWithProvider()); $this->cacheBackend->expects($this->at(0)) ->method('get') @@ -320,17 +356,13 @@ public function testSingleTableGetCache() { */ public function testNonExistingTableGetCache() { $random_table_name = $this->randomMachineName(); - $expected_views_data = $this->viewsData(); // Views data should be invoked once. - $this->moduleHandler->expects($this->once()) - ->method('invokeAll') - ->with('views_data') - ->will($this->returnValue($expected_views_data)); + $this->setupMockedModuleHandler(); $this->moduleHandler->expects($this->once()) ->method('alter') - ->with('views_data', $expected_views_data); + ->with('views_data', $this->viewsDataWithProvider()); $this->cacheBackend->expects($this->at(0)) ->method('get') @@ -354,12 +386,9 @@ public function testNonExistingTableGetCache() { * Tests the cache backend behavior with requesting the same table multiple */ public function testCacheCallsWithSameTableMultipleTimes() { - $expected_views_data = $this->viewsData(); + $expected_views_data = $this->viewsDataWithProvider(); - $this->moduleHandler->expects($this->any()) - ->method('invokeAll') - ->with('views_data') - ->will($this->returnValue($expected_views_data)); + $this->setupMockedModuleHandler(); $this->cacheBackend->expects($this->at(0)) ->method('get') @@ -392,9 +421,9 @@ public function testCacheCallsWithSameTableMultipleTimes() { * - views_test_data */ public function testCacheCallsWithSameTableMultipleTimesAndWarmCache() { - $expected_views_data = $this->viewsData(); + $expected_views_data = $this->viewsDataWithProvider(); $this->moduleHandler->expects($this->never()) - ->method('invokeAll'); + ->method('getImplementations'); // Setup a warm cache backend for a single table. $this->cacheBackend->expects($this->once()) @@ -422,9 +451,9 @@ public function testCacheCallsWithSameTableMultipleTimesAndWarmCache() { * - views_test_data_2 */ public function testCacheCallsWithWarmCacheAndDifferentTable() { - $expected_views_data = $this->viewsData(); + $expected_views_data = $this->viewsDataWithProvider(); $this->moduleHandler->expects($this->never()) - ->method('invokeAll'); + ->method('getImplementations'); // Setup a warm cache backend for a single table. $this->cacheBackend->expects($this->at(0)) @@ -457,10 +486,10 @@ public function testCacheCallsWithWarmCacheAndDifferentTable() { * - $non_existing_table */ public function testCacheCallsWithWarmCacheAndInvalidTable() { - $expected_views_data = $this->viewsData(); + $expected_views_data = $this->viewsDataWithProvider(); $non_existing_table = $this->randomMachineName(); $this->moduleHandler->expects($this->never()) - ->method('invokeAll'); + ->method('getImplementations'); // Setup a warm cache backend for a single table. $this->cacheBackend->expects($this->at(0)) @@ -496,7 +525,7 @@ public function testCacheCallsWithWarmCacheAndInvalidTable() { public function testCacheCallsWithWarmCacheForInvalidTable() { $non_existing_table = $this->randomMachineName(); $this->moduleHandler->expects($this->never()) - ->method('invokeAll'); + ->method('getImplementations'); // Setup a warm cache backend for a single table. $this->cacheBackend->expects($this->once()) @@ -521,10 +550,8 @@ public function testCacheCallsWithWarmCacheForInvalidTable() { * Tests the cache calls for all views data without a warm cache. */ public function testCacheCallsWithoutWarmCacheAndGetAllTables() { - $expected_views_data = $this->viewsData(); - $this->moduleHandler->expects($this->once()) - ->method('invokeAll') - ->will($this->returnValue($expected_views_data)); + $expected_views_data = $this->viewsDataWithProvider(); + $this->setupMockedModuleHandler(); // Setup a warm cache backend for a single table. $this->cacheBackend->expects($this->once()) @@ -549,9 +576,9 @@ public function testCacheCallsWithoutWarmCacheAndGetAllTables() { * - all tables */ public function testCacheCallsWithWarmCacheAndGetAllTables() { - $expected_views_data = $this->viewsData(); + $expected_views_data = $this->viewsDataWithProvider(); $this->moduleHandler->expects($this->never()) - ->method('invokeAll'); + ->method('getImplementations'); // Setup a warm cache backend for a single table. $this->cacheBackend->expects($this->once()) @@ -575,7 +602,7 @@ public function testCacheCallsWithWarmCacheAndGetAllTables() { * @covers ::get */ public function testCacheCallsWithoutWarmCacheAndGetMultipleTables() { - $expected_views_data = $this->viewsData(); + $expected_views_data = $this->viewsDataWithProvider(); $table_name = 'views_test_data'; $table_name_2 = 'views_test_data_2';