Commit 01567a63 authored by webchick's avatar webchick

Issue #1867782 by damiankloip, tim.plunkett: Replace _views_fetch_data() with cache class.

parent dc6c7a4b
...@@ -128,7 +128,7 @@ function get_base_table() { ...@@ -128,7 +128,7 @@ function get_base_table() {
$relationships = $this->view->display_handler->getOption('relationships'); $relationships = $this->view->display_handler->getOption('relationships');
if (!empty($relationships[$this->options['relationship']])) { if (!empty($relationships[$this->options['relationship']])) {
$options = $relationships[$this->options['relationship']]; $options = $relationships[$this->options['relationship']];
$data = views_fetch_data($options['table']); $data = drupal_container()->get('views.views_data')->get($options['table']);
$this->base_table = $data[$options['field']]['relationship']['base']; $this->base_table = $data[$options['field']]['relationship']['base'];
} }
} }
......
...@@ -40,7 +40,7 @@ public function query() { ...@@ -40,7 +40,7 @@ public function query() {
$this->ensureMyTable(); $this->ensureMyTable();
// First, relate our base table to the current base table to the // First, relate our base table to the current base table to the
// field, using the base table's id field to the field's column. // field, using the base table's id field to the field's column.
$views_data = views_fetch_data($this->table); $views_data = drupal_container()->get('views.views_data')->get($this->table);
$left_field = $views_data['table']['base']['field']; $left_field = $views_data['table']['base']['field'];
$first = array( $first = array(
......
...@@ -71,9 +71,6 @@ function setUp() { ...@@ -71,9 +71,6 @@ function setUp() {
); );
$this->nodes[] = $this->drupalCreateNode($edit); $this->nodes[] = $this->drupalCreateNode($edit);
} }
// Reset views data cache.
$this->clearViewsCaches();
} }
/** /**
...@@ -82,7 +79,7 @@ function setUp() { ...@@ -82,7 +79,7 @@ function setUp() {
* We check data structure for both node and node revision tables. * We check data structure for both node and node revision tables.
*/ */
function testViewsData() { function testViewsData() {
$data = views_fetch_data(); $data = drupal_container()->get('views.views_data')->get();
// Check the table and the joins of the first field. // Check the table and the joins of the first field.
// Attached to node only. // Attached to node only.
......
...@@ -74,14 +74,4 @@ function setUpInstances($bundle = 'page') { ...@@ -74,14 +74,4 @@ function setUpInstances($bundle = 'page') {
} }
} }
/**
* Clear all views caches and static caches which are required for the patch.
*/
function clearViewsCaches() {
// Reset views data cache.
drupal_static_reset('_views_fetch_data_cache');
drupal_static_reset('_views_fetch_data_recursion_protected');
drupal_static_reset('_views_fetch_data_fully_loaded');
}
} }
...@@ -52,8 +52,6 @@ protected function setUp() { ...@@ -52,8 +52,6 @@ protected function setUp() {
$this->setUpInstances(); $this->setUpInstances();
$this->clearViewsCaches();
// Create some nodes. // Create some nodes.
$this->nodes = array(); $this->nodes = array();
for ($i = 0; $i < 3; $i++) { for ($i = 0; $i < 3; $i++) {
......
<?php
/**
* @file
* Load Views' data so that it knows what is available to build queries from.
*/
/**
* Fetch Views' data from the cache.
*
* $param string|null $table
* (optional) The name of the table for which to fetch Views' data. If
* NULL, data for all tables will be retrieved
* @param bool $reset
* (optional) Whether to rebuild the cache. Defaults to FALSE.
*
* @return array
* An associative array of views data for the given table. If $table is
* NULL, the array will be keyed by table name, with each key corresponding
* to the data array for that table.
*
* @see hook_views_data()
*/
function _views_fetch_data($table = NULL, $reset = FALSE) {
$cache = &drupal_static(__FUNCTION__ . '_cache');
$recursion_protection = &drupal_static(__FUNCTION__ . '_recursion_protected');
$fully_loaded = &drupal_static(__FUNCTION__ . '_fully_loaded');
if ($reset) {
$cache = NULL;
$fully_loaded = FALSE;
}
if ($table) {
if (!isset($cache[$table])) {
$cid = 'views_data:' . $table;
$data = views_cache_get($cid, TRUE);
if (!empty($data->data)) {
$cache[$table] = $data->data;
}
else {
// No cache entry, rebuild.
$cache = _views_fetch_data_build();
$fully_loaded = TRUE;
}
}
if (isset($cache[$table])) {
return $cache[$table];
}
}
else {
if (!$fully_loaded) {
$data = views_cache_get('views_data', TRUE);
if (!empty($data->data)) {
$cache = $data->data;
}
if (empty($cache)) {
$cache = _views_fetch_data_build();
}
$fully_loaded = TRUE;
}
return $cache;
}
// Return an empty array if there is no match.
return array();
}
/**
* Build, set the views data cache if empty and return the views data.
*
* @return array
* The views_data of all tables.
*/
function _views_fetch_data_build() {
$cache = module_invoke_all('views_data');
foreach (module_implements('views_data_alter') as $module) {
$function = $module . '_views_data_alter';
$function($cache);
}
_views_data_process_entity_types($cache);
// Keep a record with all data.
views_cache_set('views_data', $cache, TRUE);
// Save data in seperate cache entries.
foreach ($cache as $key => $data) {
$cid = 'views_data:' . $key;
views_cache_set($cid, $data, TRUE);
}
return $cache;
}
/**
* Links tables having an 'entity type' specified to the respective generic entity-type tables.
*/
function _views_data_process_entity_types(&$data) {
foreach ($data as $table_name => $table_info) {
// Add in a join from the entity-table if an entity-type is given.
if (!empty($table_info['table']['entity type'])) {
$entity_table = 'views_entity_' . $table_info['table']['entity type'];
$data[$entity_table]['table']['join'][$table_name] = array(
'left_table' => $table_name,
);
$data[$entity_table]['table']['entity type'] = $table_info['table']['entity type'];
// Copy over the default table group if we have none yet.
if (!empty($table_info['table']['group']) && empty($data[$entity_table]['table']['group'])) {
$data[$entity_table]['table']['group'] = $table_info['table']['group'];
}
}
}
}
/**
* Set a cached item in the views cache.
*
* This is just a convenience wrapper around cache_set().
*
* @param $cid
* The cache ID of the data to store.
* @param $data
* The data to store in the cache. Complex data types will be automatically serialized before insertion.
* Strings will be stored as plain text and not serialized.
* @param $use_language
* If TRUE, the data will be cached specific to the currently active language.
*/
function views_cache_set($cid, $data, $use_language = FALSE) {
if (config('views.settings')->get('skip_cache')) {
return;
}
if ($use_language) {
$cid .= ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode;
}
cache('views_info')->set($cid, $data);
}
/**
* Return data from the persistent views cache.
*
* This is just a convenience wrapper around cache_get().
*
* @param int $cid
* The cache ID of the data to retrieve.
* @param bool $use_language
* If TRUE, the data will be requested specific to the currently active language.
*
* @return stdClass|bool
* The cache or FALSE on failure.
*/
function views_cache_get($cid, $use_language = FALSE) {
if (config('views.settings')->get('skip_cache')) {
return FALSE;
}
if ($use_language) {
$cid .= ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode;
}
return cache('views_info')->get($cid);
}
...@@ -37,8 +37,9 @@ public function getDerivativeDefinition($derivative_id, array $base_plugin_defin ...@@ -37,8 +37,9 @@ public function getDerivativeDefinition($derivative_id, array $base_plugin_defin
public function getDerivativeDefinitions(array $base_plugin_definition) { public function getDerivativeDefinitions(array $base_plugin_definition) {
$base_tables = array_keys(views_fetch_base_tables()); $base_tables = array_keys(views_fetch_base_tables());
$this->derivatives = array(); $this->derivatives = array();
$views_data = drupal_container()->get('views.views_data');
foreach ($base_tables as $table) { foreach ($base_tables as $table) {
$views_info = views_fetch_data($table); $views_info = $views_data->get($table);
if (empty($views_info['table']['wizard_id'])) { if (empty($views_info['table']['wizard_id'])) {
$this->derivatives[$table] = array( $this->derivatives[$table] = array(
'id' => 'standard', 'id' => 'standard',
......
...@@ -770,7 +770,7 @@ public static function getTimezone() { ...@@ -770,7 +770,7 @@ public static function getTimezone() {
* @return Drupal\views\Plugin\views\join\JoinPluginBase * @return Drupal\views\Plugin\views\join\JoinPluginBase
*/ */
public static function getTableJoin($table, $base_table) { public static function getTableJoin($table, $base_table) {
$data = views_fetch_data($table); $data = drupal_container()->get('views.views_data')->get($table);
if (isset($data['table']['join'][$base_table])) { if (isset($data['table']['join'][$base_table])) {
$join_info = $data['table']['join'][$base_table]; $join_info = $data['table']['join'][$base_table];
if (!empty($join_info['join_id'])) { if (!empty($join_info['join_id'])) {
...@@ -815,10 +815,10 @@ public function getEntityType() { ...@@ -815,10 +815,10 @@ public function getEntityType() {
// If the user has configured a relationship on the handler take that into // If the user has configured a relationship on the handler take that into
// account. // account.
if (!empty($this->options['relationship']) && $this->options['relationship'] != 'none') { if (!empty($this->options['relationship']) && $this->options['relationship'] != 'none') {
$views_data = views_fetch_data($this->view->relationship->table); $views_data = drupal_container()->get('views.views_data')->get($this->view->relationship->table);
} }
else { else {
$views_data = views_fetch_data($this->view->storage->get('base_table')); $views_data = drupal_container()->get('views.views_data')->get($this->view->storage->get('base_table'));
} }
if (isset($views_data['table']['entity type'])) { if (isset($views_data['table']['entity type'])) {
......
...@@ -127,7 +127,6 @@ public function initDisplay(ViewExecutable $view, array &$display, array &$optio ...@@ -127,7 +127,6 @@ public function initDisplay(ViewExecutable $view, array &$display, array &$optio
unset($options['defaults']); unset($options['defaults']);
} }
views_include('cache');
// Cache for unpackOptions, but not if we are in the ui. // Cache for unpackOptions, but not if we are in the ui.
static $unpack_options = array(); static $unpack_options = array();
if (empty($view->editing)) { if (empty($view->editing)) {
...@@ -799,7 +798,7 @@ public function getPlugin($type) { ...@@ -799,7 +798,7 @@ public function getPlugin($type) {
// Query plugins allow specifying a specific query class per base table. // Query plugins allow specifying a specific query class per base table.
if ($type == 'query') { if ($type == 'query') {
$views_data = views_fetch_data($this->view->storage->get('base_table')); $views_data = drupal_container()->get('views.views_data')->get($this->view->storage->get('base_table'));
$name = isset($views_data['table']['base']['query_id']) ? $views_data['table']['base']['query_id'] : 'views_query'; $name = isset($views_data['table']['base']['query_id']) ? $views_data['table']['base']['query_id'] : 'views_query';
} }
......
...@@ -1444,7 +1444,7 @@ function execute(ViewExecutable $view) { ...@@ -1444,7 +1444,7 @@ function execute(ViewExecutable $view) {
$count_query->addMetaData('view', $view); $count_query->addMetaData('view', $view);
if (empty($this->options['disable_sql_rewrite'])) { if (empty($this->options['disable_sql_rewrite'])) {
$base_table_data = views_fetch_data($this->view->storage->get('base_table')); $base_table_data = drupal_container()->get('views.views_data')->get($this->view->storage->get('base_table'));
if (isset($base_table_data['table']['base']['access query tag'])) { if (isset($base_table_data['table']['base']['access query tag'])) {
$access_tag = $base_table_data['table']['base']['access query tag']; $access_tag = $base_table_data['table']['base']['access query tag'];
$query->addTag($access_tag); $query->addTag($access_tag);
...@@ -1542,7 +1542,8 @@ function execute(ViewExecutable $view) { ...@@ -1542,7 +1542,8 @@ function execute(ViewExecutable $view) {
function get_entity_tables() { function get_entity_tables() {
// Start with the base table. // Start with the base table.
$entity_tables = array(); $entity_tables = array();
$base_table_data = views_fetch_data($this->view->storage->get('base_table')); $views_data = drupal_container()->get('views.views_data');
$base_table_data = $views_data->get($this->view->storage->get('base_table'));
if (isset($base_table_data['table']['entity type'])) { if (isset($base_table_data['table']['entity type'])) {
$entity_tables[$this->view->storage->get('base_table')] = array( $entity_tables[$this->view->storage->get('base_table')] = array(
'base' => $this->view->storage->get('base_table'), 'base' => $this->view->storage->get('base_table'),
...@@ -1553,7 +1554,7 @@ function get_entity_tables() { ...@@ -1553,7 +1554,7 @@ function get_entity_tables() {
} }
// Include all relationships. // Include all relationships.
foreach ($this->view->relationship as $relationship_id => $relationship) { foreach ($this->view->relationship as $relationship_id => $relationship) {
$table_data = views_fetch_data($relationship->definition['base']); $table_data = $views_data->get($relationship->definition['base']);
if (isset($table_data['table']['entity type'])) { if (isset($table_data['table']['entity type'])) {
$entity_tables[$relationship->alias] = array( $entity_tables[$relationship->alias] = array(
'base' => $relationship->definition['base'], 'base' => $relationship->definition['base'],
......
...@@ -93,7 +93,7 @@ public function buildOptionsForm(&$form, &$form_state) { ...@@ -93,7 +93,7 @@ public function buildOptionsForm(&$form, &$form_state) {
foreach ($sorts as $sort_id => $sort) { foreach ($sorts as $sort_id => $sort) {
$sort_options[$sort_id] = "$sort[group]: $sort[title]"; $sort_options[$sort_id] = "$sort[group]: $sort[title]";
} }
$base_table_data = views_fetch_data($this->definition['base']); $base_table_data = drupal_container()->get('views.views_data')->get($this->definition['base']);
$form['subquery_sort'] = array( $form['subquery_sort'] = array(
'#type' => 'select', '#type' => 'select',
...@@ -219,7 +219,7 @@ function left_query($options) { ...@@ -219,7 +219,7 @@ function left_query($options) {
$temp_view->args[] = '**CORRELATED**'; $temp_view->args[] = '**CORRELATED**';
// Add the base table ID field. // Add the base table ID field.
$views_data = views_fetch_data($this->definition['base']); $views_data = drupal_container()->get('views.views_data')->get($this->definition['base']);
$base_field = $views_data['table']['base']['field']; $base_field = $views_data['table']['base']['field'];
$temp_view->addItem('default', 'field', $this->definition['base'], $this->definition['field']); $temp_view->addItem('default', 'field', $this->definition['base'], $this->definition['field']);
...@@ -341,7 +341,7 @@ function condition_namespace($string) { ...@@ -341,7 +341,7 @@ function condition_namespace($string) {
*/ */
public function query() { public function query() {
// Figure out what base table this relationship brings to the party. // Figure out what base table this relationship brings to the party.
$table_data = views_fetch_data($this->definition['base']); $table_data = drupal_container()->get('views.views_data')->get($this->definition['base']);
$base_field = empty($this->definition['base field']) ? $table_data['table']['base']['field'] : $this->definition['base field']; $base_field = empty($this->definition['base field']) ? $table_data['table']['base']['field'] : $this->definition['base field'];
$this->ensureMyTable(); $this->ensureMyTable();
......
...@@ -119,7 +119,7 @@ public function buildOptionsForm(&$form, &$form_state) { ...@@ -119,7 +119,7 @@ public function buildOptionsForm(&$form, &$form_state) {
*/ */
public function query() { public function query() {
// Figure out what base table this relationship brings to the party. // Figure out what base table this relationship brings to the party.
$table_data = views_fetch_data($this->definition['base']); $table_data = drupal_container()->get('views.views_data')->get($this->definition['base']);
$base_field = empty($this->definition['base field']) ? $table_data['table']['base']['field'] : $this->definition['base field']; $base_field = empty($this->definition['base field']) ? $table_data['table']['base']['field'] : $this->definition['base field'];
$this->ensureMyTable(); $this->ensureMyTable();
......
...@@ -73,7 +73,7 @@ public function buildOptionsForm(&$form, &$form_state) { ...@@ -73,7 +73,7 @@ public function buildOptionsForm(&$form, &$form_state) {
$relationship_handler = views_get_handler($relationship['table'], $relationship['field'], 'relationship'); $relationship_handler = views_get_handler($relationship['table'], $relationship['field'], 'relationship');
// If this relationship is valid for this type, add it to the list. // If this relationship is valid for this type, add it to the list.
$data = views_fetch_data($relationship['table']); $data = drupal_container()->get('views.views_data')->get($relationship['table']);
$base = $data[$relationship['field']]['relationship']['base']; $base = $data[$relationship['field']]['relationship']['base'];
if ($base == $this->base_table) { if ($base == $this->base_table) {
$relationship_handler->init($executable, $relationship); $relationship_handler->init($executable, $relationship);
......
...@@ -755,7 +755,7 @@ protected function default_display_options() { ...@@ -755,7 +755,7 @@ protected function default_display_options() {
// Add a least one field so the view validates and the user has a preview. // Add a least one field so the view validates and the user has a preview.
// The base field can provide a default in its base settings; otherwise, // The base field can provide a default in its base settings; otherwise,
// choose the first field with a field handler. // choose the first field with a field handler.
$data = views_fetch_data($this->base_table); $data = drupal_container()->get('views.views_data')->get($this->base_table);
if (isset($data['table']['base']['defaults']['field'])) { if (isset($data['table']['base']['defaults']['field'])) {
$field = $data['table']['base']['defaults']['field']; $field = $data['table']['base']['defaults']['field'];
} }
...@@ -836,7 +836,7 @@ protected function default_display_filters_user(array $form, array &$form_state) ...@@ -836,7 +836,7 @@ protected function default_display_filters_user(array $form, array &$form_state)
} }
} }
} }
$table_data = views_fetch_data($table); $table_data = drupal_container()->get('views.views_data')->get($table);
// If the 'in' operator is being used, map the values to an array. // If the 'in' operator is being used, map the values to an array.
$handler = $table_data[$bundle_key]['filter']['id']; $handler = $table_data[$bundle_key]['filter']['id'];
$handler_definition = drupal_container()->get('plugin.manager.views.filter')->getDefinition($handler); $handler_definition = drupal_container()->get('plugin.manager.views.filter')->getDefinition($handler);
...@@ -922,7 +922,7 @@ protected function default_display_sorts_user($form, $form_state) { ...@@ -922,7 +922,7 @@ protected function default_display_sorts_user($form, $form_state) {
// created from node, but the wizard type is another base table, make // created from node, but the wizard type is another base table, make
// sure it is not added. This usually don't happen if you have js // sure it is not added. This usually don't happen if you have js
// enabled. // enabled.
$data = views_fetch_data($table); $data = drupal_container()->get('views.views_data')->get($table);
if (isset($data[$column]['sort'])) { if (isset($data[$column]['sort'])) {
$sorts[$column] = array( $sorts[$column] = array(
'id' => $column, 'id' => $column,
......
...@@ -53,7 +53,7 @@ public static function getInfo() { ...@@ -53,7 +53,7 @@ public static function getInfo() {
*/ */
public function testHandlers() { public function testHandlers() {
$object_types = array_keys(ViewExecutable::viewsHandlerTypes()); $object_types = array_keys(ViewExecutable::viewsHandlerTypes());
foreach (views_fetch_data() as $base_table => $info) { foreach (drupal_container()->get('views.views_data')->get() as $base_table => $info) {
if (!isset($info['table']['base'])) { if (!isset($info['table']['base'])) {
continue; continue;
} }
......
...@@ -219,7 +219,7 @@ protected function formatViewOptions(array $views = array()) { ...@@ -219,7 +219,7 @@ protected function formatViewOptions(array $views = array()) {
* Ensure that a certain handler is a instance of a certain table/field. * Ensure that a certain handler is a instance of a certain table/field.
*/ */
function assertInstanceHandler($handler, $table, $field, $id) { function assertInstanceHandler($handler, $table, $field, $id) {
$table_data = views_fetch_data($table); $table_data = drupal_container()->get('views.views_data')->get($table);
$field_data = $table_data[$field][$id]; $field_data = $table_data[$field][$id];
$this->assertEqual($field_data['id'], $handler->getPluginId()); $this->assertEqual($field_data['id'], $handler->getPluginId());
......
...@@ -31,16 +31,12 @@ public function testViewsFetchData() { ...@@ -31,16 +31,12 @@ public function testViewsFetchData() {
$table_name = 'views_test_data'; $table_name = 'views_test_data';
$expected_data = $this->viewsData(); $expected_data = $this->viewsData();
$data = views_fetch_data($table_name); $data = drupal_container()->get('views.views_data')->get($table_name);
$this->assertEqual($data, $expected_data[$table_name], 'Make sure fetching views data by table works as expected.'); $this->assertEqual($data, $expected_data[$table_name], 'Make sure fetching views data by table works as expected.');
$data = views_fetch_data(); $data = drupal_container()->get('views.views_data')->get();
$this->assertTrue(isset($data[$table_name]), 'Make sure the views_test_data info appears in the total views data.'); $this->assertTrue(isset($data[$table_name]), 'Make sure the views_test_data info appears in the total views data.');
$this->assertEqual($data[$table_name], $expected_data[$table_name], 'Make sure the views_test_data has the expected values.'); $this->assertEqual($data[$table_name], $expected_data[$table_name], 'Make sure the views_test_data has the expected values.');
$data = views_fetch_data(NULL, TRUE);
$this->assertTrue(isset($data[$table_name]), 'Make sure the views_fetch_data appears in the total views data with reset = TRUE.');
$this->assertEqual($data[$table_name], $expected_data[$table_name], 'Make sure the views_test_data has the expected values.');
} }
/** /**
......
...@@ -946,7 +946,7 @@ public function initQuery() { ...@@ -946,7 +946,7 @@ public function initQuery() {
} }
// Create and initialize the query object. // Create and initialize the query object.
$views_data = views_fetch_data($this->storage->get('base_table')); $views_data = drupal_container()->get('views.views_data')->get($this->storage->get('base_table'));
$this->storage->set('base_field', !empty($views_data['table']['base']['field']) ? $views_data['table']['base']['field'] : ''); $this->storage->set('base_field', !empty($views_data['table']['base']['field']) ? $views_data['table']['base']['field'] : '');
if (!empty($views_data['table']['base']['database'])) { if (!empty($views_data['table']['base']['database'])) {
$this->base_database = $views_data['table']['base']['database']; $this->base_database = $views_data['table']['base']['database'];
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\Reference;
use Drupal\views\ViewExecutable; use Drupal\views\ViewExecutable;
/** /**
...@@ -24,6 +25,16 @@ public function build(ContainerBuilder $container) { ...@@ -24,6 +25,16 @@ public function build(ContainerBuilder $container) {
$container->register("plugin.manager.views.$type", 'Drupal\views\Plugin\ViewsPluginManager') $container->register("plugin.manager.views.$type", 'Drupal\views\Plugin\ViewsPluginManager')
->addArgument($type); ->addArgument($type);
} }
$container
->register('cache.views_info', 'Drupal\Core\Cache\CacheBackendInterface')
->setFactoryClass('Drupal\Core\Cache\CacheFactory')
->setFactoryMethod('get')
->addArgument('views_info');
$container->register('views.views_data', 'Drupal\views\ViewsDataCache')
->addArgument(new Reference('cache.views_info'))
->addArgument(new Reference('config.factory'));
} }
} }
<?php
/**
* @file
* Contains \Drupal\views\ViewsDataCache.
*/
namespace Drupal\views;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Cache\CacheBackendInterface;
/**
* Class to manage and lazy load cached views data.
*/
class ViewsDataCache {
/**
* The base cache ID to use.
*
* @var string
*/
protected $baseCid = 'views_data';
/**
* The cache backend to use.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cacheBackend;
/**
* Storage for the data itself.
*
* @var array
*/
protected $storage = array();
/**
* The configuration factory object.
*
* @var \Drupal\Core\Config\ConfigFactory
*/
protected $config;
/**
* The current language code.
*
* @var string