Issue #1817582 by dawehner, damiankloip, tim.plunkett: Added Lazy load display plugins.

8.0.x
catch 2012-12-17 22:29:36 +00:00
parent 341e9f66ea
commit 838a9e573c
8 changed files with 325 additions and 39 deletions

View File

@ -0,0 +1,165 @@
<?php
/**
* @file
* Contains \Drupal\views\DisplayArray.
*/
namespace Drupal\views;
/**
* A class which wraps the displays of a view so you can lazy-initialize them.
*/
class DisplayArray implements \ArrayAccess, \Iterator, \Countable {
/**
* Stores a reference to the view which has this displays attached.
*
* @var \Drupal\views\ViewExecutable
*/
protected $view;
/**
* Stores the actual display instances in an array.
*
* @var array
*/
protected $displayHandlers = array();
/**
* Stores all display IDs, coming from $this->view->storage->get('display').
*
* @var array
*/
protected $displayIDs;
/**
* Constructs a DisplayArray object.
*
* @param \Drupal\views\ViewExecutable
* The view which has this displays attached.
*/
public function __construct(ViewExecutable $view) {
$this->view = $view;
$this->initializeDisplay('default');
// Store all display IDs to access them easy and fast.
$display = $this->view->storage->get('display');
$this->displayIDs = drupal_map_assoc(array_keys($display));
}
/**
* Destructs a DisplayArray object.
*/
public function __destruct() {
foreach ($this->displayHandlers as $display_id => $display) {
$display->destroy();
unset($this->displayHandlers[$display_id]);
}
}
/**
* Initializes a single display and stores the result in $this->displayHandlers.
*
* @param string $display_id
* The name of the display to initialize.
*/
protected function initializeDisplay($display_id) {
// If the display was initialized before, just return.
if (isset($this->displayHandlers[$display_id])) {
return;
}
// Retrieve and initialize the new display handler with data.
$display = &$this->view->storage->getDisplay($display_id);
$this->displayHandlers[$display_id] = drupal_container()->get("plugin.manager.views.display")->createInstance($display['display_plugin']);
if (empty($this->displayHandlers[$display_id])) {
// Provide a 'default' handler as an emergency. This won't work well but
// it will keep things from crashing.
$this->displayHandlers[$display_id] = drupal_container()->get("plugin.manager.views.display")->createInstance('default');
}
$this->displayHandlers[$display_id]->init($this->view, $display);
// If this is not the default display handler, let it know which is since
// it may well utilize some data from the default.
if ($display_id != 'default') {
$this->displayHandlers[$display_id]->default_display = $this->displayHandlers['default'];
}
}
/**
* Implements \ArrayAccess::offsetExists().
*/
public function offsetExists($offset) {
return isset($this->displayHandlers[$offset]) || isset($this->displayIDs[$offset]);
}
/**
* Implements \ArrayAccess::offsetGet().
*/
public function offsetGet($offset) {
if (!isset($this->displayHandlers[$offset])) {
$this->initializeDisplay($offset);
}
return $this->displayHandlers[$offset];
}
/**
* Implements \ArrayAccess::offsetSet().
*/
public function offsetSet($offset, $value) {
$this->displayHandlers[$offset] = $value;
}
/**
* Implements \ArrayAccess::offsetUnset().
*/
public function offsetUnset($offset) {
unset($this->displayHandlers[$offset]);
}
/**
* Implements \Iterator::current().
*/
public function current() {
return $this->offsetGet($this->key());
}
/**
* Implements \Iterator::next().
*/
public function next() {
next($this->displayIDs);
}
/**
* Implements \Iterator::key().
*/
public function key() {
return key($this->displayIDs);
}
/**
* Implements \Iterator::valid().
*/
public function valid() {
$key = key($this->displayIDs);
return $key !== NULL && $key !== FALSE;
}
/**
* Implements \Iterator::rewind().
*/
public function rewind() {
reset($this->displayIDs);
}
/**
* Implements \Countable::count().
*/
public function count() {
return count($this->displayIDs);
}
}

View File

@ -311,10 +311,10 @@ class View extends ConfigEntityBase implements ViewStorageInterface {
* (optional) The ID to use, e.g., 'default', 'page_1', 'block_2'. Defaults
* to NULL.
*
* @return Drupal\views\Plugin\views\display\DisplayPluginBase
* A reference to the new handler object.
* @return \Drupal\views\Plugin\views\display\DisplayPluginBase
* A new display plugin instance.
*/
public function &newDisplay($plugin_id = 'page', $title = NULL, $id = NULL) {
public function newDisplay($plugin_id = 'page', $title = NULL, $id = NULL) {
$id = $this->addDisplay($plugin_id, $title, $id);
return $this->get('executable')->newDisplay($id);
}

View File

@ -679,6 +679,29 @@ abstract class DisplayPluginBase extends PluginBase {
*/
public function usesExposedFormInBlock() { return $this->hasPath(); }
/**
* Find out all displays which are attached to this display.
*
* The method is just using the pure storage object to avoid loading of the
* sub displays which would kill lazy loading.
*/
public function getAttachedDisplays() {
$current_display_id = $this->display['id'];
$attached_displays = array();
// Go through all displays and search displays which link to this one.
foreach ($this->view->storage->get('display') as $display_id => $display) {
if (isset($display['display_options']['displays'])) {
$displays = $display['display_options']['displays'];
if (isset($displays[$current_display_id])) {
$attached_displays[] = $display_id;
}
}
}
return $attached_displays;
}
/**
* Check to see which display to use when creating links within
* a view using this display.

View File

@ -19,7 +19,7 @@ class DisplayTest extends PluginTestBase {
*
* @var array
*/
public static $testViews = array('test_filter_groups');
public static $testViews = array('test_filter_groups', 'test_get_attach_displays');
/**
* Modules to enable.
@ -127,4 +127,18 @@ class DisplayTest extends PluginTestBase {
$this->assertFalse($view->displayHandlers['page']->isDefaulted('filters'), "Take sure that 'filters'' is marked as overridden.");
}
/**
* Tests the getAttachedDisplays method.
*/
public function testGetAttachedDisplays() {
$view = views_get_view('test_get_attach_displays');
// Both the feed_1 and the feed_2 display are attached to the page display.
$view->setDisplay('page_1');
$this->assertEqual($view->display_handler->getAttachedDisplays(), array('feed_1', 'feed_2'));
$view->setDisplay('feed_1');
$this->assertEqual($view->display_handler->getAttachedDisplays(), array());
}
}

View File

@ -62,7 +62,8 @@ class DisplayTest extends UITestBase {
// Delete the page, so we can test the undo process.
$this->drupalPost($path_prefix . '/page_1', array(), 'delete Page');
$this->assertFieldById('edit-displays-settings-settings-content-tab-content-details-top-actions-undo-delete', 'undo delete of Page', 'Make sure there a undo button on the page display after deleting.');
$this->assertTrue($this->xpath('//a[contains(@class, :class)]', array(':class' => 'views-display-deleted-link')), 'Make sure the display link is marked as to be deleted.');
$element = $this->xpath('//a[contains(@href, :href) and contains(@class, :class)]', array(':href' => $path_prefix . '/page_1', ':class' => 'views-display-deleted-link'));
$this->assertTrue(!empty($element), 'Make sure the display link is marked as to be deleted.');
// Undo the deleting of the display.
$this->drupalPost($path_prefix . '/page_1', array(), 'undo delete of Page');

View File

@ -8,6 +8,7 @@
namespace Drupal\views\Tests;
use Drupal\views\ViewExecutable;
use Drupal\views\DisplayArray;
use Drupal\views\Plugin\views\display\DefaultDisplay;
use Drupal\views\Plugin\views\display\Page;
@ -119,8 +120,7 @@ class ViewExecutableTest extends ViewTestBase {
// Tests Drupal\views\ViewExecutable::initDisplay().
$view->initDisplay();
$count = count($view->displayHandlers);
$this->assertEqual($count, 3, format_string('Make sure all display handlers got instantiated (@count of @count_expected)', array('@count' => $count, '@count_expected' => 3)));
$this->assertTrue($view->displayHandlers instanceof DisplayArray, 'The displayHandlers property has the right class.');
// Tests the classes of the instances.
$this->assertTrue($view->displayHandlers['default'] instanceof DefaultDisplay);
$this->assertTrue($view->displayHandlers['page'] instanceof Page);

View File

@ -596,22 +596,8 @@ class ViewExecutable {
return TRUE;
}
// Instantiate all displays
foreach ($this->storage->get('display') as $id => $display) {
$this->displayHandlers[$id] = drupal_container()->get("plugin.manager.views.display")->createInstance($display['display_plugin']);
if (!empty($this->displayHandlers[$id])) {
// Initialize the new display handler with data.
// @todo Refactor display to not need the handler data by reference.
$this->displayHandlers[$id]->init($this, $this->storage->getDisplay($id));
// If this is NOT the default display handler, let it know which is
// since it may well utilize some data from the default.
// This assumes that the 'default' handler is always first. It always
// is. Make sure of it.
if ($id != 'default') {
$this->displayHandlers[$id]->default_display =& $this->displayHandlers['default'];
}
}
}
// Initialize the display cache array.
$this->displayHandlers = new DisplayArray($this);
$this->current_display = 'default';
$this->display_handler = $this->displayHandlers['default'];
@ -1489,13 +1475,11 @@ class ViewExecutable {
}
$this->is_attachment = TRUE;
// Give other displays an opportunity to attach to the view.
foreach ($this->displayHandlers as $id => $display) {
if (!empty($this->displayHandlers[$id])) {
// Create a clone for the attachments to manipulate. 'static' refers to the current class name.
$cloned_view = new static($this->storage);
$this->displayHandlers[$id]->attachTo($cloned_view, $this->current_display);
}
// Find out which other displays attach to the current one.
foreach ($this->display_handler->getAttachedDisplays() as $id) {
// Create a clone for the attachments to manipulate. 'static' refers to the current class name.
$cloned_view = new static($this->storage);
$this->displayHandlers[$id]->attachTo($cloned_view, $this->current_display);
}
$this->is_attachment = FALSE;
}
@ -1832,12 +1816,7 @@ class ViewExecutable {
* collected.
*/
public function destroy() {
foreach (array_keys($this->displayHandlers) as $display_id) {
if (isset($this->displayHandlers[$display_id])) {
$this->displayHandlers[$display_id]->destroy();
unset($this->displayHandlers[$display_id]);
}
}
unset($this->displayHandlers);
foreach ($this::viewsHandlerTypes() as $type => $info) {
if (isset($this->$type)) {
@ -2219,10 +2198,10 @@ class ViewExecutable {
* @param string $id
* The ID for the display being added.
*
* @return Drupal\views\Plugin\views\display\DisplayPluginBase
* A reference to the new handler object.
* @return \Drupal\views\Plugin\views\display\DisplayPluginBase
* A new display plugin instance.
*/
public function &newDisplay($id) {
public function newDisplay($id) {
// Create a handler.
$display = $this->storage->get('display');
$manager = drupal_container()->get("plugin.manager.views.display");

View File

@ -0,0 +1,104 @@
base_table: node
name: test_get_attach_displays
description: ''
tag: ''
human_name: test_get_attach_displays
core: 8.x
api_version: '3.0'
display:
default:
display_plugin: default
id: default
display_title: Master
position: ''
display_options:
access:
type: perm
cache:
type: none
query:
type: views_query
exposed_form:
type: basic
pager:
type: full
options:
items_per_page: '10'
style:
type: default
row:
type: node
options:
build_mode: teaser
links: '1'
comments: '0'
fields:
title:
id: title
table: node
field: title
label: ''
alter:
alter_text: '0'
make_link: '0'
absolute: '0'
trim: '0'
word_boundary: '0'
ellipsis: '0'
strip_tags: '0'
html: '0'
hide_empty: '0'
empty_zero: '0'
link_to_node: '1'
filters:
status:
value: '1'
table: node
field: status
id: status
expose:
operator: '0'
group: '1'
sorts:
created:
id: created
table: node
field: created
order: DESC
title: test_get_attach_displays
page_1:
display_plugin: page
id: page_1
display_title: Page
position: ''
display_options:
path: test-get-attach-displays
feed_1:
display_plugin: feed
id: feed_1
display_title: Feed
position: ''
display_options:
pager:
type: some
style:
type: rss
row:
type: node_rss
path: test-get-attach-displays.xml
displays:
default: default
page_1: page_1
feed_2:
display_plugin: feed
id: feed_2
display_title: 'Feed 2'
position: ''
display_options:
displays:
default: default
page_1: page_1
base_field: nid
disabled: '0'
module: views
langcode: und