1<?php 2 3namespace Drupal\views; 4 5use Drupal\Component\Render\FormattableMarkup; 6use Drupal\Component\Utility\Html; 7use Drupal\Component\Utility\Tags; 8use Drupal\Core\Routing\RouteProviderInterface; 9use Drupal\Core\Session\AccountInterface; 10use Drupal\views\Plugin\views\display\DisplayRouterInterface; 11use Symfony\Component\HttpFoundation\Request; 12use Symfony\Component\HttpFoundation\Response; 13use Symfony\Component\Routing\Exception\RouteNotFoundException; 14 15/** 16 * Represents a view as a whole. 17 * 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. 20 * 21 * This class does not implement the Serializable interface since problems 22 * occurred when using the serialize method. 23 * 24 * @see https://www.drupal.org/node/2849674 25 * @see https://bugs.php.net/bug.php?id=66052 26 */ 27class ViewExecutable { 28 29 /** 30 * The config entity in which the view is stored. 31 * 32 * @var \Drupal\views\Entity\View 33 */ 34 public $storage; 35 36 /** 37 * Whether or not the view has been built. 38 * 39 * @todo Group with other static properties. 40 * 41 * @var bool 42 */ 43 public $built = FALSE; 44 45 /** 46 * Whether the view has been executed/query has been run. 47 * 48 * @todo Group with other static properties. 49 * 50 * @var bool 51 */ 52 public $executed = FALSE; 53 54 /** 55 * Any arguments that have been passed into the view. 56 * 57 * @var array 58 */ 59 public $args = []; 60 61 /** 62 * An array of build info. 63 * 64 * @var array 65 */ 66 public $build_info = []; 67 68 /** 69 * Whether this view uses AJAX. 70 * 71 * @var bool 72 */ 73 protected $ajaxEnabled = FALSE; 74 75 /** 76 * Where the results of a query will go. 77 * 78 * The array must use a numeric index starting at 0. 79 * 80 * @var \Drupal\views\ResultRow[] 81 */ 82 public $result = []; 83 84 // May be used to override the current pager info. 85 86 /** 87 * The current page. If the view uses pagination. 88 * 89 * @var int 90 */ 91 protected $current_page = NULL; 92 93 /** 94 * The number of items per page. 95 * 96 * @var int 97 */ 98 protected $items_per_page = NULL; 99 100 /** 101 * The pager offset. 102 * 103 * @var int 104 */ 105 protected $offset = NULL; 106 107 /** 108 * The total number of rows returned from the query. 109 * 110 * @var int 111 */ 112 public $total_rows = NULL; 113 114 /** 115 * Attachments to place before the view. 116 * 117 * @var array 118 */ 119 public $attachment_before = []; 120 121 /** 122 * Attachments to place after the view. 123 * 124 * @var array 125 */ 126 public $attachment_after = []; 127 128 /** 129 * Feed icons attached to the view. 130 * 131 * @var array 132 */ 133 public $feedIcons = []; 134 135 // Exposed widget input 136 137 /** 138 * All the form data from $form_state->getValues(). 139 * 140 * @var array 141 */ 142 public $exposed_data = []; 143 144 /** 145 * An array of input values from exposed forms. 146 * 147 * @var array 148 */ 149 protected $exposed_input = []; 150 151 /** 152 * Exposed widget input directly from the $form_state->getValues(). 153 * 154 * @var array 155 */ 156 public $exposed_raw_input = []; 157 158 /** 159 * Used to store views that were previously running if we recurse. 160 * 161 * @var \Drupal\views\ViewExecutable[] 162 */ 163 public $old_view = []; 164 165 /** 166 * To avoid recursion in views embedded into areas. 167 * 168 * @var \Drupal\views\ViewExecutable[] 169 */ 170 public $parent_views = []; 171 172 /** 173 * Whether this view is an attachment to another view. 174 * 175 * @var bool 176 */ 177 public $is_attachment = NULL; 178 179 /** 180 * Identifier of the current display. 181 * 182 * @var string 183 */ 184 public $current_display; 185 186 /** 187 * Where the $query object will reside. 188 * 189 * @var \Drupal\views\Plugin\views\query\QueryPluginBase 190 */ 191 public $query = NULL; 192 193 /** 194 * The used pager plugin used by the current executed view. 195 * 196 * @var \Drupal\views\Plugin\views\pager\PagerPluginBase 197 */ 198 public $pager = NULL; 199 200 /** 201 * The current used display plugin. 202 * 203 * @var \Drupal\views\Plugin\views\display\DisplayPluginBase 204 */ 205 public $display_handler; 206 207 /** 208 * The list of used displays of the view. 209 * 210 * An array containing Drupal\views\Plugin\views\display\DisplayPluginBase 211 * objects. 212 * 213 * @var \Drupal\views\DisplayPluginCollection 214 */ 215 public $displayHandlers; 216 217 /** 218 * The current used style plugin. 219 * 220 * @var \Drupal\views\Plugin\views\style\StylePluginBase 221 */ 222 public $style_plugin; 223 224 /** 225 * The current used row plugin, if the style plugin supports row plugins. 226 * 227 * @var \Drupal\views\Plugin\views\row\RowPluginBase 228 */ 229 public $rowPlugin; 230 231 /** 232 * Stores the current active row while rendering. 233 * 234 * @var int 235 */ 236 public $row_index; 237 238 /** 239 * Allow to override the url of the current view. 240 * 241 * @var \Drupal\Core\Url 242 */ 243 public $override_url; 244 245 /** 246 * Allow to override the path used for generated urls. 247 * 248 * @var string 249 */ 250 public $override_path = NULL; 251 252 /** 253 * Allow to override the used database which is used for this query. 254 * 255 * @var bool 256 */ 257 public $base_database = NULL; 258 259 // Handlers which are active on this view. 260 261 /** 262 * Stores the field handlers which are initialized on this view. 263 * 264 * @var \Drupal\views\Plugin\views\field\FieldPluginBase[] 265 */ 266 public $field; 267 268 /** 269 * Stores the argument handlers which are initialized on this view. 270 * 271 * @var \Drupal\views\Plugin\views\argument\ArgumentPluginBase[] 272 */ 273 public $argument; 274 275 /** 276 * Stores the sort handlers which are initialized on this view. 277 * 278 * @var \Drupal\views\Plugin\views\sort\SortPluginBase[] 279 */ 280 public $sort; 281 282 /** 283 * Stores the filter handlers which are initialized on this view. 284 * 285 * @var \Drupal\views\Plugin\views\filter\FilterPluginBase[] 286 */ 287 public $filter; 288 289 /** 290 * Stores the relationship handlers which are initialized on this view. 291 * 292 * @var \Drupal\views\Plugin\views\relationship\RelationshipPluginBase[] 293 */ 294 public $relationship; 295 296 /** 297 * Stores the area handlers for the header which are initialized on this view. 298 * 299 * @var \Drupal\views\Plugin\views\area\AreaPluginBase[] 300 */ 301 public $header; 302 303 /** 304 * Stores the area handlers for the footer which are initialized on this view. 305 * 306 * @var \Drupal\views\Plugin\views\area\AreaPluginBase[] 307 */ 308 public $footer; 309 310 /** 311 * Stores the area handlers for the empty text which are initialized on this view. 312 * 313 * An array containing Drupal\views\Plugin\views\area\AreaPluginBase objects. 314 * 315 * @var \Drupal\views\Plugin\views\area\AreaPluginBase[] 316 */ 317 public $empty; 318 319 /** 320 * Stores the current response object. 321 * 322 * @var \Symfony\Component\HttpFoundation\Response 323 */ 324 protected $response = NULL; 325 326 /** 327 * Stores the current request object. 328 * 329 * @var \Symfony\Component\HttpFoundation\Request 330 */ 331 protected $request; 332 333 /** 334 * Does this view already have loaded its handlers. 335 * 336 * @todo Group with other static properties. 337 * 338 * @var bool 339 */ 340 public $inited; 341 342 /** 343 * The rendered output of the exposed form. 344 * 345 * @var string 346 */ 347 public $exposed_widgets; 348 349 /** 350 * If this view has been previewed. 351 * 352 * @var bool 353 */ 354 public $preview; 355 356 /** 357 * Force the query to calculate the total number of results. 358 * 359 * @todo Move to the query. 360 * 361 * @var bool 362 */ 363 public $get_total_rows; 364 365 /** 366 * Indicates if the sorts have been built. 367 * 368 * @todo Group with other static properties. 369 * 370 * @var bool 371 */ 372 public $build_sort; 373 374 /** 375 * Stores the many-to-one tables for performance. 376 * 377 * @var array 378 */ 379 public $many_to_one_tables; 380 381 /** 382 * A unique identifier which allows to update multiple views output via js. 383 * 384 * @var string 385 */ 386 public $dom_id; 387 388 /** 389 * A render array container to store render related information. 390 * 391 * For example you can alter the array and attach some asset library or JS 392 * settings via the #attached key. This is the required way to add custom 393 * CSS or JS. 394 * 395 * @var array 396 * 397 * @see \Drupal\Core\Render\AttachmentsResponseProcessorInterface::processAttachments() 398 */ 399 public $element = [ 400 '#attached' => [ 401 'library' => ['views/views.module'], 402 'drupalSettings' => [], 403 ], 404 '#cache' => [], 405 ]; 406 407 /** 408 * The current user. 409 * 410 * @var \Drupal\Core\Session\AccountInterface 411 */ 412 protected $user; 413 414 /** 415 * Should the admin links be shown on the rendered view. 416 * 417 * @var bool 418 */ 419 protected $showAdminLinks; 420 421 /** 422 * The views data. 423 * 424 * @var \Drupal\views\ViewsData 425 */ 426 protected $viewsData; 427 428 /** 429 * The route provider. 430 * 431 * @var \Drupal\Core\Routing\RouteProviderInterface 432 */ 433 protected $routeProvider; 434 435 /** 436 * The entity type of the base table, if available. 437 * 438 * @var \Drupal\Core\Entity\EntityTypeInterface|false 439 */ 440 protected $baseEntityType; 441 442 /** 443 * Holds all necessary data for proper unserialization. 444 * 445 * @var array 446 */ 447 protected $serializationData; 448 449 /** 450 * Constructs a new ViewExecutable object. 451 * 452 * @param \Drupal\views\ViewEntityInterface $storage 453 * The view config entity the actual information is stored on. 454 * @param \Drupal\Core\Session\AccountInterface $user 455 * The current user. 456 * @param \Drupal\views\ViewsData $views_data 457 * The views data. 458 * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider 459 * The route provider. 460 */ 461 public function __construct(ViewEntityInterface $storage, AccountInterface $user, ViewsData $views_data, RouteProviderInterface $route_provider) { 462 // Reference the storage and the executable to each other. 463 $this->storage = $storage; 464 $this->storage->set('executable', $this); 465 $this->user = $user; 466 $this->viewsData = $views_data; 467 $this->routeProvider = $route_provider; 468 } 469 470 /** 471 * Returns the identifier. 472 * 473 * @return string|null 474 * The entity identifier, or NULL if the object does not yet have an 475 * identifier. 476 */ 477 public function id() { 478 return $this->storage->id(); 479 } 480 481 /** 482 * Saves the view. 483 */ 484 public function save() { 485 $this->storage->save(); 486 } 487 488 /** 489 * Sets the arguments for the view. 490 * 491 * @param array $args 492 * The arguments passed to the view. 493 */ 494 public function setArguments(array $args) { 495 // The array keys of the arguments will be incorrect if set by 496 // views_embed_view() or \Drupal\views\ViewExecutable:preview(). 497 $this->args = array_values($args); 498 } 499 500 /** 501 * Expands the list of used cache contexts for the view. 502 * 503 * @param string $cache_context 504 * The additional cache context. 505 * 506 * @return $this 507 */ 508 public function addCacheContext($cache_context) { 509 $this->element['#cache']['contexts'][] = $cache_context; 510 511 return $this; 512 } 513 514 /** 515 * Sets the current page for the pager. 516 * 517 * @param int $page 518 * The current page. 519 */ 520 public function setCurrentPage($page) { 521 $this->current_page = $page; 522 523 // Calls like ::unserialize() might call this method without a proper $page. 524 // Also check whether the element is pre rendered. At that point, the cache 525 // keys cannot longer be manipulated. 526 if ($page !== NULL && empty($this->element['#pre_rendered'])) { 527 $this->element['#cache']['keys'][] = 'page:' . $page; 528 } 529 530 // If the pager is already initialized, pass it through to the pager. 531 if (!empty($this->pager)) { 532 return $this->pager->setCurrentPage($page); 533 } 534 } 535 536 /** 537 * Gets the current page from the pager. 538 * 539 * @return int 540 * The current page. 541 */ 542 public function getCurrentPage() { 543 // If the pager is already initialized, pass it through to the pager. 544 if (!empty($this->pager)) { 545 return $this->pager->getCurrentPage(); 546 } 547 548 if (isset($this->current_page)) { 549 return $this->current_page; 550 } 551 } 552 553 /** 554 * Gets the items per page from the pager. 555 * 556 * @return int 557 * The items per page. 558 */ 559 public function getItemsPerPage() { 560 // If the pager is already initialized, pass it through to the pager. 561 if (!empty($this->pager)) { 562 return $this->pager->getItemsPerPage(); 563 } 564 565 if (isset($this->items_per_page)) { 566 return $this->items_per_page; 567 } 568 } 569 570 /** 571 * Sets the items per page on the pager. 572 * 573 * @param int $items_per_page 574 * The items per page. 575 */ 576 public function setItemsPerPage($items_per_page) { 577 // Check whether the element is pre rendered. At that point, the cache keys 578 // cannot longer be manipulated. 579 if (empty($this->element['#pre_rendered'])) { 580 $this->element['#cache']['keys'][] = 'items_per_page:' . $items_per_page; 581 } 582 $this->items_per_page = $items_per_page; 583 584 // If the pager is already initialized, pass it through to the pager. 585 if (!empty($this->pager)) { 586 $this->pager->setItemsPerPage($items_per_page); 587 } 588 } 589 590 /** 591 * Gets the pager offset from the pager. 592 * 593 * @return int 594 * The pager offset. 595 */ 596 public function getOffset() { 597 // If the pager is already initialized, pass it through to the pager. 598 if (!empty($this->pager)) { 599 return $this->pager->getOffset(); 600 } 601 602 if (isset($this->offset)) { 603 return $this->offset; 604 } 605 } 606 607 /** 608 * Sets the offset on the pager. 609 * 610 * @param int $offset 611 * The pager offset. 612 */ 613 public function setOffset($offset) { 614 // Check whether the element is pre rendered. At that point, the cache keys 615 // cannot longer be manipulated. 616 if (empty($this->element['#pre_rendered'])) { 617 $this->element['#cache']['keys'][] = 'offset:' . $offset; 618 } 619 620 $this->offset = $offset; 621 622 // If the pager is already initialized, pass it through to the pager. 623 if (!empty($this->pager)) { 624 $this->pager->setOffset($offset); 625 } 626 } 627 628 /** 629 * Determines if the view uses a pager. 630 * 631 * @return bool 632 * TRUE if the view uses a pager, FALSE otherwise. 633 */ 634 public function usePager() { 635 if (!empty($this->pager)) { 636 return $this->pager->usePager(); 637 } 638 } 639 640 /** 641 * Sets whether or not AJAX should be used. 642 * 643 * If AJAX is used, paging, table sorting, and exposed filters will be fetched 644 * via an AJAX call rather than a page refresh. 645 * 646 * @param bool $ajax_enabled 647 * TRUE if AJAX should be used, FALSE otherwise. 648 */ 649 public function setAjaxEnabled($ajax_enabled) { 650 $this->ajaxEnabled = (bool) $ajax_enabled; 651 } 652 653 /** 654 * Determines whether or not AJAX should be used. 655 * 656 * @return bool 657 * TRUE if AJAX is enabled, FALSE otherwise. 658 */ 659 public function ajaxEnabled() { 660 return $this->ajaxEnabled; 661 } 662 663 /** 664 * Sets the exposed filters input to an array. 665 * 666 * @param string[] $filters 667 * The values taken from the view's exposed filters and sorts. 668 */ 669 public function setExposedInput($filters) { 670 $this->exposed_input = $filters; 671 } 672 673 /** 674 * Figures out what the exposed input for this view is. 675 * 676 * They will be taken from \Drupal::request()->query or from 677 * something previously set on the view. 678 * 679 * @return string[] 680 * An array containing the exposed input values keyed by the filter and sort 681 * name. 682 * 683 * @see self::setExposedInput() 684 */ 685 public function getExposedInput() { 686 // Fill our input either from \Drupal::request()->query or from something 687 // previously set on the view. 688 if (empty($this->exposed_input)) { 689 // Ensure that we can call the method at any point in time. 690 $this->initDisplay(); 691 692 $this->exposed_input = \Drupal::request()->query->all(); 693 // unset items that are definitely not our input: 694 foreach (['page', 'q'] as $key) { 695 if (isset($this->exposed_input[$key])) { 696 unset($this->exposed_input[$key]); 697 } 698 } 699 700 // If we have no input at all, check for remembered input via session. 701 702 // If filters are not overridden, store the 'remember' settings on the 703 // default display. If they are, store them on this display. This way, 704 // multiple displays in the same view can share the same filters and 705 // remember settings. 706 $display_id = ($this->display_handler->isDefaulted('filters')) ? 'default' : $this->current_display; 707 708 if (empty($this->exposed_input) && !empty($_SESSION['views'][$this->storage->id()][$display_id])) { 709 $this->exposed_input = $_SESSION['views'][$this->storage->id()][$display_id]; 710 } 711 } 712 713 return $this->exposed_input; 714 } 715 716 /** 717 * Sets the display for this view and initializes the display handler. 718 * 719 * @return true 720 * Always returns TRUE. 721 */ 722 public function initDisplay() { 723 if (isset($this->current_display)) { 724 return TRUE; 725 } 726 727 // Initialize the display cache array. 728 $this->displayHandlers = new DisplayPluginCollection($this, Views::pluginManager('display')); 729 730 $this->current_display = 'default'; 731 $this->display_handler = $this->displayHandlers->get('default'); 732 733 return TRUE; 734 } 735 736 /** 737 * Gets the first display that is accessible to the user. 738 * 739 * @param array|string $displays 740 * Either a single display id or an array of display ids. 741 * 742 * @return string 743 * The first accessible display id, at least default. 744 */ 745 public function chooseDisplay($displays) { 746 if (!is_array($displays)) { 747 return $displays; 748 } 749 750 $this->initDisplay(); 751 752 foreach ($displays as $display_id) { 753 if ($this->displayHandlers->get($display_id)->access($this->user)) { 754 return $display_id; 755 } 756 } 757 758 return 'default'; 759 } 760 761 /** 762 * Gets the current display plugin. 763 * 764 * @return \Drupal\views\Plugin\views\display\DisplayPluginBase 765 * The current display plugin. 766 */ 767 public function getDisplay() { 768 if (!isset($this->display_handler)) { 769 $this->initDisplay(); 770 } 771 772 return $this->display_handler; 773 } 774 775 /** 776 * Sets the current display. 777 * 778 * @param string $display_id 779 * The ID of the display to mark as current. 780 * 781 * @return bool 782 * TRUE if the display was correctly set, FALSE otherwise. 783 */ 784 public function setDisplay($display_id = NULL) { 785 // If we have not already initialized the display, do so. 786 if (!isset($this->current_display)) { 787 // This will set the default display and instantiate the default display 788 // plugin. 789 $this->initDisplay(); 790 } 791 792 // If no display ID is passed, we either have initialized the default or 793 // already have a display set. 794 if (!isset($display_id)) { 795 return TRUE; 796 } 797 798 $display_id = $this->chooseDisplay($display_id); 799 800 // Ensure the requested display exists. 801 if (!$this->displayHandlers->has($display_id)) { 802 trigger_error(new FormattableMarkup('setDisplay() called with invalid display ID "@display".', ['@display' => $display_id]), E_USER_WARNING); 803 return FALSE; 804 } 805 806 // Reset if the display has changed. It could be called multiple times for 807 // the same display, especially in the UI. 808 if ($this->current_display != $display_id) { 809 // Set the current display. 810 $this->current_display = $display_id; 811 812 // Reset the style and row plugins. 813 $this->style_plugin = NULL; 814 $this->plugin_name = NULL; 815 $this->rowPlugin = NULL; 816 } 817 818 if ($display = $this->displayHandlers->get($display_id)) { 819 // Set a shortcut. 820 $this->display_handler = $display; 821 return TRUE; 822 } 823 824 return FALSE; 825 } 826 827 /** 828 * Creates a new display and a display handler instance for it. 829 * 830 * @param string $plugin_id 831 * (optional) The plugin type from the Views plugin annotation. Defaults to 832 * 'page'. 833 * @param string $title 834 * (optional) The title of the display. Defaults to NULL. 835 * @param string $id 836 * (optional) The ID to use, e.g., 'default', 'page_1', 'block_2'. Defaults 837 * to NULL. 838 * 839 * @return \Drupal\views\Plugin\views\display\DisplayPluginBase 840 * A new display plugin instance if executable is set, the new display ID 841 * otherwise. 842 */ 843 public function newDisplay($plugin_id = 'page', $title = NULL, $id = NULL) { 844 $this->initDisplay(); 845 846 $id = $this->storage->addDisplay($plugin_id, $title, $id); 847 $this->displayHandlers->addInstanceId($id); 848 849 $display = $this->displayHandlers->get($id); 850 $display->newDisplay(); 851 return $display; 852 } 853 854 /** 855 * Gets the current style plugin. 856 * 857 * @return \Drupal\views\Plugin\views\style\StylePluginBase 858 * The current style plugin. 859 */ 860 public function getStyle() { 861 if (!isset($this->style_plugin)) { 862 $this->initStyle(); 863 } 864 865 return $this->style_plugin; 866 } 867 868 /** 869 * Finds and initializes the style plugin. 870 * 871 * Note that arguments may have changed which style plugin we use, so 872 * check the view object first, then ask the display handler. 873 * 874 * @return bool 875 * TRUE if the style plugin was or could be initialized, FALSE otherwise. 876 */ 877 public function initStyle() { 878 if (isset($this->style_plugin)) { 879 return TRUE; 880 } 881 882 $this->style_plugin = $this->display_handler->getPlugin('style'); 883 884 if (empty($this->style_plugin)) { 885 return FALSE; 886 } 887 888 return TRUE; 889 } 890 891 /** 892 * Acquires and attaches all of the handlers. 893 */ 894 public function initHandlers() { 895 $this->initDisplay(); 896 if (empty($this->inited)) { 897 foreach ($this::getHandlerTypes() as $key => $info) { 898 $this->_initHandler($key, $info); 899 } 900 $this->inited = TRUE; 901 } 902 } 903 904 /** 905 * Gets the current pager plugin. 906 * 907 * @return \Drupal\views\Plugin\views\pager\PagerPluginBase 908 * The current pager plugin. 909 */ 910 public function getPager() { 911 if (!isset($this->pager)) { 912 $this->initPager(); 913 } 914 915 return $this->pager; 916 } 917 918 /** 919 * Initializes the pager. 920 * 921 * Like style initialization, pager initialization is held until late to allow 922 * for overrides. 923 */ 924 public function initPager() { 925 if (!isset($this->pager)) { 926 $this->pager = $this->display_handler->getPlugin('pager'); 927 928 if ($this->usePager()) { 929 $this->pager->setCurrentPage($this->current_page); 930 } 931 932 // These overrides may have been set earlier via $view->set_* 933 // functions. 934 if (isset($this->items_per_page)) { 935 $this->pager->setItemsPerPage($this->items_per_page); 936 } 937 938 if (isset($this->offset)) { 939 $this->pager->setOffset($this->offset); 940 } 941 } 942 } 943 944 /** 945 * Renders the pager, if necessary. 946 * 947 * @param string[] $exposed_input 948 * The input values from the exposed forms and sorts of the view. 949 * 950 * @return array|string 951 * The render array of the pager if it's set, blank string otherwise. 952 */ 953 public function renderPager($exposed_input) { 954 if ($this->usePager()) { 955 return $this->pager->render($exposed_input); 956 } 957 958 return ''; 959 } 960 961 /** 962 * Creates a list of base tables to be used by the view. 963 * 964 * This is used primarily for the UI. The display must be already initialized. 965 * 966 * @return array 967 * An array of base tables to be used by the view. 968 */ 969 public function getBaseTables() { 970 $base_tables = [ 971 $this->storage->get('base_table') => TRUE, 972 '#global' => TRUE, 973 ]; 974 975 foreach ($this->display_handler->getHandlers('relationship') as $handler) { 976 $base_tables[$handler->definition['base']] = TRUE; 977 } 978 return $base_tables; 979 } 980 981 /** 982 * Returns the entity type of the base table, if available. 983 * 984 * @return \Drupal\Core\Entity\EntityType|false 985 * The entity type of the base table, or FALSE if none exists. 986 */ 987 public function getBaseEntityType() { 988 if (!isset($this->baseEntityType)) { 989 $view_base_table = $this->storage->get('base_table'); 990 $views_data = $this->viewsData->get($view_base_table); 991 if (!empty($views_data['table']['entity type'])) { 992 $entity_type_id = $views_data['table']['entity type']; 993 $this->baseEntityType = \Drupal::entityTypeManager()->getDefinition($entity_type_id); 994 } 995 else { 996 $this->baseEntityType = FALSE; 997 } 998 } 999 return $this->baseEntityType; 1000 } 1001 1002 /** 1003 * Runs the preQuery() on all active handlers. 1004 */ 1005 protected function _preQuery() { 1006 foreach ($this::getHandlerTypes() as $key => $info) { 1007 $handlers = &$this->$key; 1008 $position = 0; 1009 foreach ($handlers as $id => $handler) { 1010 $handlers[$id]->position = $position; 1011 $handlers[$id]->preQuery(); 1012 $position++; 1013 } 1014 } 1015 } 1016 1017 /** 1018 * Runs the postExecute() on all active handlers. 1019 */ 1020 protected function _postExecute() { 1021 foreach ($this::getHandlerTypes() as $key => $info) { 1022 $handlers = &$this->$key; 1023 foreach ($handlers as $id => $handler) { 1024 $handlers[$id]->postExecute($this->result); 1025 } 1026 } 1027 } 1028 1029 /** 1030 * Attaches the views handler for the specific type. 1031 * 1032 * @param string $key 1033 * One of 'argument', 'field', 'sort', 'filter', 'relationship'. 1034 * @param array $info 1035 * An array of views handler types use in the view with additional 1036 * information about them. 1037 */ 1038 protected function _initHandler($key, $info) { 1039 // Load the requested items from the display onto the object. 1040 $this->$key = &$this->display_handler->getHandlers($key); 1041 1042 // This reference deals with difficult PHP indirection. 1043 $handlers = &$this->$key; 1044 1045 // Run through and test for accessibility. 1046 foreach ($handlers as $id => $handler) { 1047 if (!$handler->access($this->user)) { 1048 unset($handlers[$id]); 1049 } 1050 } 1051 } 1052 1053 /** 1054 * Builds all the arguments. 1055 * 1056 * @return bool 1057 * TRUE if the arguments were built successfully, FALSE otherwise. 1058 */ 1059 protected function _buildArguments() { 1060 // Initially, we want to build sorts and fields. This can change, though, 1061 // if we get a summary view. 1062 if (empty($this->argument)) { 1063 return TRUE; 1064 } 1065 1066 // build arguments. 1067 $position = -1; 1068 $substitutions = []; 1069 $status = TRUE; 1070 1071 // Get the title. 1072 $title = $this->display_handler->getOption('title'); 1073 1074 // Iterate through each argument and process. 1075 foreach ($this->argument as $id => $arg) { 1076 $position++; 1077 $argument = $this->argument[$id]; 1078 1079 if ($argument->broken()) { 1080 continue; 1081 } 1082 1083 $argument->setRelationship(); 1084 1085 $arg = isset($this->args[$position]) ? $this->args[$position] : NULL; 1086 $argument->position = $position; 1087 1088 if (isset($arg) || $argument->hasDefaultArgument()) { 1089 if (!isset($arg)) { 1090 $arg = $argument->getDefaultArgument(); 1091 // make sure default args get put back. 1092 if (isset($arg)) { 1093 $this->args[$position] = $arg; 1094 } 1095 // remember that this argument was computed, not passed on the URL. 1096 $argument->is_default = TRUE; 1097 } 1098 1099 // Set the argument, which ensures that the argument is valid and 1100 // possibly transforms the value. 1101 if (!$argument->setArgument($arg)) { 1102 $status = $argument->validateFail($arg); 1103 break; 1104 } 1105 1106 if ($argument->isException()) { 1107 $arg_title = $argument->exceptionTitle(); 1108 } 1109 else { 1110 $arg_title = $argument->getTitle(); 1111 $argument->query($this->display_handler->useGroupBy()); 1112 } 1113 1114 // Add this argument's substitution. 1115 $substitutions["{{ arguments.$id }}"] = $arg_title; 1116 // Since argument validator plugins can potentially transform the value, 1117 // use whatever value the argument handler now has, not the raw value. 1118 $substitutions["{{ raw_arguments.$id }}"] = strip_tags(Html::decodeEntities($argument->getValue())); 1119 1120 // Test to see if we should use this argument's title 1121 if (!empty($argument->options['title_enable']) && !empty($argument->options['title'])) { 1122 $title = $argument->options['title']; 1123 } 1124 } 1125 else { 1126 // determine default condition and handle. 1127 $status = $argument->defaultAction(); 1128 break; 1129 } 1130 1131 // Be safe with references and loops: 1132 unset($argument); 1133 } 1134 1135 // set the title in the build info. 1136 if (!empty($title)) { 1137 $this->build_info['title'] = $title; 1138 } 1139 1140 // Store the arguments for later use. 1141 $this->build_info['substitutions'] = $substitutions; 1142 1143 return $status; 1144 } 1145 1146 /** 1147 * Gets the current query plugin. 1148 * 1149 * @return \Drupal\views\Plugin\views\query\QueryPluginBase 1150 * The current query plugin. 1151 */ 1152 public function getQuery() { 1153 if (!isset($this->query)) { 1154 $this->initQuery(); 1155 } 1156 1157 return $this->query; 1158 } 1159 1160 /** 1161 * Initializes the query object for the view. 1162 * 1163 * @return true 1164 * Always returns TRUE. 1165 */ 1166 public function initQuery() { 1167 if (!empty($this->query)) { 1168 $class = get_class($this->query); 1169 if ($class && $class != 'stdClass') { 1170 // return if query is already initialized. 1171 return TRUE; 1172 } 1173 } 1174 1175 // Create and initialize the query object. 1176 $views_data = Views::viewsData()->get($this->storage->get('base_table')); 1177 $this->storage->set('base_field', !empty($views_data['table']['base']['field']) ? $views_data['table']['base']['field'] : ''); 1178 if (!empty($views_data['table']['base']['database'])) { 1179 $this->base_database = $views_data['table']['base']['database']; 1180 } 1181 1182 $this->query = $this->display_handler->getPlugin('query'); 1183 return TRUE; 1184 } 1185 1186 /** 1187 * Builds the query for the view. 1188 * 1189 * @param string $display_id 1190 * The display ID of the view. 1191 * 1192 * @return bool|null 1193 * TRUE if the view build process was successful, FALSE if setting the 1194 * display fails or NULL if the view has been built already. 1195 */ 1196 public function build($display_id = NULL) { 1197 if (!empty($this->built)) { 1198 return; 1199 } 1200 1201 if (empty($this->current_display) || $display_id) { 1202 if (!$this->setDisplay($display_id)) { 1203 return FALSE; 1204 } 1205 } 1206 1207 // Let modules modify the view just prior to building it. 1208 $module_handler = \Drupal::moduleHandler(); 1209 $module_handler->invokeAll('views_pre_build', [$this]); 1210 1211 // Attempt to load from cache. 1212 // @todo Load a build_info from cache. 1213 1214 $start = microtime(TRUE); 1215 // If that fails, let's build! 1216 $this->build_info = [ 1217 'query' => '', 1218 'count_query' => '', 1219 'query_args' => [], 1220 ]; 1221 1222 $this->initQuery(); 1223 1224 // Call a module hook and see if it wants to present us with a 1225 // pre-built query or instruct us not to build the query for 1226 // some reason. 1227 // @todo: Implement this. Use the same mechanism Panels uses. 1228 1229 // Run through our handlers and ensure they have necessary information. 1230 $this->initHandlers(); 1231 1232 // Let the handlers interact with each other if they really want. 1233 $this->_preQuery(); 1234 1235 if ($this->display_handler->usesExposed()) { 1236 /** @var \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginInterface $exposed_form */ 1237 $exposed_form = $this->display_handler->getPlugin('exposed_form'); 1238 $this->exposed_widgets = $exposed_form->renderExposedForm(); 1239 if (!empty($this->build_info['abort'])) { 1240 $this->built = TRUE; 1241 // Don't execute the query, $form_state, but rendering will still be executed to display the empty text. 1242 $this->executed = TRUE; 1243 return empty($this->build_info['fail']); 1244 } 1245 } 1246 1247 // Build all the relationships first thing. 1248 $this->_build('relationship'); 1249 1250 // Set the filtering groups. 1251 if (!empty($this->filter)) { 1252 $filter_groups = $this->display_handler->getOption('filter_groups'); 1253 if ($filter_groups) { 1254 $this->query->setGroupOperator($filter_groups['operator']); 1255 foreach ($filter_groups['groups'] as $id => $operator) { 1256 $this->query->setWhereGroup($operator, $id); 1257 } 1258 } 1259 } 1260 1261 // Build all the filters. 1262 $this->_build('filter'); 1263 1264 $this->build_sort = TRUE; 1265 1266 // Arguments can, in fact, cause this whole thing to abort. 1267 if (!$this->_buildArguments()) { 1268 $this->build_time = microtime(TRUE) - $start; 1269 $this->attachDisplays(); 1270 return $this->built; 1271 } 1272 1273 // Initialize the style; arguments may have changed which style we use, 1274 // so waiting as long as possible is important. But we need to know 1275 // about the style when we go to build fields. 1276 if (!$this->initStyle()) { 1277 $this->build_info['fail'] = TRUE; 1278 return FALSE; 1279 } 1280 1281 if ($this->style_plugin->usesFields()) { 1282 $this->_build('field'); 1283 } 1284 1285 // Build our sort criteria if we were instructed to do so. 1286 if (!empty($this->build_sort)) { 1287 // Allow the style handler to deal with sorting. 1288 if ($this->style_plugin->buildSort()) { 1289 $this->_build('sort'); 1290 } 1291 // allow the plugin to build second sorts as well. 1292 $this->style_plugin->buildSortPost(); 1293 } 1294 1295 // Allow area handlers to affect the query. 1296 $this->_build('header'); 1297 $this->_build('footer'); 1298 $this->_build('empty'); 1299 1300 // Allow display handler to affect the query: 1301 $this->display_handler->query($this->display_handler->useGroupBy()); 1302 1303 // Allow style handler to affect the query: 1304 $this->style_plugin->query($this->display_handler->useGroupBy()); 1305 1306 // Allow exposed form to affect the query: 1307 if (isset($exposed_form)) { 1308 $exposed_form->query(); 1309 } 1310 1311 if (\Drupal::config('views.settings')->get('sql_signature')) { 1312 $this->query->addSignature($this); 1313 } 1314 1315 // Let modules modify the query just prior to finalizing it. 1316 $this->query->alter($this); 1317 1318 // Only build the query if we weren't interrupted. 1319 if (empty($this->built)) { 1320 // Build the necessary info to execute the query. 1321 $this->query->build($this); 1322 } 1323 1324 $this->built = TRUE; 1325 $this->build_time = microtime(TRUE) - $start; 1326 1327 // Attach displays 1328 $this->attachDisplays(); 1329 1330 // Let modules modify the view just after building it. 1331 $module_handler->invokeAll('views_post_build', [$this]); 1332 1333 return TRUE; 1334 } 1335 1336 /** 1337 * Builds an individual set of handlers. 1338 * 1339 * This is an internal method. 1340 * 1341 * @todo Some filter needs this function, even it is internal. 1342 * 1343 * @param string $key 1344 * The type of handlers (filter etc.) which should be iterated over to build 1345 * the relationship and query information. 1346 */ 1347 public function _build($key) { 1348 $handlers = &$this->$key; 1349 foreach ($handlers as $id => $data) { 1350 1351 if (!empty($handlers[$id]) && is_object($handlers[$id])) { 1352 $multiple_exposed_input = [0 => NULL]; 1353 if ($handlers[$id]->multipleExposedInput()) { 1354 $multiple_exposed_input = $handlers[$id]->groupMultipleExposedInput($this->exposed_data); 1355 } 1356 foreach ($multiple_exposed_input as $group_id) { 1357 // Give this handler access to the exposed filter input. 1358 if (!empty($this->exposed_data)) { 1359 if ($handlers[$id]->isAGroup()) { 1360 $converted = $handlers[$id]->convertExposedInput($this->exposed_data, $group_id); 1361 $handlers[$id]->storeGroupInput($this->exposed_data, $converted); 1362 if (!$converted) { 1363 continue; 1364 } 1365 } 1366 $rc = $handlers[$id]->acceptExposedInput($this->exposed_data); 1367 $handlers[$id]->storeExposedInput($this->exposed_data, $rc); 1368 if (!$rc) { 1369 continue; 1370 } 1371 } 1372 $handlers[$id]->setRelationship(); 1373 $handlers[$id]->query($this->display_handler->useGroupBy()); 1374 } 1375 } 1376 } 1377 } 1378 1379 /** 1380 * Executes the view's query. 1381 * 1382 * @param string $display_id 1383 * The machine name of the display, which should be executed. 1384 * 1385 * @return bool 1386 * TRUE if the view execution was successful, FALSE otherwise. For example, 1387 * an argument could stop the process. 1388 */ 1389 public function execute($display_id = NULL) { 1390 if (empty($this->built)) { 1391 if (!$this->build($display_id)) { 1392 return FALSE; 1393 } 1394 } 1395 1396 if (!empty($this->executed)) { 1397 return TRUE; 1398 } 1399 1400 // Don't allow to use deactivated displays, but display them on the live preview. 1401 if (!$this->display_handler->isEnabled() && empty($this->live_preview)) { 1402 $this->build_info['fail'] = TRUE; 1403 return FALSE; 1404 } 1405 1406 // Let modules modify the view just prior to executing it. 1407 $module_handler = \Drupal::moduleHandler(); 1408 $module_handler->invokeAll('views_pre_execute', [$this]); 1409 1410 // Check for already-cached results. 1411 /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */ 1412 if (!empty($this->live_preview)) { 1413 $cache = Views::pluginManager('cache')->createInstance('none'); 1414 } 1415 else { 1416 $cache = $this->display_handler->getPlugin('cache'); 1417 } 1418 1419 if ($cache->cacheGet('results')) { 1420 if ($this->usePager()) { 1421 $this->pager->total_items = $this->total_rows; 1422 $this->pager->updatePageInfo(); 1423 } 1424 } 1425 else { 1426 $this->query->execute($this); 1427 // Enforce the array key rule as documented in 1428 // views_plugin_query::execute(). 1429 $this->result = array_values($this->result); 1430 $this->_postExecute(); 1431 $cache->cacheSet('results'); 1432 } 1433 1434 // Let modules modify the view just after executing it. 1435 $module_handler->invokeAll('views_post_execute', [$this]); 1436 1437 return $this->executed = TRUE; 1438 } 1439 1440 /** 1441 * Renders this view for a certain display. 1442 * 1443 * Note: You should better use just the preview function if you want to 1444 * render a view. 1445 * 1446 * @param string $display_id 1447 * The machine name of the display, which should be rendered. 1448 * 1449 * @return array|null 1450 * A renderable array containing the view output or NULL if the build 1451 * process failed. 1452 */ 1453 public function render($display_id = NULL) { 1454 $this->execute($display_id); 1455 1456 // Check to see if the build failed. 1457 if (!empty($this->build_info['fail'])) { 1458 return; 1459 } 1460 if (!empty($this->build_info['denied'])) { 1461 return; 1462 } 1463 1464 /** @var \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginInterface $exposed_form */ 1465 $exposed_form = $this->display_handler->getPlugin('exposed_form'); 1466 $exposed_form->preRender($this->result); 1467 1468 $module_handler = \Drupal::moduleHandler(); 1469 1470 // @todo In the long run, it would be great to execute a view without 1471 // the theme system at all. See https://www.drupal.org/node/2322623. 1472 $active_theme = \Drupal::theme()->getActiveTheme(); 1473 $themes = array_keys($active_theme->getBaseThemeExtensions()); 1474 $themes[] = $active_theme->getName(); 1475 1476 // Check for already-cached output. 1477 /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */ 1478 if (!empty($this->live_preview)) { 1479 $cache = Views::pluginManager('cache')->createInstance('none'); 1480 } 1481 else { 1482 $cache = $this->display_handler->getPlugin('cache'); 1483 } 1484 1485 // Run preRender for the pager as it might change the result. 1486 if (!empty($this->pager)) { 1487 $this->pager->preRender($this->result); 1488 } 1489 1490 // Initialize the style plugin. 1491 $this->initStyle(); 1492 1493 if (!isset($this->response)) { 1494 // Set the response so other parts can alter it. 1495 $this->response = new Response('', 200); 1496 } 1497 1498 // Give field handlers the opportunity to perform additional queries 1499 // using the entire resultset prior to rendering. 1500 if ($this->style_plugin->usesFields()) { 1501 foreach ($this->field as $id => $handler) { 1502 if (!empty($this->field[$id])) { 1503 $this->field[$id]->preRender($this->result); 1504 } 1505 } 1506 } 1507 1508 $this->style_plugin->preRender($this->result); 1509 1510 // Let each area handler have access to the result set. 1511 $areas = ['header', 'footer']; 1512 // Only call preRender() on the empty handlers if the result is empty. 1513 if (empty($this->result)) { 1514 $areas[] = 'empty'; 1515 } 1516 foreach ($areas as $area) { 1517 foreach ($this->{$area} as $handler) { 1518 $handler->preRender($this->result); 1519 } 1520 } 1521 1522 // Let modules modify the view just prior to rendering it. 1523 $module_handler->invokeAll('views_pre_render', [$this]); 1524 1525 // Let the themes play too, because prerender is a very themey thing. 1526 foreach ($themes as $theme_name) { 1527 $function = $theme_name . '_views_pre_render'; 1528 if (function_exists($function)) { 1529 $function($this); 1530 } 1531 } 1532 1533 $this->display_handler->output = $this->display_handler->render(); 1534 1535 $exposed_form->postRender($this->display_handler->output); 1536 1537 $cache->postRender($this->display_handler->output); 1538 1539 // Let modules modify the view output after it is rendered. 1540 $module_handler->invokeAll('views_post_render', [$this, &$this->display_handler->output, $cache]); 1541 1542 // Let the themes play too, because post render is a very themey thing. 1543 foreach ($themes as $theme_name) { 1544 $function = $theme_name . '_views_post_render'; 1545 if (function_exists($function)) { 1546 $function($this, $this->display_handler->output, $cache); 1547 } 1548 } 1549 1550 return $this->display_handler->output; 1551 } 1552 1553 /** 1554 * Gets the cache tags associated with the executed view. 1555 * 1556 * Note: The cache plugin controls the used tags, so you can override it, if 1557 * needed. 1558 * 1559 * @return string[] 1560 * An array of cache tags. 1561 */ 1562 public function getCacheTags() { 1563 $this->initDisplay(); 1564 /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */ 1565 $cache = $this->display_handler->getPlugin('cache'); 1566 return $cache->getCacheTags(); 1567 } 1568 1569 /** 1570 * Builds the render array outline for the given display. 1571 * 1572 * This render array has a #pre_render callback which will call 1573 * ::executeDisplay in order to actually execute the view and then build the 1574 * final render array structure. 1575 * 1576 * @param string $display_id 1577 * The display ID. 1578 * @param array $args 1579 * An array of arguments passed along to the view. 1580 * @param bool $cache 1581 * (optional) Should the result be render cached. 1582 * 1583 * @return array|null 1584 * A renderable array with #type 'view' or NULL if the display ID was 1585 * invalid. 1586 */ 1587 public function buildRenderable($display_id = NULL, $args = [], $cache = TRUE) { 1588 // @todo Extract that into a generic method. 1589 if (empty($this->current_display) || $this->current_display != $this->chooseDisplay($display_id)) { 1590 if (!$this->setDisplay($display_id)) { 1591 return NULL; 1592 } 1593 } 1594 1595 return $this->display_handler->buildRenderable($args, $cache); 1596 } 1597 1598 /** 1599 * Executes the given display, with the given arguments. 1600 * 1601 * To be called externally by whatever mechanism invokes the view, 1602 * such as a page callback, hook_block, etc. 1603 * 1604 * This function should NOT be used by anything external as this 1605 * returns data in the format specified by the display. It can also 1606 * have other side effects that are only intended for the 'proper' 1607 * use of the display, such as setting page titles. 1608 * 1609 * If you simply want to view the display, use View::preview() instead. 1610 * 1611 * @param string $display_id 1612 * The display ID of the view to be executed. 1613 * @param string[] $args 1614 * The arguments to be passed to the view. 1615 * 1616 * @return array|null 1617 * A renderable array containing the view output or NULL if the display ID 1618 * of the view to be executed doesn't exist. 1619 */ 1620 public function executeDisplay($display_id = NULL, $args = []) { 1621 if (empty($this->current_display) || $this->current_display != $this->chooseDisplay($display_id)) { 1622 if (!$this->setDisplay($display_id)) { 1623 return NULL; 1624 } 1625 } 1626 1627 $this->preExecute($args); 1628 1629 // Execute the view 1630 $output = $this->display_handler->execute(); 1631 1632 $this->postExecute(); 1633 return $output; 1634 } 1635 1636 /** 1637 * Previews the given display, with the given arguments. 1638 * 1639 * To be called externally, probably by an AJAX handler of some flavor. 1640 * Can also be called when views are embedded, as this guarantees 1641 * normalized output. 1642 * 1643 * This function does not do any access checks on the view. It is the 1644 * responsibility of the caller to check $view->access() or implement other 1645 * access logic. To render the view normally with access checks, use 1646 * views_embed_view() instead. 1647 * 1648 * @return array|null 1649 * A renderable array containing the view output or NULL if the display ID 1650 * of the view to be executed doesn't exist. 1651 */ 1652 public function preview($display_id = NULL, $args = []) { 1653 if (empty($this->current_display) || ((!empty($display_id)) && $this->current_display != $display_id)) { 1654 if (!$this->setDisplay($display_id)) { 1655 return FALSE; 1656 } 1657 } 1658 1659 $this->preview = TRUE; 1660 $this->preExecute($args); 1661 // Preview the view. 1662 $output = $this->display_handler->preview(); 1663 1664 $this->postExecute(); 1665 return $output; 1666 } 1667 1668 /** 1669 * Runs attachments and lets the display do what it needs to before running. 1670 * 1671 * @param array $args 1672 * An array of arguments from the URL that can be used by the view. 1673 */ 1674 public function preExecute($args = []) { 1675 $this->old_view[] = views_get_current_view(); 1676 views_set_current_view($this); 1677 $display_id = $this->current_display; 1678 1679 // Prepare the view with the information we have, but only if we were 1680 // passed arguments, as they may have been set previously. 1681 if ($args) { 1682 $this->setArguments($args); 1683 } 1684 1685 // Let modules modify the view just prior to executing it. 1686 \Drupal::moduleHandler()->invokeAll('views_pre_view', [$this, $display_id, &$this->args]); 1687 1688 // Allow hook_views_pre_view() to set the dom_id, then ensure it is set. 1689 $this->dom_id = !empty($this->dom_id) ? $this->dom_id : hash('sha256', $this->storage->id() . REQUEST_TIME . mt_rand()); 1690 1691 // Allow the display handler to set up for execution 1692 $this->display_handler->preExecute(); 1693 } 1694 1695 /** 1696 * Unsets the current view, mostly. 1697 */ 1698 public function postExecute() { 1699 // unset current view so we can be properly destructed later on. 1700 // Return the previous value in case we're an attachment. 1701 1702 if ($this->old_view) { 1703 $old_view = array_pop($this->old_view); 1704 } 1705 1706 views_set_current_view(isset($old_view) ? $old_view : FALSE); 1707 } 1708 1709 /** 1710 * Runs attachment displays for the view. 1711 */ 1712 public function attachDisplays() { 1713 if (!empty($this->is_attachment)) { 1714 return; 1715 } 1716 1717 if (!$this->display_handler->acceptAttachments()) { 1718 return; 1719 } 1720 1721 $this->is_attachment = TRUE; 1722 // Find out which other displays attach to the current one. 1723 foreach ($this->display_handler->getAttachedDisplays() as $id) { 1724 $display_handler = $this->displayHandlers->get($id); 1725 // Only attach enabled attachments that the user has access to. 1726 if ($display_handler->isEnabled() && $display_handler->access()) { 1727 $cloned_view = Views::executableFactory()->get($this->storage); 1728 $display_handler->attachTo($cloned_view, $this->current_display, $this->element); 1729 } 1730 } 1731 $this->is_attachment = FALSE; 1732 } 1733 1734 /** 1735 * Determines if the given user has access to the view. 1736 * 1737 * Note that this sets the display handler if it hasn't been set. 1738 * 1739 * @param string $displays 1740 * The machine name of the display. 1741 * @param \Drupal\Core\Session\AccountInterface $account 1742 * The user object. 1743 * 1744 * @return bool 1745 * TRUE if the user has access to the view, FALSE otherwise. 1746 */ 1747 public function access($displays = NULL, $account = NULL) { 1748 // No one should have access to disabled views. 1749 if (!$this->storage->status()) { 1750 return FALSE; 1751 } 1752 1753 if (!isset($this->current_display)) { 1754 $this->initDisplay(); 1755 } 1756 1757 if (!$account) { 1758 $account = $this->user; 1759 } 1760 1761 // We can't use choose_display() here because that function 1762 // calls this one. 1763 $displays = (array) $displays; 1764 foreach ($displays as $display_id) { 1765 if ($this->displayHandlers->has($display_id)) { 1766 if (($display = $this->displayHandlers->get($display_id)) && $display->access($account)) { 1767 return TRUE; 1768 } 1769 } 1770 } 1771 1772 return FALSE; 1773 } 1774 1775 /** 1776 * Sets the used response object of the view. 1777 * 1778 * @param \Symfony\Component\HttpFoundation\Response $response 1779 * The response object which should be set. 1780 */ 1781 public function setResponse(Response $response) { 1782 $this->response = $response; 1783 } 1784 1785 /** 1786 * Gets the response object used by the view. 1787 * 1788 * @return \Symfony\Component\HttpFoundation\Response 1789 * The response object of the view. 1790 */ 1791 public function getResponse() { 1792 if (!isset($this->response)) { 1793 $this->response = new Response(); 1794 } 1795 return $this->response; 1796 } 1797 1798 /** 1799 * Sets the request object. 1800 * 1801 * @param \Symfony\Component\HttpFoundation\Request $request 1802 * The request object. 1803 */ 1804 public function setRequest(Request $request) { 1805 $this->request = $request; 1806 } 1807 1808 /** 1809 * Gets the request object. 1810 * 1811 * @return \Symfony\Component\HttpFoundation\Request 1812 * The request object. 1813 */ 1814 public function getRequest() { 1815 return $this->request; 1816 } 1817 1818 /** 1819 * Gets the view's current title. 1820 * 1821 * This can change depending upon how it was built. 1822 * 1823 * @return string|false 1824 * The view title, FALSE if the display is not set. 1825 */ 1826 public function getTitle() { 1827 if (empty($this->display_handler)) { 1828 if (!$this->setDisplay('default')) { 1829 return FALSE; 1830 } 1831 } 1832 1833 // During building, we might find a title override. If so, use it. 1834 if (!empty($this->build_info['title'])) { 1835 $title = $this->build_info['title']; 1836 } 1837 else { 1838 $title = $this->display_handler->getOption('title'); 1839 } 1840 1841 // Allow substitutions from the first row. 1842 if ($this->initStyle()) { 1843 $title = $this->style_plugin->tokenizeValue($title, 0); 1844 } 1845 return $title; 1846 } 1847 1848 /** 1849 * Overrides the view's current title. 1850 * 1851 * The tokens in the title gets replaced before rendering. 1852 * 1853 * @return true 1854 * Always returns TRUE. 1855 */ 1856 public function setTitle($title) { 1857 $this->build_info['title'] = $title; 1858 return TRUE; 1859 } 1860 1861 /** 1862 * Forces the view to build a title. 1863 */ 1864 public function buildTitle() { 1865 $this->initDisplay(); 1866 1867 if (empty($this->built)) { 1868 $this->initQuery(); 1869 } 1870 1871 $this->initHandlers(); 1872 1873 $this->_buildArguments(); 1874 } 1875 1876 /** 1877 * Determines whether you can link to the view or a particular display. 1878 * 1879 * Some displays (e.g. block displays) do not have their own route, but may 1880 * optionally provide a link to another display that does have a route. 1881 * 1882 * @param array $args 1883 * (optional) The arguments. 1884 * @param string $display_id 1885 * (optional) The display ID. The current display will be used by default. 1886 * 1887 * @return bool 1888 * TRUE if the current display has a valid route available, FALSE otherwise. 1889 */ 1890 public function hasUrl($args = NULL, $display_id = NULL) { 1891 if (!empty($this->override_url)) { 1892 return TRUE; 1893 } 1894 1895 // If the display has a valid route available (either its own or for a 1896 // linked display), then we can provide a URL for it. 1897 $display_handler = $this->displayHandlers->get($display_id ?: $this->current_display)->getRoutedDisplay(); 1898 if (!$display_handler instanceof DisplayRouterInterface) { 1899 return FALSE; 1900 } 1901 1902 // Look up the route name to make sure it exists. The name may exist, but 1903 // not be available yet in some instances when editing a view and doing 1904 // a live preview. 1905 $provider = \Drupal::service('router.route_provider'); 1906 try { 1907 $provider->getRouteByName($display_handler->getRouteName()); 1908 } 1909 catch (RouteNotFoundException $e) { 1910 return FALSE; 1911 } 1912 1913 return TRUE; 1914 } 1915 1916 /** 1917 * Gets the URL for the current view. 1918 * 1919 * This URL will be adjusted for arguments. 1920 * 1921 * @param array $args 1922 * (optional) Passed in arguments. 1923 * @param string $display_id 1924 * (optional) Specify the display ID to link to, fallback to the current ID. 1925 * 1926 * @return \Drupal\Core\Url 1927 * The URL of the current view. 1928 * 1929 * @throws \InvalidArgumentException 1930 * Thrown when the current view doesn't have a route available. 1931 */ 1932 public function getUrl($args = NULL, $display_id = NULL) { 1933 if (!empty($this->override_url)) { 1934 return $this->override_url; 1935 } 1936 1937 $display_handler = $this->displayHandlers->get($display_id ?: $this->current_display)->getRoutedDisplay(); 1938 if (!$display_handler instanceof DisplayRouterInterface) { 1939 throw new \InvalidArgumentException('You cannot create a URL to a display without routes.'); 1940 } 1941 1942 if (!isset($args)) { 1943 $args = $this->args; 1944 1945 // Exclude arguments that were computed, not passed on the URL. 1946 $position = 0; 1947 if (!empty($this->argument)) { 1948 foreach ($this->argument as $argument) { 1949 if (!empty($argument->is_default) && !empty($argument->options['default_argument_skip_url'])) { 1950 unset($args[$position]); 1951 } 1952 $position++; 1953 } 1954 } 1955 } 1956 1957 $path = $this->getPath(); 1958 1959 // Don't bother working if there's nothing to do: 1960 if (empty($path) || (empty($args) && strpos($path, '%') === FALSE)) { 1961 return $display_handler->getUrlInfo(); 1962 } 1963 1964 $argument_keys = isset($this->argument) ? array_keys($this->argument) : []; 1965 $id = current($argument_keys); 1966 1967 /** @var \Drupal\Core\Url $url */ 1968 $url = $display_handler->getUrlInfo(); 1969 $route = $this->routeProvider->getRouteByName($url->getRouteName()); 1970 1971 $variables = $route->compile()->getVariables(); 1972 $parameters = $url->getRouteParameters(); 1973 1974 foreach ($variables as $variable_name) { 1975 if (empty($args)) { 1976 // Try to never put % in a URL; use the wildcard instead. 1977 if ($id && !empty($this->argument[$id]->options['exception']['value'])) { 1978 $parameters[$variable_name] = $this->argument[$id]->options['exception']['value']; 1979 } 1980 else { 1981 // Provide some fallback in case no exception value could be found. 1982 $parameters[$variable_name] = '*'; 1983 } 1984 } 1985 else { 1986 $parameters[$variable_name] = array_shift($args); 1987 } 1988 1989 if ($id) { 1990 $id = next($argument_keys); 1991 } 1992 } 1993 1994 $url->setRouteParameters($parameters); 1995 return $url; 1996 } 1997 1998 /** 1999 * Gets the Url object associated with the display handler. 2000 * 2001 * @param string $display_id 2002 * (optional) The display ID (used only to detail an exception). 2003 * 2004 * @return \Drupal\Core\Url 2005 * The display handlers URL object. 2006 * 2007 * @throws \InvalidArgumentException 2008 * Thrown when the display plugin does not have a URL to return. 2009 */ 2010 public function getUrlInfo($display_id = '') { 2011 $this->initDisplay(); 2012 if (!$this->display_handler instanceof DisplayRouterInterface) { 2013 throw new \InvalidArgumentException("You cannot generate a URL for the display '$display_id'"); 2014 } 2015 return $this->display_handler->getUrlInfo(); 2016 } 2017 2018 /** 2019 * Gets the base path used for this view. 2020 * 2021 * @return string|false 2022 * The base path used for the view or FALSE if setting the display fails. 2023 */ 2024 public function getPath() { 2025 if (!empty($this->override_path)) { 2026 return $this->override_path; 2027 } 2028 2029 if (empty($this->display_handler)) { 2030 if (!$this->setDisplay('default')) { 2031 return FALSE; 2032 } 2033 } 2034 return $this->display_handler->getPath(); 2035 } 2036 2037 /** 2038 * Gets the current user. 2039 * 2040 * Views plugins can receive the current user in order to not need dependency 2041 * injection. 2042 * 2043 * @return \Drupal\Core\Session\AccountInterface 2044 * The current user. 2045 */ 2046 public function getUser() { 2047 return $this->user; 2048 } 2049 2050 /** 2051 * Creates a duplicate ViewExecutable object. 2052 * 2053 * Makes a copy of this view that has been sanitized of handlers, any runtime 2054 * data, ID, and UUID. 2055 */ 2056 public function createDuplicate() { 2057 return $this->storage->createDuplicate()->getExecutable(); 2058 } 2059 2060 /** 2061 * Unsets references so that a $view object may be properly garbage collected. 2062 */ 2063 public function destroy() { 2064 foreach ($this::getHandlerTypes() as $type => $info) { 2065 if (isset($this->$type)) { 2066 foreach ($this->{$type} as $handler) { 2067 $handler->destroy(); 2068 } 2069 } 2070 } 2071 2072 if (isset($this->style_plugin)) { 2073 $this->style_plugin->destroy(); 2074 } 2075 2076 $reflection = new \ReflectionClass($this); 2077 $defaults = $reflection->getDefaultProperties(); 2078 // The external dependencies should not be reset. This is not generated by 2079 // the execution of a view. 2080 unset( 2081 $defaults['storage'], 2082 $defaults['user'], 2083 $defaults['request'], 2084 $defaults['routeProvider'], 2085 $defaults['viewsData'] 2086 ); 2087 2088 foreach ($defaults as $property => $default) { 2089 $this->{$property} = $default; 2090 } 2091 } 2092 2093 /** 2094 * Makes sure the view is completely valid. 2095 * 2096 * @return array 2097 * An array of error strings. This will be empty if there are no validation 2098 * errors. 2099 */ 2100 public function validate() { 2101 $errors = []; 2102 2103 $this->initDisplay(); 2104 $current_display = $this->current_display; 2105 2106 foreach ($this->displayHandlers as $id => $display) { 2107 if (!empty($display)) { 2108 if (!empty($display->display['deleted'])) { 2109 continue; 2110 } 2111 2112 $result = $this->displayHandlers->get($id)->validate(); 2113 if (!empty($result) && is_array($result)) { 2114 $errors[$id] = $result; 2115 } 2116 } 2117 } 2118 2119 $this->setDisplay($current_display); 2120 2121 return $errors; 2122 } 2123 2124 /** 2125 * Provides a list of views handler types used in a view. 2126 * 2127 * This also provides some information about the views handler types. 2128 * 2129 * @return array 2130 * An array of associative arrays containing: 2131 * - title: The title of the handler type. 2132 * - ltitle: The lowercase title of the handler type. 2133 * - stitle: A singular title of the handler type. 2134 * - lstitle: A singular lowercase title of the handler type. 2135 * - plural: Plural version of the handler type. 2136 * - (optional) type: The actual internal used handler type. This key is 2137 * just used for header,footer,empty to link to the internal type: area. 2138 */ 2139 public static function getHandlerTypes() { 2140 return Views::getHandlerTypes(); 2141 } 2142 2143 /** 2144 * Returns the valid types of plugins that can be used. 2145 * 2146 * @return array 2147 * An array of plugin type strings. 2148 */ 2149 public static function getPluginTypes($type = NULL) { 2150 return Views::getPluginTypes($type); 2151 } 2152 2153 /** 2154 * Adds an instance of a handler to the view. 2155 * 2156 * Items may be fields, filters, sort criteria, or arguments. 2157 * 2158 * @param string $display_id 2159 * The machine name of the display. 2160 * @param string $type 2161 * The type of handler being added. 2162 * @param string $table 2163 * The name of the table this handler is from. 2164 * @param string $field 2165 * The name of the field this handler is from. 2166 * @param array $options 2167 * (optional) Extra options for this instance. Defaults to an empty array. 2168 * @param string $id 2169 * (optional) A unique ID for this handler instance. Defaults to NULL, in 2170 * which case one will be generated. 2171 * 2172 * @return string 2173 * The unique ID for this handler instance. 2174 */ 2175 public function addHandler($display_id, $type, $table, $field, $options = [], $id = NULL) { 2176 $types = $this::getHandlerTypes(); 2177 $this->setDisplay($display_id); 2178 2179 $data = $this->viewsData->get($table); 2180 $fields = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']); 2181 2182 if (empty($id)) { 2183 $id = $this->generateHandlerId($field, $fields); 2184 } 2185 2186 // If the desired type is not found, use the original value directly. 2187 $handler_type = !empty($types[$type]['type']) ? $types[$type]['type'] : $type; 2188 2189 $fields[$id] = [ 2190 'id' => $id, 2191 'table' => $table, 2192 'field' => $field, 2193 ] + $options; 2194 2195 if (isset($data['table']['entity type'])) { 2196 $fields[$id]['entity_type'] = $data['table']['entity type']; 2197 } 2198 if (isset($data[$field]['entity field'])) { 2199 $fields[$id]['entity_field'] = $data[$field]['entity field']; 2200 } 2201 2202 // Load the plugin ID if available. 2203 if (isset($data[$field][$handler_type]['id'])) { 2204 $fields[$id]['plugin_id'] = $data[$field][$handler_type]['id']; 2205 } 2206 2207 $this->displayHandlers->get($display_id)->setOption($types[$type]['plural'], $fields); 2208 2209 return $id; 2210 } 2211 2212 /** 2213 * Generates a unique ID for a handler instance. 2214 * 2215 * These handler instances are typically fields, filters, sort criteria, or 2216 * arguments. 2217 * 2218 * @param string $requested_id 2219 * The requested ID for the handler instance. 2220 * @param array $existing_items 2221 * An array of existing handler instances, keyed by their IDs. 2222 * 2223 * @return string 2224 * A unique ID. This will be equal to $requested_id if no handler instance 2225 * with that ID already exists. Otherwise, it will be appended with an 2226 * integer to make it unique, e.g., "{$requested_id}_1", 2227 * "{$requested_id}_2", etc. 2228 */ 2229 public static function generateHandlerId($requested_id, $existing_items) { 2230 $count = 0; 2231 $id = $requested_id; 2232 while (!empty($existing_items[$id])) { 2233 $id = $requested_id . '_' . ++$count; 2234 } 2235 return $id; 2236 } 2237 2238 /** 2239 * Gets an array of handler instances for the current display. 2240 * 2241 * @param string $type 2242 * The type of handlers to retrieve. 2243 * @param string $display_id 2244 * (optional) A specific display machine name to use. If NULL, the current 2245 * display will be used. 2246 * 2247 * @return array 2248 * An array of handler instances of a given type for this display. 2249 */ 2250 public function getHandlers($type, $display_id = NULL) { 2251 $old_display_id = !empty($this->current_display) ? $this->current_display : 'default'; 2252 2253 $this->setDisplay($display_id); 2254 2255 if (!isset($display_id)) { 2256 $display_id = $this->current_display; 2257 } 2258 2259 // Get info about the types so we can get the right data. 2260 $types = static::getHandlerTypes(); 2261 2262 $handlers = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']); 2263 2264 // Restore initial display id (if any) or set to 'default'. 2265 if ($display_id != $old_display_id) { 2266 $this->setDisplay($old_display_id); 2267 } 2268 return $handlers; 2269 } 2270 2271 /** 2272 * Gets the configuration of a handler instance on a given display. 2273 * 2274 * @param string $display_id 2275 * The machine name of the display. 2276 * @param string $type 2277 * The type of handler to retrieve. 2278 * @param string $id 2279 * The ID of the handler to retrieve. 2280 * 2281 * @return array|null 2282 * Either the handler instance's configuration, or NULL if the handler is 2283 * not used on the display. 2284 */ 2285 public function getHandler($display_id, $type, $id) { 2286 // Get info about the types so we can get the right data. 2287 $types = static::getHandlerTypes(); 2288 // Initialize the display 2289 $this->setDisplay($display_id); 2290 2291 // Get the existing configuration 2292 $fields = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']); 2293 2294 return isset($fields[$id]) ? $fields[$id] : NULL; 2295 } 2296 2297 /** 2298 * Sets the configuration of a handler instance on a given display. 2299 * 2300 * @param string $display_id 2301 * The machine name of the display. 2302 * @param string $type 2303 * The type of handler being set. 2304 * @param string $id 2305 * The ID of the handler being set. 2306 * @param array|null $item 2307 * An array of configuration for a handler, or NULL to remove this instance. 2308 * 2309 * @see set_item_option() 2310 */ 2311 public function setHandler($display_id, $type, $id, $item) { 2312 // Get info about the types so we can get the right data. 2313 $types = static::getHandlerTypes(); 2314 // Initialize the display. 2315 $this->setDisplay($display_id); 2316 2317 // Get the existing configuration. 2318 $fields = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']); 2319 if (isset($item)) { 2320 $fields[$id] = $item; 2321 } 2322 2323 // Store. 2324 $this->displayHandlers->get($display_id)->setOption($types[$type]['plural'], $fields); 2325 } 2326 2327 /** 2328 * Removes configuration for a handler instance on a given display. 2329 * 2330 * @param string $display_id 2331 * The machine name of the display. 2332 * @param string $type 2333 * The type of handler being removed. 2334 * @param string $id 2335 * The ID of the handler being removed. 2336 */ 2337 public function removeHandler($display_id, $type, $id) { 2338 // Get info about the types so we can get the right data. 2339 $types = static::getHandlerTypes(); 2340 // Initialize the display. 2341 $this->setDisplay($display_id); 2342 2343 // Get the existing configuration. 2344 $fields = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']); 2345 // Unset the item. 2346 unset($fields[$id]); 2347 2348 // Store. 2349 $this->displayHandlers->get($display_id)->setOption($types[$type]['plural'], $fields); 2350 } 2351 2352 /** 2353 * Sets an option on a handler instance. 2354 * 2355 * Use this only if you have just 1 or 2 options to set; if you have many, 2356 * consider getting the handler instance, adding the options and using 2357 * set_item() directly. 2358 * 2359 * @param string $display_id 2360 * The machine name of the display. 2361 * @param string $type 2362 * The type of handler being set. 2363 * @param string $id 2364 * The ID of the handler being set. 2365 * @param string $option 2366 * The configuration key for the value being set. 2367 * @param mixed $value 2368 * The value being set. 2369 * 2370 * @see set_item() 2371 */ 2372 public function setHandlerOption($display_id, $type, $id, $option, $value) { 2373 $item = $this->getHandler($display_id, $type, $id); 2374 $item[$option] = $value; 2375 $this->setHandler($display_id, $type, $id, $item); 2376 } 2377 2378 /** 2379 * Enables admin links on the rendered view. 2380 * 2381 * @param bool $show_admin_links 2382 * TRUE if the admin links should be shown. 2383 */ 2384 public function setShowAdminLinks($show_admin_links) { 2385 $this->showAdminLinks = (bool) $show_admin_links; 2386 } 2387 2388 /** 2389 * Returns whether admin links should be rendered on the view. 2390 * 2391 * @return bool 2392 * TRUE if admin links should be rendered, else FALSE. 2393 */ 2394 public function getShowAdminLinks() { 2395 if (!isset($this->showAdminLinks)) { 2396 return $this->getDisplay()->getOption('show_admin_links'); 2397 } 2398 return $this->showAdminLinks; 2399 } 2400 2401 /** 2402 * Merges all plugin default values for each display. 2403 */ 2404 public function mergeDefaults() { 2405 $this->initDisplay(); 2406 // Initialize displays and merge all plugin defaults. 2407 foreach ($this->displayHandlers as $display) { 2408 $display->mergeDefaults(); 2409 } 2410 } 2411 2412 /** 2413 * Provides a full array of possible theme functions to try for a given hook. 2414 * 2415 * @param string $hook 2416 * The hook to use. This is the base theme/template name. 2417 * 2418 * @return array 2419 * An array of theme hook suggestions. 2420 */ 2421 public function buildThemeFunctions($hook) { 2422 $themes = []; 2423 $display = isset($this->display_handler) ? $this->display_handler->display : NULL; 2424 $id = $this->storage->id(); 2425 2426 if ($display) { 2427 $themes[] = $hook . '__' . $id . '__' . $display['id']; 2428 $themes[] = $hook . '__' . $display['id']; 2429 // Add theme suggestions for each single tag. 2430 foreach (Tags::explode($this->storage->get('tag')) as $tag) { 2431 $themes[] = $hook . '__' . preg_replace('/[^a-z0-9]/', '_', strtolower($tag)); 2432 } 2433 2434 if ($display['id'] != $display['display_plugin']) { 2435 $themes[] = $hook . '__' . $id . '__' . $display['display_plugin']; 2436 $themes[] = $hook . '__' . $display['display_plugin']; 2437 } 2438 } 2439 $themes[] = $hook . '__' . $id; 2440 $themes[] = $hook; 2441 2442 return $themes; 2443 } 2444 2445 /** 2446 * Determines if this view has form elements. 2447 * 2448 * @return bool 2449 * TRUE if this view contains handlers with views form implementations, 2450 * FALSE otherwise. 2451 */ 2452 public function hasFormElements() { 2453 foreach ($this->field as $field) { 2454 if (property_exists($field, 'views_form_callback') || method_exists($field, 'viewsForm')) { 2455 return TRUE; 2456 } 2457 } 2458 $area_handlers = array_merge(array_values($this->header), array_values($this->footer)); 2459 $empty = empty($this->result); 2460 foreach ($area_handlers as $area) { 2461 if (method_exists($area, 'viewsForm') && !$area->viewsFormEmpty($empty)) { 2462 return TRUE; 2463 } 2464 } 2465 2466 return FALSE; 2467 } 2468 2469 /** 2470 * Gets dependencies for the view. 2471 * 2472 * @see \Drupal\views\Entity\View::calculateDependencies() 2473 * @see \Drupal\views\Entity\View::getDependencies() 2474 * 2475 * @return array 2476 * An array of dependencies grouped by type (module, theme, entity). 2477 */ 2478 public function getDependencies() { 2479 return $this->storage->calculateDependencies()->getDependencies(); 2480 } 2481 2482 /** 2483 * Magic method implementation to serialize the view executable. 2484 * 2485 * @return array 2486 * The names of all variables that should be serialized. 2487 */ 2488 public function __sleep() { 2489 // Limit to only the required data which is needed to properly restore the 2490 // state during unserialization. 2491 $this->serializationData = [ 2492 'storage' => $this->storage->id(), 2493 'views_data' => $this->viewsData->_serviceId, 2494 'route_provider' => $this->routeProvider->_serviceId, 2495 'current_display' => $this->current_display, 2496 'args' => $this->args, 2497 'current_page' => $this->current_page, 2498 'exposed_input' => $this->exposed_input, 2499 'exposed_raw_input' => $this->exposed_raw_input, 2500 'exposed_data' => $this->exposed_data, 2501 'dom_id' => $this->dom_id, 2502 'executed' => $this->executed, 2503 ]; 2504 return ['serializationData']; 2505 } 2506 2507 /** 2508 * Magic method implementation to unserialize the view executable. 2509 */ 2510 public function __wakeup() { 2511 // There are cases, like in testing where we don't have a container 2512 // available. 2513 if (\Drupal::hasContainer() && !empty($this->serializationData)) { 2514 // Load and reference the storage. 2515 $this->storage = \Drupal::entityTypeManager()->getStorage('view') 2516 ->load($this->serializationData['storage']); 2517 $this->storage->set('executable', $this); 2518 2519 // Attach all necessary services. 2520 $this->user = \Drupal::currentUser(); 2521 $this->viewsData = \Drupal::service($this->serializationData['views_data']); 2522 $this->routeProvider = \Drupal::service($this->serializationData['route_provider']); 2523 2524 // Restore the state of this executable. 2525 if ($request = \Drupal::request()) { 2526 $this->setRequest($request); 2527 } 2528 $this->setDisplay($this->serializationData['current_display']); 2529 $this->setArguments($this->serializationData['args']); 2530 $this->setCurrentPage($this->serializationData['current_page']); 2531 $this->setExposedInput($this->serializationData['exposed_input']); 2532 $this->exposed_data = $this->serializationData['exposed_data']; 2533 $this->exposed_raw_input = $this->serializationData['exposed_raw_input']; 2534 $this->dom_id = $this->serializationData['dom_id']; 2535 2536 $this->initHandlers(); 2537 2538 // If the display was previously executed, execute it now. 2539 if ($this->serializationData['executed']) { 2540 $this->execute($this->current_display); 2541 } 2542 } 2543 // Unset serializationData since it serves no further purpose. 2544 unset($this->serializationData); 2545 } 2546 2547} 2548