Issue #1817582 by dawehner, damiankloip, tim.plunkett: Added Lazy load display plugins.
parent
341e9f66ea
commit
838a9e573c
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue