diff --git a/core/modules/node/src/Tests/Views/BulkFormTest.php b/core/modules/node/src/Tests/Views/BulkFormTest.php
index 42dc34af902..3ab918a8c7a 100644
--- a/core/modules/node/src/Tests/Views/BulkFormTest.php
+++ b/core/modules/node/src/Tests/Views/BulkFormTest.php
@@ -62,18 +62,15 @@ class BulkFormTest extends NodeTestBase {
$this->assertTrue($node->isPublished(), 'Node has been published');
// Make sticky action.
- $node->setPublished(FALSE);
- $node->save();
$this->assertFalse($node->isSticky(), 'Node is not sticky');
$edit = array(
'node_bulk_form[0]' => TRUE,
'action' => 'node_make_sticky_action',
);
$this->drupalPostForm(NULL, $edit, t('Apply'));
- // Re-load the node and check the status and sticky flag.
+ // Re-load the node and check the sticky flag.
$node_storage->resetCache(array($node->id()));
$node = $node_storage->load($node->id());
- $this->assertTrue($node->isPublished(), 'Node has been published');
$this->assertTrue($node->isSticky(), 'Node has been made sticky');
// Make unsticky action.
@@ -88,18 +85,15 @@ class BulkFormTest extends NodeTestBase {
$this->assertFalse($node->isSticky(), 'Node is not sticky anymore');
// Promote to front page.
- $node->setPublished(FALSE);
- $node->save();
$this->assertFalse($node->isPromoted(), 'Node is not promoted to the front page');
$edit = array(
'node_bulk_form[0]' => TRUE,
'action' => 'node_promote_action',
);
$this->drupalPostForm(NULL, $edit, t('Apply'));
- // Re-load the node and check the status and promoted flag.
+ // Re-load the node and check the promoted flag.
$node_storage->resetCache(array($node->id()));
$node = $node_storage->load($node->id());
- $this->assertTrue($node->isPublished(), 'Node has been published');
$this->assertTrue($node->isPromoted(), 'Node has been promoted to the front page');
// Demote from front page.
diff --git a/core/modules/views/src/Entity/View.php b/core/modules/views/src/Entity/View.php
index ecd541ed2a2..63d3ba110e2 100644
--- a/core/modules/views/src/Entity/View.php
+++ b/core/modules/views/src/Entity/View.php
@@ -433,4 +433,13 @@ class View extends ConfigEntityBase implements ViewEntityInterface {
return (bool) \Drupal::service('views.views_data')->get($this->base_table);
}
+ /**
+ * {@inheritdoc}
+ */
+ public function __sleep() {
+ $keys = parent::__sleep();
+ unset($keys[array_search('executable', $keys)]);
+ return $keys;
+ }
+
}
diff --git a/core/modules/views/src/Form/ViewsExposedForm.php b/core/modules/views/src/Form/ViewsExposedForm.php
index 1b1aa35402d..c87113c8558 100644
--- a/core/modules/views/src/Form/ViewsExposedForm.php
+++ b/core/modules/views/src/Form/ViewsExposedForm.php
@@ -121,7 +121,7 @@ class ViewsExposedForm extends FormBase {
$form['#id'] = Html::cleanCssIdentifier('views_exposed_form-' . String::checkPlain($view->storage->id()) . '-' . String::checkPlain($display['id']));
/** @var \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase $exposed_form_plugin */
- $exposed_form_plugin = $form_state->get('exposed_form_plugin');
+ $exposed_form_plugin = $view->display_handler->getPlugin('exposed_form');
$exposed_form_plugin->exposedFormAlter($form, $form_state);
// Save the form.
@@ -134,15 +134,17 @@ class ViewsExposedForm extends FormBase {
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
+ $view = $form_state->get('view');
+
foreach (array('field', 'filter') as $type) {
/** @var \Drupal\views\Plugin\views\ViewsHandlerInterface[] $handlers */
- $handlers = &$form_state->get('view')->$type;
+ $handlers = &$view->$type;
foreach ($handlers as $key => $handler) {
$handlers[$key]->validateExposed($form, $form_state);
}
}
/** @var \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase $exposed_form_plugin */
- $exposed_form_plugin = $form_state->get('exposed_form_plugin');
+ $exposed_form_plugin = $view->display_handler->getPlugin('exposed_form');
$exposed_form_plugin->exposedFormValidate($form, $form_state);
}
@@ -157,13 +159,14 @@ class ViewsExposedForm extends FormBase {
$handlers[$key]->submitExposed($form, $form_state);
}
}
+
$view = $form_state->get('view');
$view->exposed_data = $form_state->getValues();
$view->exposed_raw_input = [];
- $exclude = array('submit', 'form_build_id', 'form_id', 'form_token', 'exposed_form_plugin', '', 'reset');
+ $exclude = array('submit', 'form_build_id', 'form_id', 'form_token', 'exposed_form_plugin', 'reset');
/** @var \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase $exposed_form_plugin */
- $exposed_form_plugin = $form_state->get('exposed_form_plugin');
+ $exposed_form_plugin = $view->display_handler->getPlugin('exposed_form');
$exposed_form_plugin->exposedFormSubmit($form, $form_state, $exclude);
foreach ($form_state->getValues() as $key => $value) {
diff --git a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
index f9ba90441a2..7f0caa92918 100644
--- a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
+++ b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
@@ -150,7 +150,6 @@ abstract class ExposedFormPluginBase extends PluginBase {
$form_state->set('ajax', TRUE);
}
- $form_state->set('exposed_form_plugin', $this);
$form = \Drupal::formBuilder()->buildForm('\Drupal\views\Form\ViewsExposedForm', $form_state);
if (!$this->view->display_handler->displaysExposed() || (!$block && $this->view->display_handler->getOption('exposed_block'))) {
diff --git a/core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php b/core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php
index bb6b803d641..34a2a9a8be2 100644
--- a/core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php
+++ b/core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php
@@ -54,6 +54,13 @@ use Drupal\views\Views;
*/
abstract class RelationshipPluginBase extends HandlerBase {
+ /**
+ * The relationship alias.
+ *
+ * @var string
+ */
+ public $alias;
+
/**
* Overrides \Drupal\views\Plugin\views\HandlerBase::init().
*
diff --git a/core/modules/views/src/Tests/ViewExecutableTest.php b/core/modules/views/src/Tests/ViewExecutableTest.php
index ecbe8573bdb..ed1b47ad8f4 100644
--- a/core/modules/views/src/Tests/ViewExecutableTest.php
+++ b/core/modules/views/src/Tests/ViewExecutableTest.php
@@ -449,6 +449,7 @@ class ViewExecutableTest extends ViewUnitTestBase {
$errors = $executable->validate();
$total_error_count = array_reduce($errors, function ($carry, $item) {
$carry += count($item);
+
return $carry;
});
// Assert that there were 9 total errors across 3 displays.
@@ -456,4 +457,29 @@ class ViewExecutableTest extends ViewUnitTestBase {
$this->assertIdentical(3, count($errors));
}
+ /**
+ * Tests serialization of the ViewExecutable object.
+ */
+ public function testSerialization() {
+ $view = Views::getView('test_executable_displays');
+ $view->setDisplay('page_1');
+ $view->setArguments(['test']);
+ $view->setCurrentPage(2);
+
+ $serialized = serialize($view);
+
+ // Test the view storage object is not present in the actual serialized
+ // string.
+ $this->assertIdentical(strpos($serialized, '"Drupal\views\Entity\View"'), FALSE, 'The Drupal\views\Entity\View class was not found in the serialized string.');
+
+ /** @var \Drupal\views\ViewExecutable $unserialized */
+ $unserialized = unserialize($serialized);
+
+ $this->assertTrue($unserialized instanceof ViewExecutable);
+ $this->assertIdentical($view->storage->id(), $unserialized->storage->id(), 'The expected storage entity was loaded on the unserialized view.');
+ $this->assertIdentical($unserialized->current_display, 'page_1', 'The expected display was set on the unserialized view.');
+ $this->assertIdentical($unserialized->args, ['test'], 'The expected argument was set on the unserialized view.');
+ $this->assertIdentical($unserialized->getCurrentPage(), 2, 'The expected current page was set on the unserialized view.');
+ }
+
}
diff --git a/core/modules/views/src/ViewExecutable.php b/core/modules/views/src/ViewExecutable.php
index d74fe311dfa..0eb445d4eb6 100644
--- a/core/modules/views/src/ViewExecutable.php
+++ b/core/modules/views/src/ViewExecutable.php
@@ -26,7 +26,7 @@ use Symfony\Component\HttpFoundation\Response;
* An object to contain all of the data to generate a view, plus the member
* functions to build the view query, execute the query and render the output.
*/
-class ViewExecutable {
+class ViewExecutable implements \Serializable {
use DependencySerializationTrait;
/**
@@ -2295,4 +2295,49 @@ class ViewExecutable {
return $this->storage->calculateDependencies();
}
+ /**
+ * {@inheritdoc}
+ */
+ public function serialize() {
+ return serialize([
+ // Only serialize the storage entity ID.
+ $this->storage->id(),
+ $this->current_display,
+ $this->args,
+ $this->current_page,
+ $this->exposed_input,
+ $this->exposed_raw_input,
+ $this->exposed_data,
+ $this->dom_id,
+ $this->executed,
+ ]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function unserialize($serialized) {
+ list($storage, $current_display, $args, $current_page, $exposed_input, $exposed_raw_input, $exposed_data, $dom_id, $executed) = unserialize($serialized);
+
+ $this->setRequest(\Drupal::request());
+ $this->user = \Drupal::currentUser();
+
+ $this->storage = \Drupal::entityManager()->getStorage('view')->load($storage);
+
+ $this->setDisplay($current_display);
+ $this->setArguments($args);
+ $this->setCurrentPage($current_page);
+ $this->setExposedInput($exposed_input);
+ $this->exposed_data = $exposed_data;
+ $this->exposed_raw_input = $exposed_raw_input;
+ $this->dom_id = $dom_id;
+
+ $this->initHandlers();
+
+ // If the display was previously executed, execute it now.
+ if ($executed) {
+ $this->execute($this->current_display);
+ }
+ }
+
}
diff --git a/core/modules/views_ui/src/ViewEditForm.php b/core/modules/views_ui/src/ViewEditForm.php
index 5d27d3066a6..28b1175a8c3 100644
--- a/core/modules/views_ui/src/ViewEditForm.php
+++ b/core/modules/views_ui/src/ViewEditForm.php
@@ -266,6 +266,7 @@ class ViewEditForm extends ViewFormBase {
public function save(array $form, FormStateInterface $form_state) {
$view = $this->entity;
$executable = $view->getExecutable();
+ $executable->initDisplay();
// Go through and remove displayed scheduled for removal.
$displays = $view->get('display');
diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php
index 68eb0e89de5..5355fcd3727 100644
--- a/core/modules/views_ui/src/ViewUI.php
+++ b/core/modules/views_ui/src/ViewUI.php
@@ -115,13 +115,6 @@ class ViewUI implements ViewEntityInterface {
*/
protected $storage;
- /**
- * The View executable object.
- *
- * @var \Drupal\views\ViewExecutable
- */
- protected $executable;
-
/**
* Stores a list of database queries run beside the main one from views.
*
@@ -170,13 +163,9 @@ class ViewUI implements ViewEntityInterface {
* @param \Drupal\views\ViewEntityInterface $storage
* The View storage object to wrap.
*/
- public function __construct(ViewEntityInterface $storage, ViewExecutable $executable = NULL) {
+ public function __construct(ViewEntityInterface $storage) {
$this->entityType = 'view';
$this->storage = $storage;
- if (!isset($executable)) {
- $executable = Views::executableFactory()->get($this);
- }
- $this->executable = $executable;
}
/**
@@ -259,7 +248,7 @@ class ViewUI implements ViewEntityInterface {
$display_id = $form_state->get('display_id');
if ($revert) {
// If it's revert just change the override and return.
- $display = &$this->executable->displayHandlers->get($display_id);
+ $display = &$this->getExecutable()->displayHandlers->get($display_id);
$display->optionsOverride($form, $form_state);
// Don't execute the normal submit handling but still store the changed view into cache.
@@ -273,7 +262,7 @@ class ViewUI implements ViewEntityInterface {
elseif ($was_defaulted && !$is_defaulted) {
// We were using the default display's values, but we're now overriding
// the default display and saving values specific to this display.
- $display = &$this->executable->displayHandlers->get($display_id);
+ $display = &$this->getExecutable()->displayHandlers->get($display_id);
// optionsOverride toggles the override of this section.
$display->optionsOverride($form, $form_state);
$display->submitOptionsForm($form, $form_state);
@@ -283,7 +272,7 @@ class ViewUI implements ViewEntityInterface {
// to go back to the default display.
// Overwrite the default display with the current form values, and make
// the current display use the new default values.
- $display = &$this->executable->displayHandlers->get($display_id);
+ $display = &$this->getExecutable()->displayHandlers->get($display_id);
// optionsOverride toggles the override of this section.
$display->optionsOverride($form, $form_state);
$display->submitOptionsForm($form, $form_state);
@@ -481,7 +470,7 @@ class ViewUI implements ViewEntityInterface {
if ($was_defaulted && !$is_defaulted) {
// We were using the default display's values, but we're now overriding
// the default display and saving values specific to this display.
- $display = &$this->executable->displayHandlers->get($display_id);
+ $display = &$this->getExecutable()->displayHandlers->get($display_id);
// setOverride toggles the override of this section.
$display->setOverride($section);
}
@@ -490,7 +479,7 @@ class ViewUI implements ViewEntityInterface {
// to go back to the default display.
// Overwrite the default display with the current form values, and make
// the current display use the new default values.
- $display = &$this->executable->displayHandlers->get($display_id);
+ $display = &$this->getExecutable()->displayHandlers->get($display_id);
// optionsOverride toggles the override of this section.
$display->setOverride($section);
}
@@ -503,7 +492,7 @@ class ViewUI implements ViewEntityInterface {
if ($cut = strpos($field, '$')) {
$field = substr($field, 0, $cut);
}
- $id = $this->executable->addHandler($display_id, $type, $table, $field);
+ $id = $this->getExecutable()->addHandler($display_id, $type, $table, $field);
// check to see if we have group by settings
$key = $type;
@@ -516,7 +505,7 @@ class ViewUI implements ViewEntityInterface {
'field' => $field,
);
$handler = Views::handlerManager($key)->getHandler($item);
- if ($this->executable->displayHandlers->get('default')->useGroupBy() && $handler->usesGroupBy()) {
+ if ($this->getExecutable()->displayHandlers->get('default')->useGroupBy() && $handler->usesGroupBy()) {
$this->addFormToStack('handler-group', $display_id, $type, $id);
}
@@ -564,6 +553,7 @@ class ViewUI implements ViewEntityInterface {
// Save the current path so it can be restored before returning from this function.
$request_stack = \Drupal::requestStack();
$current_request = $request_stack->getCurrentRequest();
+ $executable = $this->getExecutable();
// Determine where the query and performance statistics should be output.
$config = \Drupal::config('views.settings');
@@ -580,11 +570,11 @@ class ViewUI implements ViewEntityInterface {
$rows = array('query' => array(), 'statistics' => array());
- $errors = $this->executable->validate();
- $this->executable->destroy();
+ $errors = $this->getExecutable()->validate();
+ $executable->destroy();
if (empty($errors)) {
$this->ajax = TRUE;
- $this->executable->live_preview = TRUE;
+ $executable->live_preview = TRUE;
// AJAX happens via HTTP POST but everything expects exposed data to
// be in GET. Copy stuff but remove ajax-framework specific keys.
@@ -597,19 +587,19 @@ class ViewUI implements ViewEntityInterface {
unset($exposed_input[$key]);
}
}
- $this->executable->setExposedInput($exposed_input);
+ $executable->setExposedInput($exposed_input);
- if (!$this->executable->setDisplay($display_id)) {
+ if (!$executable->setDisplay($display_id)) {
return [
'#markup' => t('Invalid display id @display', array('@display' => $display_id)),
];
}
- $this->executable->setArguments($args);
+ $executable->setArguments($args);
// Store the current view URL for later use:
- if ($this->executable->display_handler->getOption('path')) {
- $path = $this->executable->getUrl();
+ if ($executable->display_handler->getOption('path')) {
+ $path = $executable->getUrl();
}
// Make view links come back to preview.
@@ -646,7 +636,7 @@ class ViewUI implements ViewEntityInterface {
}
// Execute/get the view preview.
- $preview = $this->executable->preview($display_id, $args);
+ $preview = $executable->preview($display_id, $args);
if ($show_additional_queries) {
$this->endQueryCapture();
@@ -660,13 +650,13 @@ class ViewUI implements ViewEntityInterface {
// below the view preview.
if ($show_info || $show_query || $show_stats) {
// Get information from the preview for display.
- if (!empty($this->executable->build_info['query'])) {
+ if (!empty($executable->build_info['query'])) {
if ($show_query) {
- $query_string = $this->executable->build_info['query'];
+ $query_string = $executable->build_info['query'];
// Only the sql default class has a method getArguments.
$quoted = array();
- if ($this->executable->query instanceof Sql) {
+ if ($executable->query instanceof Sql) {
$quoted = $query_string->getArguments();
$connection = Database::getConnection();
foreach ($quoted as $key => $val) {
@@ -722,7 +712,7 @@ class ViewUI implements ViewEntityInterface {
'#template' => "{% trans 'Title' %}",
),
),
- Xss::filterAdmin($this->executable->getTitle()),
+ Xss::filterAdmin($executable->getTitle()),
);
if (isset($path)) {
// @todo Views should expect and store a leading /. See:
@@ -743,7 +733,7 @@ class ViewUI implements ViewEntityInterface {
'#template' => "{% trans 'Query build time' %}",
),
),
- t('@time ms', array('@time' => intval($this->executable->build_time * 100000) / 100)),
+ t('@time ms', array('@time' => intval($this->getExecutable()->build_time * 100000) / 100)),
);
$rows['statistics'][] = array(
@@ -753,7 +743,7 @@ class ViewUI implements ViewEntityInterface {
'#template' => "{% trans 'Query execute time' %}",
),
),
- t('@time ms', array('@time' => intval($this->executable->execute_time * 100000) / 100)),
+ t('@time ms', array('@time' => intval($this->getExecutable()->execute_time * 100000) / 100)),
);
$rows['statistics'][] = array(
@@ -763,10 +753,10 @@ class ViewUI implements ViewEntityInterface {
'#template' => "{% trans 'View render time' %}",
),
),
- t('@time ms', array('@time' => intval($this->executable->render_time * 100000) / 100)),
+ t('@time ms', array('@time' => intval($executable->render_time * 100000) / 100)),
);
}
- \Drupal::moduleHandler()->alter('views_preview_info', $rows, $this->executable);
+ \Drupal::moduleHandler()->alter('views_preview_info', $rows, $executable);
}
else {
// No query was run. Display that information in place of either the
@@ -873,14 +863,14 @@ class ViewUI implements ViewEntityInterface {
if (isset($executable->current_display)) {
// Add the knowledge of the changed display, too.
$this->changed_display[$executable->current_display] = TRUE;
- unset($executable->current_display);
+ $executable->current_display = NULL;
}
- // Unset handlers; we don't want to write these into the cache.
- unset($executable->display_handler);
- unset($executable->default_display);
+ // Unset handlers. We don't want to write these into the cache.
+ $executable->display_handler = NULL;
+ $executable->default_display = NULL;
$executable->query = NULL;
- unset($executable->displayHandlers);
+ $executable->displayHandlers = NULL;
\Drupal::service('user.shared_tempstore')->get('views')->set($this->id(), $this);
}
@@ -1239,13 +1229,6 @@ class ViewUI implements ViewEntityInterface {
return $this->storage->addDisplay($plugin_id, $title, $id);
}
- /**
- * {@inheritdoc}
- */
- public function getViewExecutable() {
- return $this->storage->getViewExecutable();
- }
-
/**
* {@inheritdoc}
*/
@@ -1287,4 +1270,5 @@ class ViewUI implements ViewEntityInterface {
public function getThirdPartyProviders() {
return $this->storage->getThirdPartyProviders();
}
+
}
diff --git a/core/modules/views_ui/tests/src/Unit/ViewUIObjectTest.php b/core/modules/views_ui/tests/src/Unit/ViewUIObjectTest.php
index 60a9d168f74..273adddf616 100644
--- a/core/modules/views_ui/tests/src/Unit/ViewUIObjectTest.php
+++ b/core/modules/views_ui/tests/src/Unit/ViewUIObjectTest.php
@@ -9,6 +9,7 @@ namespace Drupal\Tests\views_ui\Unit;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Tests\UnitTestCase;
+use Drupal\views\Entity\View;
use Drupal\views\ViewExecutable;
use Drupal\views_ui\ViewUI;
use Symfony\Component\DependencyInjection\Container;
@@ -54,8 +55,9 @@ class ViewUIObjectTest extends UnitTestCase {
->disableOriginalConstructor()
->setConstructorArgs(array($storage))
->getMock();
+ $storage->set('executable', $executable);
- $view_ui = new ViewUI($storage, $executable);
+ $view_ui = new ViewUI($storage);
foreach ($method_args as $method => $args) {
$method_mock = $storage->expects($this->once())
@@ -80,6 +82,7 @@ class ViewUIObjectTest extends UnitTestCase {
->disableOriginalConstructor()
->setConstructorArgs(array($storage))
->getMock();
+ $storage->set('executable', $executable);
$account = $this->getMock('Drupal\Core\Session\AccountInterface');
$account->expects($this->exactly(2))
->method('id')
@@ -89,7 +92,7 @@ class ViewUIObjectTest extends UnitTestCase {
$container->set('current_user', $account);
\Drupal::setContainer($container);
- $view_ui = new ViewUI($storage, $executable);
+ $view_ui = new ViewUI($storage);
// A view_ui without a lock object is not locked.
$this->assertFalse($view_ui->isLocked());
@@ -113,4 +116,33 @@ class ViewUIObjectTest extends UnitTestCase {
$this->assertFalse($view_ui->isLocked());
}
+ /**
+ * Tests serialization of the ViewUI object.
+ */
+ public function testSerialization() {
+ // Set a container so the DependencySerializationTrait has it.
+ $container = new ContainerBuilder();
+ \Drupal::setContainer($container);
+
+ $storage = new View([], 'view');
+ $executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
+ ->disableOriginalConstructor()
+ ->setConstructorArgs([$storage])
+ ->getMock();
+ $storage->set('executable', $executable);
+
+ $view_ui = new ViewUI($storage);
+
+ // Make sure the executable is returned before serializing.
+ $this->assertInstanceOf('Drupal\views\ViewExecutable', $view_ui->getExecutable());
+
+ $serialized = serialize($view_ui);
+
+ // Make sure the ViewExecutable class is not found in the serialized string.
+ $this->assertSame(strpos($serialized, '"Drupal\views\ViewExecutable"'), FALSE);
+
+ $unserialized = unserialize($serialized);
+ $this->assertInstanceOf('Drupal\views_ui\ViewUI', $unserialized);
+ }
+
}