3 namespace Drupal\views;
5 use Drupal\Component\Utility\Html;
6 use Drupal\Component\Utility\Tags;
7 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
8 use Drupal\Core\Routing\RouteProviderInterface;
9 use Drupal\Core\Session\AccountInterface;
10 use Drupal\views\Plugin\views\display\DisplayRouterInterface;
11 use Symfony\Component\HttpFoundation\Request;
12 use Symfony\Component\HttpFoundation\Response;
13 use Symfony\Component\Routing\Exception\RouteNotFoundException;
16 * Represents a view as a whole.
18 * An object to contain all of the data to generate a view, plus the member
19 * functions to build the view query, execute the query and render the output.
21 class ViewExecutable implements \Serializable {
22 use DependencySerializationTrait;
25 * The config entity in which the view is stored.
27 * @var \Drupal\views\Entity\View
32 * Whether or not the view has been built.
34 * @todo Group with other static properties.
38 public $built = FALSE;
41 * Whether the view has been executed/query has been run.
43 * @todo Group with other static properties.
47 public $executed = FALSE;
50 * Any arguments that have been passed into the view.
57 * An array of build info.
61 public $build_info = [];
64 * Whether this view uses AJAX.
68 protected $ajaxEnabled = FALSE;
71 * Where the results of a query will go.
73 * The array must use a numeric index starting at 0.
75 * @var \Drupal\views\ResultRow[]
79 // May be used to override the current pager info.
82 * The current page. If the view uses pagination.
86 protected $current_page = NULL;
89 * The number of items per page.
93 protected $items_per_page = NULL;
100 protected $offset = NULL;
103 * The total number of rows returned from the query.
107 public $total_rows = NULL;
110 * Attachments to place before the view.
114 public $attachment_before = [];
117 * Attachments to place after the view.
121 public $attachment_after = [];
124 * Feed icons attached to the view.
128 public $feedIcons = [];
130 // Exposed widget input
133 * All the form data from $form_state->getValues().
137 public $exposed_data = [];
140 * An array of input values from exposed forms.
144 protected $exposed_input = [];
147 * Exposed widget input directly from the $form_state->getValues().
151 public $exposed_raw_input = [];
154 * Used to store views that were previously running if we recurse.
156 * @var \Drupal\views\ViewExecutable[]
158 public $old_view = [];
161 * To avoid recursion in views embedded into areas.
163 * @var \Drupal\views\ViewExecutable[]
165 public $parent_views = [];
168 * Whether this view is an attachment to another view.
172 public $is_attachment = NULL;
175 * Identifier of the current display.
179 public $current_display;
182 * Where the $query object will reside.
184 * @var \Drupal\views\Plugin\views\query\QueryPluginBase
186 public $query = NULL;
189 * The used pager plugin used by the current executed view.
191 * @var \Drupal\views\Plugin\views\pager\PagerPluginBase
193 public $pager = NULL;
196 * The current used display plugin.
198 * @var \Drupal\views\Plugin\views\display\DisplayPluginBase
200 public $display_handler;
203 * The list of used displays of the view.
205 * An array containing Drupal\views\Plugin\views\display\DisplayPluginBase
208 * @var \Drupal\views\DisplayPluginCollection
210 public $displayHandlers;
213 * The current used style plugin.
215 * @var \Drupal\views\Plugin\views\style\StylePluginBase
217 public $style_plugin;
220 * The current used row plugin, if the style plugin supports row plugins.
222 * @var \Drupal\views\Plugin\views\row\RowPluginBase
227 * Stores the current active row while rendering.
234 * Allow to override the url of the current view.
236 * @var \Drupal\Core\Url
238 public $override_url;
241 * Allow to override the path used for generated urls.
245 public $override_path = NULL;
248 * Allow to override the used database which is used for this query.
252 public $base_database = NULL;
254 // Handlers which are active on this view.
257 * Stores the field handlers which are initialized on this view.
259 * @var \Drupal\views\Plugin\views\field\FieldPluginBase[]
264 * Stores the argument handlers which are initialized on this view.
266 * @var \Drupal\views\Plugin\views\argument\ArgumentPluginBase[]
271 * Stores the sort handlers which are initialized on this view.
273 * @var \Drupal\views\Plugin\views\sort\SortPluginBase[]
278 * Stores the filter handlers which are initialized on this view.
280 * @var \Drupal\views\Plugin\views\filter\FilterPluginBase[]
285 * Stores the relationship handlers which are initialized on this view.
287 * @var \Drupal\views\Plugin\views\relationship\RelationshipPluginBase[]
289 public $relationship;
292 * Stores the area handlers for the header which are initialized on this view.
294 * @var \Drupal\views\Plugin\views\area\AreaPluginBase[]
299 * Stores the area handlers for the footer which are initialized on this view.
301 * @var \Drupal\views\Plugin\views\area\AreaPluginBase[]
306 * Stores the area handlers for the empty text which are initialized on this view.
308 * An array containing Drupal\views\Plugin\views\area\AreaPluginBase objects.
310 * @var \Drupal\views\Plugin\views\area\AreaPluginBase[]
315 * Stores the current response object.
317 * @var \Symfony\Component\HttpFoundation\Response
319 protected $response = NULL;
322 * Stores the current request object.
324 * @var \Symfony\Component\HttpFoundation\Request
329 * Does this view already have loaded it's handlers.
331 * @todo Group with other static properties.
338 * The rendered output of the exposed form.
342 public $exposed_widgets;
345 * If this view has been previewed.
352 * Force the query to calculate the total number of results.
354 * @todo Move to the query.
358 public $get_total_rows;
361 * Indicates if the sorts have been built.
363 * @todo Group with other static properties.
370 * Stores the many-to-one tables for performance.
374 public $many_to_one_tables;
377 * A unique identifier which allows to update multiple views output via js.
384 * A render array container to store render related information.
386 * For example you can alter the array and attach some asset library or JS
387 * settings via the #attached key. This is the required way to add custom
392 * @see \Drupal\Core\Render\AttachmentsResponseProcessorInterface::processAttachments()
396 'library' => ['views/views.module'],
397 'drupalSettings' => [],
405 * @var \Drupal\Core\Session\AccountInterface
410 * Should the admin links be shown on the rendered view.
414 protected $showAdminLinks;
419 * @var \Drupal\views\ViewsData
421 protected $viewsData;
424 * The route provider.
426 * @var \Drupal\Core\Routing\RouteProviderInterface
428 protected $routeProvider;
431 * The entity type of the base table, if available.
433 * @var \Drupal\Core\Entity\EntityTypeInterface|false
435 protected $baseEntityType;
438 * Constructs a new ViewExecutable object.
440 * @param \Drupal\views\ViewEntityInterface $storage
441 * The view config entity the actual information is stored on.
442 * @param \Drupal\Core\Session\AccountInterface $user
444 * @param \Drupal\views\ViewsData $views_data
446 * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
447 * The route provider.
449 public function __construct(ViewEntityInterface $storage, AccountInterface $user, ViewsData $views_data, RouteProviderInterface $route_provider) {
450 // Reference the storage and the executable to each other.
451 $this->storage = $storage;
452 $this->storage->set('executable', $this);
454 $this->viewsData = $views_data;
455 $this->routeProvider = $route_provider;
459 * Returns the identifier.
461 * @return string|null
462 * The entity identifier, or NULL if the object does not yet have an
465 public function id() {
466 return $this->storage->id();
472 public function save() {
473 $this->storage->save();
477 * Sets the arguments for the view.
480 * The arguments passed to the view.
482 public function setArguments(array $args) {
483 // The array keys of the arguments will be incorrect if set by
484 // views_embed_view() or \Drupal\views\ViewExecutable:preview().
485 $this->args = array_values($args);
489 * Expands the list of used cache contexts for the view.
491 * @param string $cache_context
492 * The additional cache context.
496 public function addCacheContext($cache_context) {
497 $this->element['#cache']['contexts'][] = $cache_context;
503 * Sets the current page for the pager.
508 public function setCurrentPage($page) {
509 $this->current_page = $page;
511 // Calls like ::unserialize() might call this method without a proper $page.
512 // Also check whether the element is pre rendered. At that point, the cache
513 // keys cannot longer be manipulated.
514 if ($page !== NULL && empty($this->element['#pre_rendered'])) {
515 $this->element['#cache']['keys'][] = 'page:' . $page;
518 // If the pager is already initialized, pass it through to the pager.
519 if (!empty($this->pager)) {
520 return $this->pager->setCurrentPage($page);
525 * Gets the current page from the pager.
530 public function getCurrentPage() {
531 // If the pager is already initialized, pass it through to the pager.
532 if (!empty($this->pager)) {
533 return $this->pager->getCurrentPage();
536 if (isset($this->current_page)) {
537 return $this->current_page;
542 * Gets the items per page from the pager.
545 * The items per page.
547 public function getItemsPerPage() {
548 // If the pager is already initialized, pass it through to the pager.
549 if (!empty($this->pager)) {
550 return $this->pager->getItemsPerPage();
553 if (isset($this->items_per_page)) {
554 return $this->items_per_page;
559 * Sets the items per page on the pager.
561 * @param int $items_per_page
562 * The items per page.
564 public function setItemsPerPage($items_per_page) {
565 // Check whether the element is pre rendered. At that point, the cache keys
566 // cannot longer be manipulated.
567 if (empty($this->element['#pre_rendered'])) {
568 $this->element['#cache']['keys'][] = 'items_per_page:' . $items_per_page;
570 $this->items_per_page = $items_per_page;
572 // If the pager is already initialized, pass it through to the pager.
573 if (!empty($this->pager)) {
574 $this->pager->setItemsPerPage($items_per_page);
579 * Gets the pager offset from the pager.
584 public function getOffset() {
585 // If the pager is already initialized, pass it through to the pager.
586 if (!empty($this->pager)) {
587 return $this->pager->getOffset();
590 if (isset($this->offset)) {
591 return $this->offset;
596 * Sets the offset on the pager.
601 public function setOffset($offset) {
602 // Check whether the element is pre rendered. At that point, the cache keys
603 // cannot longer be manipulated.
604 if (empty($this->element['#pre_rendered'])) {
605 $this->element['#cache']['keys'][] = 'offset:' . $offset;
608 $this->offset = $offset;
611 // If the pager is already initialized, pass it through to the pager.
612 if (!empty($this->pager)) {
613 $this->pager->setOffset($offset);
618 * Determines if the view uses a pager.
621 * TRUE if the view uses a pager, FALSE otherwise.
623 public function usePager() {
624 if (!empty($this->pager)) {
625 return $this->pager->usePager();
630 * Sets whether or not AJAX should be used.
632 * If AJAX is used, paging, table sorting, and exposed filters will be fetched
633 * via an AJAX call rather than a page refresh.
635 * @param bool $ajax_enabled
636 * TRUE if AJAX should be used, FALSE otherwise.
638 public function setAjaxEnabled($ajax_enabled) {
639 $this->ajaxEnabled = (bool) $ajax_enabled;
643 * Determines whether or not AJAX should be used.
646 * TRUE if AJAX is enabled, FALSE otherwise.
648 public function ajaxEnabled() {
649 return $this->ajaxEnabled;
653 * Sets the exposed filters input to an array.
655 * @param string[] $filters
656 * The values taken from the view's exposed filters and sorts.
658 public function setExposedInput($filters) {
659 $this->exposed_input = $filters;
663 * Figures out what the exposed input for this view is.
665 * They will be taken from \Drupal::request()->query or from
666 * something previously set on the view.
669 * An array containing the exposed input values keyed by the filter and sort
672 * @see self::setExposedInput()
674 public function getExposedInput() {
675 // Fill our input either from \Drupal::request()->query or from something
676 // previously set on the view.
677 if (empty($this->exposed_input)) {
678 // Ensure that we can call the method at any point in time.
679 $this->initDisplay();
681 $this->exposed_input = \Drupal::request()->query->all();
682 // unset items that are definitely not our input:
683 foreach (['page', 'q'] as $key) {
684 if (isset($this->exposed_input[$key])) {
685 unset($this->exposed_input[$key]);
689 // If we have no input at all, check for remembered input via session.
691 // If filters are not overridden, store the 'remember' settings on the
692 // default display. If they are, store them on this display. This way,
693 // multiple displays in the same view can share the same filters and
694 // remember settings.
695 $display_id = ($this->display_handler->isDefaulted('filters')) ? 'default' : $this->current_display;
697 if (empty($this->exposed_input) && !empty($_SESSION['views'][$this->storage->id()][$display_id])) {
698 $this->exposed_input = $_SESSION['views'][$this->storage->id()][$display_id];
702 return $this->exposed_input;
706 * Sets the display for this view and initializes the display handler.
709 * Always returns TRUE.
711 public function initDisplay() {
712 if (isset($this->current_display)) {
716 // Initialize the display cache array.
717 $this->displayHandlers = new DisplayPluginCollection($this, Views::pluginManager('display'));
719 $this->current_display = 'default';
720 $this->display_handler = $this->displayHandlers->get('default');
726 * Gets the first display that is accessible to the user.
728 * @param array|string $displays
729 * Either a single display id or an array of display ids.
732 * The first accessible display id, at least default.
734 public function chooseDisplay($displays) {
735 if (!is_array($displays)) {
739 $this->initDisplay();
741 foreach ($displays as $display_id) {
742 if ($this->displayHandlers->get($display_id)->access($this->user)) {
751 * Gets the current display plugin.
753 * @return \Drupal\views\Plugin\views\display\DisplayPluginBase
754 * The current display plugin.
756 public function getDisplay() {
757 if (!isset($this->display_handler)) {
758 $this->initDisplay();
761 return $this->display_handler;
765 * Sets the current display.
767 * @param string $display_id
768 * The ID of the display to mark as current.
771 * TRUE if the display was correctly set, FALSE otherwise.
773 public function setDisplay($display_id = NULL) {
774 // If we have not already initialized the display, do so.
775 if (!isset($this->current_display)) {
776 // This will set the default display and instantiate the default display
778 $this->initDisplay();
781 // If no display ID is passed, we either have initialized the default or
782 // already have a display set.
783 if (!isset($display_id)) {
787 $display_id = $this->chooseDisplay($display_id);
789 // Ensure the requested display exists.
790 if (!$this->displayHandlers->has($display_id)) {
791 debug(format_string('setDisplay() called with invalid display ID "@display".', ['@display' => $display_id]));
795 // Reset if the display has changed. It could be called multiple times for
796 // the same display, especially in the UI.
797 if ($this->current_display != $display_id) {
798 // Set the current display.
799 $this->current_display = $display_id;
801 // Reset the style and row plugins.
802 $this->style_plugin = NULL;
803 $this->plugin_name = NULL;
804 $this->rowPlugin = NULL;
807 if ($display = $this->displayHandlers->get($display_id)) {
809 $this->display_handler = $display;
817 * Creates a new display and a display handler instance for it.
819 * @param string $plugin_id
820 * (optional) The plugin type from the Views plugin annotation. Defaults to
822 * @param string $title
823 * (optional) The title of the display. Defaults to NULL.
825 * (optional) The ID to use, e.g., 'default', 'page_1', 'block_2'. Defaults
828 * @return \Drupal\views\Plugin\views\display\DisplayPluginBase
829 * A new display plugin instance if executable is set, the new display ID
832 public function newDisplay($plugin_id = 'page', $title = NULL, $id = NULL) {
833 $this->initDisplay();
835 $id = $this->storage->addDisplay($plugin_id, $title, $id);
836 $this->displayHandlers->addInstanceId($id);
838 $display = $this->displayHandlers->get($id);
839 $display->newDisplay();
844 * Gets the current style plugin.
846 * @return \Drupal\views\Plugin\views\style\StylePluginBase
847 * The current style plugin.
849 public function getStyle() {
850 if (!isset($this->style_plugin)) {
854 return $this->style_plugin;
858 * Finds and initializes the style plugin.
860 * Note that arguments may have changed which style plugin we use, so
861 * check the view object first, then ask the display handler.
864 * TRUE if the style plugin was or could be initialized, FALSE otherwise.
866 public function initStyle() {
867 if (isset($this->style_plugin)) {
871 $this->style_plugin = $this->display_handler->getPlugin('style');
873 if (empty($this->style_plugin)) {
881 * Acquires and attaches all of the handlers.
883 public function initHandlers() {
884 $this->initDisplay();
885 if (empty($this->inited)) {
886 foreach ($this::getHandlerTypes() as $key => $info) {
887 $this->_initHandler($key, $info);
889 $this->inited = TRUE;
894 * Gets the current pager plugin.
896 * @return \Drupal\views\Plugin\views\pager\PagerPluginBase
897 * The current pager plugin.
899 public function getPager() {
900 if (!isset($this->pager)) {
908 * Initializes the pager.
910 * Like style initialization, pager initialization is held until late to allow
913 public function initPager() {
914 if (!isset($this->pager)) {
915 $this->pager = $this->display_handler->getPlugin('pager');
917 if ($this->pager->usePager()) {
918 $this->pager->setCurrentPage($this->current_page);
921 // These overrides may have been set earlier via $view->set_*
923 if (isset($this->items_per_page)) {
924 $this->pager->setItemsPerPage($this->items_per_page);
927 if (isset($this->offset)) {
928 $this->pager->setOffset($this->offset);
934 * Renders the pager, if necessary.
936 * @param string[] $exposed_input
937 * The input values from the exposed forms and sorts of the view.
939 * @return array|string
940 * The render array of the pager if it's set, blank string otherwise.
942 public function renderPager($exposed_input) {
943 if (!empty($this->pager) && $this->pager->usePager()) {
944 return $this->pager->render($exposed_input);
951 * Creates a list of base tables to be used by the view.
953 * This is used primarily for the UI. The display must be already initialized.
956 * An array of base tables to be used by the view.
958 public function getBaseTables() {
960 $this->storage->get('base_table') => TRUE,
964 foreach ($this->display_handler->getHandlers('relationship') as $handler) {
965 $base_tables[$handler->definition['base']] = TRUE;
971 * Returns the entity type of the base table, if available.
973 * @return \Drupal\Core\Entity\EntityType|false
974 * The entity type of the base table, or FALSE if none exists.
976 public function getBaseEntityType() {
977 if (!isset($this->baseEntityType)) {
978 $view_base_table = $this->storage->get('base_table');
979 $views_data = $this->viewsData->get($view_base_table);
980 if (!empty($views_data['table']['entity type'])) {
981 $entity_type_id = $views_data['table']['entity type'];
982 $this->baseEntityType = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
985 $this->baseEntityType = FALSE;
988 return $this->baseEntityType;
992 * Runs the preQuery() on all active handlers.
994 protected function _preQuery() {
995 foreach ($this::getHandlerTypes() as $key => $info) {
996 $handlers = &$this->$key;
998 foreach ($handlers as $id => $handler) {
999 $handlers[$id]->position = $position;
1000 $handlers[$id]->preQuery();
1007 * Runs the postExecute() on all active handlers.
1009 protected function _postExecute() {
1010 foreach ($this::getHandlerTypes() as $key => $info) {
1011 $handlers = &$this->$key;
1012 foreach ($handlers as $id => $handler) {
1013 $handlers[$id]->postExecute($this->result);
1019 * Attaches the views handler for the specific type.
1021 * @param string $key
1022 * One of 'argument', 'field', 'sort', 'filter', 'relationship'.
1023 * @param array $info
1024 * An array of views handler types use in the view with additional
1025 * information about them.
1027 protected function _initHandler($key, $info) {
1028 // Load the requested items from the display onto the object.
1029 $this->$key = &$this->display_handler->getHandlers($key);
1031 // This reference deals with difficult PHP indirection.
1032 $handlers = &$this->$key;
1034 // Run through and test for accessibility.
1035 foreach ($handlers as $id => $handler) {
1036 if (!$handler->access($this->user)) {
1037 unset($handlers[$id]);
1043 * Builds all the arguments.
1046 * TRUE if the arguments were built successfully, FALSE otherwise.
1048 protected function _buildArguments() {
1049 // Initially, we want to build sorts and fields. This can change, though,
1050 // if we get a summary view.
1051 if (empty($this->argument)) {
1057 $substitutions = [];
1061 $title = $this->display_handler->getOption('title');
1063 // Iterate through each argument and process.
1064 foreach ($this->argument as $id => $arg) {
1066 $argument = $this->argument[$id];
1068 if ($argument->broken()) {
1072 $argument->setRelationship();
1074 $arg = isset($this->args[$position]) ? $this->args[$position] : NULL;
1075 $argument->position = $position;
1077 if (isset($arg) || $argument->hasDefaultArgument()) {
1079 $arg = $argument->getDefaultArgument();
1080 // make sure default args get put back.
1082 $this->args[$position] = $arg;
1084 // remember that this argument was computed, not passed on the URL.
1085 $argument->is_default = TRUE;
1088 // Set the argument, which will also validate that the argument can be set.
1089 if (!$argument->setArgument($arg)) {
1090 $status = $argument->validateFail($arg);
1094 if ($argument->isException()) {
1095 $arg_title = $argument->exceptionTitle();
1098 $arg_title = $argument->getTitle();
1099 $argument->query($this->display_handler->useGroupBy());
1102 // Add this argument's substitution
1103 $substitutions["{{ arguments.$id }}"] = $arg_title;
1104 $substitutions["{{ raw_arguments.$id }}"] = strip_tags(Html::decodeEntities($arg));
1106 // Test to see if we should use this argument's title
1107 if (!empty($argument->options['title_enable']) && !empty($argument->options['title'])) {
1108 $title = $argument->options['title'];
1112 // determine default condition and handle.
1113 $status = $argument->defaultAction();
1117 // Be safe with references and loops:
1121 // set the title in the build info.
1122 if (!empty($title)) {
1123 $this->build_info['title'] = $title;
1126 // Store the arguments for later use.
1127 $this->build_info['substitutions'] = $substitutions;
1133 * Gets the current query plugin.
1135 * @return \Drupal\views\Plugin\views\query\QueryPluginBase
1136 * The current query plugin.
1138 public function getQuery() {
1139 if (!isset($this->query)) {
1143 return $this->query;
1147 * Initializes the query object for the view.
1150 * Always returns TRUE.
1152 public function initQuery() {
1153 if (!empty($this->query)) {
1154 $class = get_class($this->query);
1155 if ($class && $class != 'stdClass') {
1156 // return if query is already initialized.
1161 // Create and initialize the query object.
1162 $views_data = Views::viewsData()->get($this->storage->get('base_table'));
1163 $this->storage->set('base_field', !empty($views_data['table']['base']['field']) ? $views_data['table']['base']['field'] : '');
1164 if (!empty($views_data['table']['base']['database'])) {
1165 $this->base_database = $views_data['table']['base']['database'];
1168 $this->query = $this->display_handler->getPlugin('query');
1173 * Builds the query for the view.
1175 * @param string $display_id
1176 * The display ID of the view.
1179 * TRUE if the view build process was successful, FALSE if setting the
1180 * display fails or NULL if the view has been built already.
1182 public function build($display_id = NULL) {
1183 if (!empty($this->built)) {
1187 if (empty($this->current_display) || $display_id) {
1188 if (!$this->setDisplay($display_id)) {
1193 // Let modules modify the view just prior to building it.
1194 $module_handler = \Drupal::moduleHandler();
1195 $module_handler->invokeAll('views_pre_build', [$this]);
1197 // Attempt to load from cache.
1198 // @todo Load a build_info from cache.
1200 $start = microtime(TRUE);
1201 // If that fails, let's build!
1202 $this->build_info = [
1204 'count_query' => '',
1210 // Call a module hook and see if it wants to present us with a
1211 // pre-built query or instruct us not to build the query for
1213 // @todo: Implement this. Use the same mechanism Panels uses.
1215 // Run through our handlers and ensure they have necessary information.
1216 $this->initHandlers();
1218 // Let the handlers interact with each other if they really want.
1221 if ($this->display_handler->usesExposed()) {
1222 /** @var \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginInterface $exposed_form */
1223 $exposed_form = $this->display_handler->getPlugin('exposed_form');
1224 $this->exposed_widgets = $exposed_form->renderExposedForm();
1225 if (!empty($this->build_info['abort'])) {
1226 $this->built = TRUE;
1227 // Don't execute the query, $form_state, but rendering will still be executed to display the empty text.
1228 $this->executed = TRUE;
1229 return empty($this->build_info['fail']);
1233 // Build all the relationships first thing.
1234 $this->_build('relationship');
1236 // Set the filtering groups.
1237 if (!empty($this->filter)) {
1238 $filter_groups = $this->display_handler->getOption('filter_groups');
1239 if ($filter_groups) {
1240 $this->query->setGroupOperator($filter_groups['operator']);
1241 foreach ($filter_groups['groups'] as $id => $operator) {
1242 $this->query->setWhereGroup($operator, $id);
1247 // Build all the filters.
1248 $this->_build('filter');
1250 $this->build_sort = TRUE;
1252 // Arguments can, in fact, cause this whole thing to abort.
1253 if (!$this->_buildArguments()) {
1254 $this->build_time = microtime(TRUE) - $start;
1255 $this->attachDisplays();
1256 return $this->built;
1259 // Initialize the style; arguments may have changed which style we use,
1260 // so waiting as long as possible is important. But we need to know
1261 // about the style when we go to build fields.
1262 if (!$this->initStyle()) {
1263 $this->build_info['fail'] = TRUE;
1267 if ($this->style_plugin->usesFields()) {
1268 $this->_build('field');
1271 // Build our sort criteria if we were instructed to do so.
1272 if (!empty($this->build_sort)) {
1273 // Allow the style handler to deal with sorting.
1274 if ($this->style_plugin->buildSort()) {
1275 $this->_build('sort');
1277 // allow the plugin to build second sorts as well.
1278 $this->style_plugin->buildSortPost();
1281 // Allow area handlers to affect the query.
1282 $this->_build('header');
1283 $this->_build('footer');
1284 $this->_build('empty');
1286 // Allow display handler to affect the query:
1287 $this->display_handler->query($this->display_handler->useGroupBy());
1289 // Allow style handler to affect the query:
1290 $this->style_plugin->query($this->display_handler->useGroupBy());
1292 // Allow exposed form to affect the query:
1293 if (isset($exposed_form)) {
1294 $exposed_form->query();
1297 if (\Drupal::config('views.settings')->get('sql_signature')) {
1298 $this->query->addSignature($this);
1301 // Let modules modify the query just prior to finalizing it.
1302 $this->query->alter($this);
1304 // Only build the query if we weren't interrupted.
1305 if (empty($this->built)) {
1306 // Build the necessary info to execute the query.
1307 $this->query->build($this);
1310 $this->built = TRUE;
1311 $this->build_time = microtime(TRUE) - $start;
1314 $this->attachDisplays();
1316 // Let modules modify the view just after building it.
1317 $module_handler->invokeAll('views_post_build', [$this]);
1323 * Builds an individual set of handlers.
1325 * This is an internal method.
1327 * @todo Some filter needs this function, even it is internal.
1329 * @param string $key
1330 * The type of handlers (filter etc.) which should be iterated over to
1331 * build the relationship and query information.
1333 public function _build($key) {
1334 $handlers = &$this->$key;
1335 foreach ($handlers as $id => $data) {
1337 if (!empty($handlers[$id]) && is_object($handlers[$id])) {
1338 $multiple_exposed_input = [0 => NULL];
1339 if ($handlers[$id]->multipleExposedInput()) {
1340 $multiple_exposed_input = $handlers[$id]->groupMultipleExposedInput($this->exposed_data);
1342 foreach ($multiple_exposed_input as $group_id) {
1343 // Give this handler access to the exposed filter input.
1344 if (!empty($this->exposed_data)) {
1345 if ($handlers[$id]->isAGroup()) {
1346 $converted = $handlers[$id]->convertExposedInput($this->exposed_data, $group_id);
1347 $handlers[$id]->storeGroupInput($this->exposed_data, $converted);
1352 $rc = $handlers[$id]->acceptExposedInput($this->exposed_data);
1353 $handlers[$id]->storeExposedInput($this->exposed_data, $rc);
1358 $handlers[$id]->setRelationship();
1359 $handlers[$id]->query($this->display_handler->useGroupBy());
1366 * Executes the view's query.
1368 * @param string $display_id
1369 * The machine name of the display, which should be executed.
1372 * TRUE if the view execution was successful, FALSE otherwise. For example,
1373 * an argument could stop the process.
1375 public function execute($display_id = NULL) {
1376 if (empty($this->built)) {
1377 if (!$this->build($display_id)) {
1382 if (!empty($this->executed)) {
1386 // Don't allow to use deactivated displays, but display them on the live preview.
1387 if (!$this->display_handler->isEnabled() && empty($this->live_preview)) {
1388 $this->build_info['fail'] = TRUE;
1392 // Let modules modify the view just prior to executing it.
1393 $module_handler = \Drupal::moduleHandler();
1394 $module_handler->invokeAll('views_pre_execute', [$this]);
1396 // Check for already-cached results.
1397 /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */
1398 if (!empty($this->live_preview)) {
1399 $cache = Views::pluginManager('cache')->createInstance('none');
1402 $cache = $this->display_handler->getPlugin('cache');
1405 if ($cache->cacheGet('results')) {
1406 if ($this->pager->usePager()) {
1407 $this->pager->total_items = $this->total_rows;
1408 $this->pager->updatePageInfo();
1412 $this->query->execute($this);
1413 // Enforce the array key rule as documented in
1414 // views_plugin_query::execute().
1415 $this->result = array_values($this->result);
1416 $this->_postExecute();
1417 $cache->cacheSet('results');
1420 // Let modules modify the view just after executing it.
1421 $module_handler->invokeAll('views_post_execute', [$this]);
1423 return $this->executed = TRUE;
1427 * Renders this view for a certain display.
1429 * Note: You should better use just the preview function if you want to
1432 * @param string $display_id
1433 * The machine name of the display, which should be rendered.
1435 * @return array|null
1436 * A renderable array containing the view output or NULL if the build
1439 public function render($display_id = NULL) {
1440 $this->execute($display_id);
1442 // Check to see if the build failed.
1443 if (!empty($this->build_info['fail'])) {
1446 if (!empty($this->build_info['denied'])) {
1450 /** @var \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginInterface $exposed_form */
1451 $exposed_form = $this->display_handler->getPlugin('exposed_form');
1452 $exposed_form->preRender($this->result);
1454 $module_handler = \Drupal::moduleHandler();
1456 // @TODO In the longrun, it would be great to execute a view without
1457 // the theme system at all. See https://www.drupal.org/node/2322623.
1458 $active_theme = \Drupal::theme()->getActiveTheme();
1459 $themes = array_keys($active_theme->getBaseThemes());
1460 $themes[] = $active_theme->getName();
1462 // Check for already-cached output.
1463 /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */
1464 if (!empty($this->live_preview)) {
1465 $cache = Views::pluginManager('cache')->createInstance('none');
1468 $cache = $this->display_handler->getPlugin('cache');
1471 // Run preRender for the pager as it might change the result.
1472 if (!empty($this->pager)) {
1473 $this->pager->preRender($this->result);
1476 // Initialize the style plugin.
1479 if (!isset($this->response)) {
1480 // Set the response so other parts can alter it.
1481 $this->response = new Response('', 200);
1484 // Give field handlers the opportunity to perform additional queries
1485 // using the entire resultset prior to rendering.
1486 if ($this->style_plugin->usesFields()) {
1487 foreach ($this->field as $id => $handler) {
1488 if (!empty($this->field[$id])) {
1489 $this->field[$id]->preRender($this->result);
1494 $this->style_plugin->preRender($this->result);
1496 // Let each area handler have access to the result set.
1497 $areas = ['header', 'footer'];
1498 // Only call preRender() on the empty handlers if the result is empty.
1499 if (empty($this->result)) {
1502 foreach ($areas as $area) {
1503 foreach ($this->{$area} as $handler) {
1504 $handler->preRender($this->result);
1508 // Let modules modify the view just prior to rendering it.
1509 $module_handler->invokeAll('views_pre_render', [$this]);
1511 // Let the themes play too, because pre render is a very themey thing.
1512 foreach ($themes as $theme_name) {
1513 $function = $theme_name . '_views_pre_render';
1514 if (function_exists($function)) {
1519 $this->display_handler->output = $this->display_handler->render();
1521 $exposed_form->postRender($this->display_handler->output);
1523 $cache->postRender($this->display_handler->output);
1525 // Let modules modify the view output after it is rendered.
1526 $module_handler->invokeAll('views_post_render', [$this, &$this->display_handler->output, $cache]);
1528 // Let the themes play too, because post render is a very themey thing.
1529 foreach ($themes as $theme_name) {
1530 $function = $theme_name . '_views_post_render';
1531 if (function_exists($function)) {
1532 $function($this, $this->display_handler->output, $cache);
1536 return $this->display_handler->output;
1540 * Gets the cache tags associated with the executed view.
1542 * Note: The cache plugin controls the used tags, so you can override it, if
1546 * An array of cache tags.
1548 public function getCacheTags() {
1549 $this->initDisplay();
1550 /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */
1551 $cache = $this->display_handler->getPlugin('cache');
1552 return $cache->getCacheTags();
1556 * Builds the render array outline for the given display.
1558 * This render array has a #pre_render callback which will call
1559 * ::executeDisplay in order to actually execute the view and then build the
1560 * final render array structure.
1562 * @param string $display_id
1564 * @param array $args
1565 * An array of arguments passed along to the view.
1566 * @param bool $cache
1567 * (optional) Should the result be render cached.
1569 * @return array|null
1570 * A renderable array with #type 'view' or NULL if the display ID was
1573 public function buildRenderable($display_id = NULL, $args = [], $cache = TRUE) {
1574 // @todo Extract that into a generic method.
1575 if (empty($this->current_display) || $this->current_display != $this->chooseDisplay($display_id)) {
1576 if (!$this->setDisplay($display_id)) {
1581 return $this->display_handler->buildRenderable($args, $cache);
1585 * Executes the given display, with the given arguments.
1587 * To be called externally by whatever mechanism invokes the view,
1588 * such as a page callback, hook_block, etc.
1590 * This function should NOT be used by anything external as this
1591 * returns data in the format specified by the display. It can also
1592 * have other side effects that are only intended for the 'proper'
1593 * use of the display, such as setting page titles.
1595 * If you simply want to view the display, use View::preview() instead.
1597 * @param string $display_id
1598 * The display ID of the view to be executed.
1599 * @param string[] $args
1600 * The arguments to be passed to the view.
1602 * @return array|null
1603 * A renderable array containing the view output or NULL if the display ID
1604 * of the view to be executed doesn't exist.
1606 public function executeDisplay($display_id = NULL, $args = []) {
1607 if (empty($this->current_display) || $this->current_display != $this->chooseDisplay($display_id)) {
1608 if (!$this->setDisplay($display_id)) {
1613 $this->preExecute($args);
1616 $output = $this->display_handler->execute();
1618 $this->postExecute();
1623 * Previews the given display, with the given arguments.
1625 * To be called externally, probably by an AJAX handler of some flavor.
1626 * Can also be called when views are embedded, as this guarantees
1627 * normalized output.
1629 * This function does not do any access checks on the view. It is the
1630 * responsibility of the caller to check $view->access() or implement other
1631 * access logic. To render the view normally with access checks, use
1632 * views_embed_view() instead.
1634 * @return array|null
1635 * A renderable array containing the view output or NULL if the display ID
1636 * of the view to be executed doesn't exist.
1638 public function preview($display_id = NULL, $args = []) {
1639 if (empty($this->current_display) || ((!empty($display_id)) && $this->current_display != $display_id)) {
1640 if (!$this->setDisplay($display_id)) {
1645 $this->preview = TRUE;
1646 $this->preExecute($args);
1647 // Preview the view.
1648 $output = $this->display_handler->preview();
1650 $this->postExecute();
1655 * Runs attachments and lets the display do what it needs to before running.
1657 * @param array $args
1658 * An array of arguments from the URL that can be used by the view.
1660 public function preExecute($args = []) {
1661 $this->old_view[] = views_get_current_view();
1662 views_set_current_view($this);
1663 $display_id = $this->current_display;
1665 // Prepare the view with the information we have, but only if we were
1666 // passed arguments, as they may have been set previously.
1668 $this->setArguments($args);
1671 // Let modules modify the view just prior to executing it.
1672 \Drupal::moduleHandler()->invokeAll('views_pre_view', [$this, $display_id, &$this->args]);
1674 // Allow hook_views_pre_view() to set the dom_id, then ensure it is set.
1675 $this->dom_id = !empty($this->dom_id) ? $this->dom_id : hash('sha256', $this->storage->id() . REQUEST_TIME . mt_rand());
1677 // Allow the display handler to set up for execution
1678 $this->display_handler->preExecute();
1682 * Unsets the current view, mostly.
1684 public function postExecute() {
1685 // unset current view so we can be properly destructed later on.
1686 // Return the previous value in case we're an attachment.
1688 if ($this->old_view) {
1689 $old_view = array_pop($this->old_view);
1692 views_set_current_view(isset($old_view) ? $old_view : FALSE);
1696 * Runs attachment displays for the view.
1698 public function attachDisplays() {
1699 if (!empty($this->is_attachment)) {
1703 if (!$this->display_handler->acceptAttachments()) {
1707 $this->is_attachment = TRUE;
1708 // Find out which other displays attach to the current one.
1709 foreach ($this->display_handler->getAttachedDisplays() as $id) {
1710 $display_handler = $this->displayHandlers->get($id);
1711 // Only attach enabled attachments.
1712 if ($display_handler->isEnabled()) {
1713 $cloned_view = Views::executableFactory()->get($this->storage);
1714 $display_handler->attachTo($cloned_view, $this->current_display, $this->element);
1717 $this->is_attachment = FALSE;
1721 * Determines if the given user has access to the view.
1723 * Note that this sets the display handler if it hasn't been set.
1725 * @param string $displays
1726 * The machine name of the display.
1727 * @param \Drupal\Core\Session\AccountInterface $account
1731 * TRUE if the user has access to the view, FALSE otherwise.
1733 public function access($displays = NULL, $account = NULL) {
1734 // No one should have access to disabled views.
1735 if (!$this->storage->status()) {
1739 if (!isset($this->current_display)) {
1740 $this->initDisplay();
1744 $account = $this->user;
1747 // We can't use choose_display() here because that function
1749 $displays = (array)$displays;
1750 foreach ($displays as $display_id) {
1751 if ($this->displayHandlers->has($display_id)) {
1752 if (($display = $this->displayHandlers->get($display_id)) && $display->access($account)) {
1762 * Sets the used response object of the view.
1764 * @param \Symfony\Component\HttpFoundation\Response $response
1765 * The response object which should be set.
1767 public function setResponse(Response $response) {
1768 $this->response = $response;
1772 * Gets the response object used by the view.
1774 * @return \Symfony\Component\HttpFoundation\Response
1775 * The response object of the view.
1777 public function getResponse() {
1778 if (!isset($this->response)) {
1779 $this->response = new Response();
1781 return $this->response;
1785 * Sets the request object.
1787 * @param \Symfony\Component\HttpFoundation\Request $request
1788 * The request object.
1790 public function setRequest(Request $request) {
1791 $this->request = $request;
1795 * Gets the request object.
1797 * @return \Symfony\Component\HttpFoundation\Request
1798 * The request object.
1800 public function getRequest() {
1801 return $this->request;
1805 * Gets the view's current title.
1807 * This can change depending upon how it was built.
1809 * @return string|false
1810 * The view title, FALSE if the display is not set.
1812 public function getTitle() {
1813 if (empty($this->display_handler)) {
1814 if (!$this->setDisplay('default')) {
1819 // During building, we might find a title override. If so, use it.
1820 if (!empty($this->build_info['title'])) {
1821 $title = $this->build_info['title'];
1824 $title = $this->display_handler->getOption('title');
1827 // Allow substitutions from the first row.
1828 if ($this->initStyle()) {
1829 $title = $this->style_plugin->tokenizeValue($title, 0);
1835 * Overrides the view's current title.
1837 * The tokens in the title get's replaced before rendering.
1840 * Always returns TRUE.
1842 public function setTitle($title) {
1843 $this->build_info['title'] = $title;
1848 * Forces the view to build a title.
1850 public function buildTitle() {
1851 $this->initDisplay();
1853 if (empty($this->built)) {
1857 $this->initHandlers();
1859 $this->_buildArguments();
1863 * Determines whether you can link to the view or a particular display.
1865 * Some displays (e.g. block displays) do not have their own route, but may
1866 * optionally provide a link to another display that does have a route.
1868 * @param array $args
1869 * (optional) The arguments.
1870 * @param string $display_id
1871 * (optional) The display ID. The current display will be used by default.
1874 * TRUE if the current display has a valid route available, FALSE otherwise.
1876 public function hasUrl($args = NULL, $display_id = NULL) {
1877 if (!empty($this->override_url)) {
1881 // If the display has a valid route available (either its own or for a
1882 // linked display), then we can provide a URL for it.
1883 $display_handler = $this->displayHandlers->get($display_id ?: $this->current_display)->getRoutedDisplay();
1884 if (!$display_handler instanceof DisplayRouterInterface) {
1888 // Look up the route name to make sure it exists. The name may exist, but
1889 // not be available yet in some instances when editing a view and doing
1891 $provider = \Drupal::service('router.route_provider');
1893 $provider->getRouteByName($display_handler->getRouteName());
1895 catch (RouteNotFoundException $e) {
1903 * Gets the URL for the current view.
1905 * This URL will be adjusted for arguments.
1907 * @param array $args
1908 * (optional) Passed in arguments.
1909 * @param string $display_id
1910 * (optional) Specify the display ID to link to, fallback to the current ID.
1912 * @return \Drupal\Core\Url
1913 * The URL of the current view.
1915 * @throws \InvalidArgumentException
1916 * Thrown when the current view doesn't have a route available.
1918 public function getUrl($args = NULL, $display_id = NULL) {
1919 if (!empty($this->override_url)) {
1920 return $this->override_url;
1923 $display_handler = $this->displayHandlers->get($display_id ?: $this->current_display)->getRoutedDisplay();
1924 if (!$display_handler instanceof DisplayRouterInterface) {
1925 throw new \InvalidArgumentException('You cannot create a URL to a display without routes.');
1928 if (!isset($args)) {
1929 $args = $this->args;
1931 // Exclude arguments that were computed, not passed on the URL.
1933 if (!empty($this->argument)) {
1934 foreach ($this->argument as $argument) {
1935 if (!empty($argument->is_default) && !empty($argument->options['default_argument_skip_url'])) {
1936 unset($args[$position]);
1943 $path = $this->getPath();
1945 // Don't bother working if there's nothing to do:
1946 if (empty($path) || (empty($args) && strpos($path, '%') === FALSE)) {
1947 return $display_handler->getUrlInfo();
1950 $argument_keys = isset($this->argument) ? array_keys($this->argument) : [];
1951 $id = current($argument_keys);
1953 /** @var \Drupal\Core\Url $url */
1954 $url = $display_handler->getUrlInfo();
1955 $route = $this->routeProvider->getRouteByName($url->getRouteName());
1957 $variables = $route->compile()->getVariables();
1958 $parameters = $url->getRouteParameters();
1960 foreach ($variables as $variable_name) {
1962 // Try to never put % in a URL; use the wildcard instead.
1963 if ($id && !empty($this->argument[$id]->options['exception']['value'])) {
1964 $parameters[$variable_name] = $this->argument[$id]->options['exception']['value'];
1967 // Provide some fallback in case no exception value could be found.
1968 $parameters[$variable_name] = '*';
1972 $parameters[$variable_name] = array_shift($args);
1976 $id = next($argument_keys);
1980 $url->setRouteParameters($parameters);
1985 * Gets the Url object associated with the display handler.
1987 * @param string $display_id
1988 * (optional) The display ID (used only to detail an exception).
1990 * @return \Drupal\Core\Url
1991 * The display handlers URL object.
1993 * @throws \InvalidArgumentException
1994 * Thrown when the display plugin does not have a URL to return.
1996 public function getUrlInfo($display_id = '') {
1997 $this->initDisplay();
1998 if (!$this->display_handler instanceof DisplayRouterInterface) {
1999 throw new \InvalidArgumentException("You cannot generate a URL for the display '$display_id'");
2001 return $this->display_handler->getUrlInfo();
2005 * Gets the base path used for this view.
2007 * @return string|false
2008 * The base path used for the view or FALSE if setting the display fails.
2010 public function getPath() {
2011 if (!empty($this->override_path)) {
2012 return $this->override_path;
2015 if (empty($this->display_handler)) {
2016 if (!$this->setDisplay('default')) {
2020 return $this->display_handler->getPath();
2024 * Gets the current user.
2026 * Views plugins can receive the current user in order to not need dependency
2029 * @return \Drupal\Core\Session\AccountInterface
2032 public function getUser() {
2037 * Creates a duplicate ViewExecutable object.
2039 * Makes a copy of this view that has been sanitized of handlers, any runtime
2040 * data, ID, and UUID.
2042 public function createDuplicate() {
2043 return $this->storage->createDuplicate()->getExecutable();
2047 * Unsets references so that a $view object may be properly garbage collected.
2049 public function destroy() {
2050 foreach ($this::getHandlerTypes() as $type => $info) {
2051 if (isset($this->$type)) {
2052 foreach ($this->{$type} as $handler) {
2053 $handler->destroy();
2058 if (isset($this->style_plugin)) {
2059 $this->style_plugin->destroy();
2062 $reflection = new \ReflectionClass($this);
2063 $defaults = $reflection->getDefaultProperties();
2064 // The external dependencies should not be reset. This is not generated by
2065 // the execution of a view.
2067 $defaults['storage'],
2069 $defaults['request'],
2070 $defaults['routeProvider'],
2071 $defaults['viewsData']
2074 foreach ($defaults as $property => $default) {
2075 $this->{$property} = $default;
2080 * Makes sure the view is completely valid.
2083 * An array of error strings. This will be empty if there are no validation
2086 public function validate() {
2089 $this->initDisplay();
2090 $current_display = $this->current_display;
2092 foreach ($this->displayHandlers as $id => $display) {
2093 if (!empty($display)) {
2094 if (!empty($display->display['deleted'])) {
2098 $result = $this->displayHandlers->get($id)->validate();
2099 if (!empty($result) && is_array($result)) {
2100 $errors[$id] = $result;
2105 $this->setDisplay($current_display);
2111 * Provides a list of views handler types used in a view.
2113 * This also provides some information about the views handler types.
2116 * An array of associative arrays containing:
2117 * - title: The title of the handler type.
2118 * - ltitle: The lowercase title of the handler type.
2119 * - stitle: A singular title of the handler type.
2120 * - lstitle: A singular lowercase title of the handler type.
2121 * - plural: Plural version of the handler type.
2122 * - (optional) type: The actual internal used handler type. This key is
2123 * just used for header,footer,empty to link to the internal type: area.
2125 public static function getHandlerTypes() {
2126 return Views::getHandlerTypes();
2130 * Returns the valid types of plugins that can be used.
2133 * An array of plugin type strings.
2135 public static function getPluginTypes($type = NULL) {
2136 return Views::getPluginTypes($type);
2140 * Adds an instance of a handler to the view.
2142 * Items may be fields, filters, sort criteria, or arguments.
2144 * @param string $display_id
2145 * The machine name of the display.
2146 * @param string $type
2147 * The type of handler being added.
2148 * @param string $table
2149 * The name of the table this handler is from.
2150 * @param string $field
2151 * The name of the field this handler is from.
2152 * @param array $options
2153 * (optional) Extra options for this instance. Defaults to an empty array.
2155 * (optional) A unique ID for this handler instance. Defaults to NULL, in
2156 * which case one will be generated.
2159 * The unique ID for this handler instance.
2161 public function addHandler($display_id, $type, $table, $field, $options = [], $id = NULL) {
2162 $types = $this::getHandlerTypes();
2163 $this->setDisplay($display_id);
2165 $data = $this->viewsData->get($table);
2166 $fields = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']);
2169 $id = $this->generateHandlerId($field, $fields);
2172 // If the desired type is not found, use the original value directly.
2173 $handler_type = !empty($types[$type]['type']) ? $types[$type]['type'] : $type;
2181 if (isset($data['table']['entity type'])) {
2182 $fields[$id]['entity_type'] = $data['table']['entity type'];
2184 if (isset($data[$field]['entity field'])) {
2185 $fields[$id]['entity_field'] = $data[$field]['entity field'];
2188 // Load the plugin ID if available.
2189 if (isset($data[$field][$handler_type]['id'])) {
2190 $fields[$id]['plugin_id'] = $data[$field][$handler_type]['id'];
2193 $this->displayHandlers->get($display_id)->setOption($types[$type]['plural'], $fields);
2199 * Generates a unique ID for an handler instance.
2201 * These handler instances are typically fields, filters, sort criteria, or
2204 * @param string $requested_id
2205 * The requested ID for the handler instance.
2206 * @param array $existing_items
2207 * An array of existing handler instances, keyed by their IDs.
2210 * A unique ID. This will be equal to $requested_id if no handler instance
2211 * with that ID already exists. Otherwise, it will be appended with an
2212 * integer to make it unique, e.g., "{$requested_id}_1",
2213 * "{$requested_id}_2", etc.
2215 public static function generateHandlerId($requested_id, $existing_items) {
2217 $id = $requested_id;
2218 while (!empty($existing_items[$id])) {
2219 $id = $requested_id . '_' . ++$count;
2225 * Gets an array of handler instances for the current display.
2227 * @param string $type
2228 * The type of handlers to retrieve.
2229 * @param string $display_id
2230 * (optional) A specific display machine name to use. If NULL, the current
2231 * display will be used.
2234 * An array of handler instances of a given type for this display.
2236 public function getHandlers($type, $display_id = NULL) {
2237 $old_display_id = !empty($this->current_display) ? $this->current_display : 'default';
2239 $this->setDisplay($display_id);
2241 if (!isset($display_id)) {
2242 $display_id = $this->current_display;
2245 // Get info about the types so we can get the right data.
2246 $types = static::getHandlerTypes();
2248 $handlers = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']);
2250 // Restore initial display id (if any) or set to 'default'.
2251 if ($display_id != $old_display_id) {
2252 $this->setDisplay($old_display_id);
2258 * Gets the configuration of a handler instance on a given display.
2260 * @param string $display_id
2261 * The machine name of the display.
2262 * @param string $type
2263 * The type of handler to retrieve.
2265 * The ID of the handler to retrieve.
2267 * @return array|null
2268 * Either the handler instance's configuration, or NULL if the handler is
2269 * not used on the display.
2271 public function getHandler($display_id, $type, $id) {
2272 // Get info about the types so we can get the right data.
2273 $types = static::getHandlerTypes();
2274 // Initialize the display
2275 $this->setDisplay($display_id);
2277 // Get the existing configuration
2278 $fields = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']);
2280 return isset($fields[$id]) ? $fields[$id] : NULL;
2284 * Sets the configuration of a handler instance on a given display.
2286 * @param string $display_id
2287 * The machine name of the display.
2288 * @param string $type
2289 * The type of handler being set.
2291 * The ID of the handler being set.
2292 * @param array|null $item
2293 * An array of configuration for a handler, or NULL to remove this instance.
2295 * @see set_item_option()
2297 public function setHandler($display_id, $type, $id, $item) {
2298 // Get info about the types so we can get the right data.
2299 $types = static::getHandlerTypes();
2300 // Initialize the display.
2301 $this->setDisplay($display_id);
2303 // Get the existing configuration.
2304 $fields = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']);
2306 $fields[$id] = $item;
2310 $this->displayHandlers->get($display_id)->setOption($types[$type]['plural'], $fields);
2314 * Removes configuration for a handler instance on a given display.
2316 * @param string $display_id
2317 * The machine name of the display.
2318 * @param string $type
2319 * The type of handler being removed.
2321 * The ID of the handler being removed.
2323 public function removeHandler($display_id, $type, $id) {
2324 // Get info about the types so we can get the right data.
2325 $types = static::getHandlerTypes();
2326 // Initialize the display.
2327 $this->setDisplay($display_id);
2329 // Get the existing configuration.
2330 $fields = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']);
2332 unset($fields[$id]);
2335 $this->displayHandlers->get($display_id)->setOption($types[$type]['plural'], $fields);
2339 * Sets an option on a handler instance.
2341 * Use this only if you have just 1 or 2 options to set; if you have many,
2342 * consider getting the handler instance, adding the options and using
2343 * set_item() directly.
2345 * @param string $display_id
2346 * The machine name of the display.
2347 * @param string $type
2348 * The type of handler being set.
2350 * The ID of the handler being set.
2351 * @param string $option
2352 * The configuration key for the value being set.
2353 * @param mixed $value
2354 * The value being set.
2358 public function setHandlerOption($display_id, $type, $id, $option, $value) {
2359 $item = $this->getHandler($display_id, $type, $id);
2360 $item[$option] = $value;
2361 $this->setHandler($display_id, $type, $id, $item);
2365 * Enables admin links on the rendered view.
2367 * @param bool $show_admin_links
2368 * TRUE if the admin links should be shown.
2370 public function setShowAdminLinks($show_admin_links) {
2371 $this->showAdminLinks = (bool) $show_admin_links;
2375 * Returns whether admin links should be rendered on the view.
2378 * TRUE if admin links should be rendered, else FALSE.
2380 public function getShowAdminLinks() {
2381 if (!isset($this->showAdminLinks)) {
2382 return $this->getDisplay()->getOption('show_admin_links');
2384 return $this->showAdminLinks;
2388 * Merges all plugin default values for each display.
2390 public function mergeDefaults() {
2391 $this->initDisplay();
2392 // Initialize displays and merge all plugin defaults.
2393 foreach ($this->displayHandlers as $display) {
2394 $display->mergeDefaults();
2399 * Provides a full array of possible theme functions to try for a given hook.
2401 * @param string $hook
2402 * The hook to use. This is the base theme/template name.
2405 * An array of theme hook suggestions.
2407 public function buildThemeFunctions($hook) {
2409 $display = isset($this->display_handler) ? $this->display_handler->display : NULL;
2410 $id = $this->storage->id();
2413 $themes[] = $hook . '__' . $id . '__' . $display['id'];
2414 $themes[] = $hook . '__' . $display['id'];
2415 // Add theme suggestions for each single tag.
2416 foreach (Tags::explode($this->storage->get('tag')) as $tag) {
2417 $themes[] = $hook . '__' . preg_replace('/[^a-z0-9]/', '_', strtolower($tag));
2420 if ($display['id'] != $display['display_plugin']) {
2421 $themes[] = $hook . '__' . $id . '__' . $display['display_plugin'];
2422 $themes[] = $hook . '__' . $display['display_plugin'];
2425 $themes[] = $hook . '__' . $id;
2432 * Determines if this view has form elements.
2435 * TRUE if this view contains handlers with views form implementations,
2438 public function hasFormElements() {
2439 foreach ($this->field as $field) {
2440 if (property_exists($field, 'views_form_callback') || method_exists($field, 'viewsForm')) {
2444 $area_handlers = array_merge(array_values($this->header), array_values($this->footer));
2445 $empty = empty($this->result);
2446 foreach ($area_handlers as $area) {
2447 if (method_exists($area, 'viewsForm') && !$area->viewsFormEmpty($empty)) {
2456 * Gets dependencies for the view.
2458 * @see \Drupal\views\Entity\View::calculateDependencies()
2459 * @see \Drupal\views\Entity\View::getDependencies()
2462 * An array of dependencies grouped by type (module, theme, entity).
2464 public function getDependencies() {
2465 return $this->storage->calculateDependencies()->getDependencies();
2471 public function serialize() {
2473 // Only serialize the storage entity ID.
2474 $this->storage->id(),
2475 $this->current_display,
2477 $this->current_page,
2478 $this->exposed_input,
2479 $this->exposed_raw_input,
2480 $this->exposed_data,
2489 public function unserialize($serialized) {
2490 list($storage, $current_display, $args, $current_page, $exposed_input, $exposed_raw_input, $exposed_data, $dom_id, $executed) = unserialize($serialized);
2492 // There are cases, like in testing, where we don't have a container
2494 if (\Drupal::hasContainer()) {
2495 $this->setRequest(\Drupal::request());
2496 $this->user = \Drupal::currentUser();
2498 $this->storage = \Drupal::entityManager()->getStorage('view')->load($storage);
2500 $this->setDisplay($current_display);
2501 $this->setArguments($args);
2502 $this->setCurrentPage($current_page);
2503 $this->setExposedInput($exposed_input);
2504 $this->exposed_data = $exposed_data;
2505 $this->exposed_raw_input = $exposed_raw_input;
2506 $this->dom_id = $dom_id;
2508 $this->initHandlers();
2510 // If the display was previously executed, execute it now.
2512 $this->execute($this->current_display);