1

We have a View page that is coming from myview. Using the header setting in the view we have added @total results already, which displays in the views-view--myview--page-1.html.twig using the {{ header }} variable.

We want to be able to fetch the view results within our mymodule_preprocess_page() hook. On the View page, we added facet blocks that will return a list of checkboxes (this is from the Article's category tag).

Issue

When we modify the facet value to some value such as value1, the ksm($view->total_results) returns NULL value.

Questions:

  1. How would i fetch the results using mymodule_preprocess_page() hook?
  2. Is there a way to do it without running a query within mymodule_preprocess_page() hook?

View Setup

Display: page

Format

  • unformatted list
  • rendered entity | "my_view_mode"

Filter criteria

  • published = true
  • content type = article

Header

  • Global: result summary (global: result summary)

Other - Machine name: page_1 - Use Ajax = yes

What we tried:

function mymodule_preprocess_page($variables) {
  $view_id = \Drupal::routeMatch()->getParameter('view_id');
  $display_id = \Drupal::routeMatch()->getParameter('display_id');

  if($view_id == 'myviewId' && $display_id = 'page_1') {
    // first check we are on view page.
    $view = \Drupal\views\Views::getView($view_id);
    ksm($view->total_results); // returns null. 
  }
}

2 Answers 2

4

The easiest way would be to execute the View again in the page preprocess hook.

If you want to avoid executing the View twice the task is getting a bit more tricky. This is because in D8 page elements are not necessarily rendered downstream in the order you expect. The element could have been cached in a previous request or in case the element doesn't qualify for caching it could have automatically been placeholdered and the page has been built without it, postponing the rendering until the cached page is finally delivered.

In Drupal 8 elements often need to send information upstream despite being cached or placeholdered, like cache tags or libraries. The mechanism for this is bubbleable metadata. You can use this for other things, too. See for example Add an attribute to $variables from a Controller

In case of the views results you can try this:

/**
 * Implements hook_views_pre_render().
 */
function mymodule_views_pre_render($view) {
  if (isset($view) && ($view->id() == 'my_view')) {
    $view->element['#attached']['placeholders']['<div id="my-view-results"></div>']['#markup'] =
      '<div id="my-view-results">' . $view->total_rows . '</div>';
  }
}

Place the empty div container somewhere on the page, for example in the body field of a custom block or directly in page.html.twig:

<div id="my-view-results"></div>

AJAX

When you enable AJAX the code above only works for the first page load. To update the results when the View gets refreshed via AJAX add your own command in a response event subscriber.

Getting the executed View then is straightforward, Views AJAX implementation keeps it in the response, so that you can use it when altering the response, see ViewAjaxResponse::getView.

/src/EventSubscriber/ViewAjaxResponseSubscriber.php

<?php

namespace Drupal\mymodule\EventSubscriber;

use Drupal\views\Ajax\ViewAjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

/**
 * Response subscriber to handle View AJAX responses.
 */
class ViewAjaxResponseSubscriber implements EventSubscriberInterface {

  /**
   * Adds an AJAX command to ViewAjaxResponse.
   */
  public function onResponse(FilterResponseEvent $event) {
    $response = $event->getResponse();
    if ($response instanceof ViewAjaxResponse) {
      $view = $response->getView();
      if (isset($view) && ($view->id() == 'my_view')) {
        $response->addCommand(new HtmlCommand('#my-view-results', $view->total_rows));
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events[KernelEvents::RESPONSE][] = ['onResponse'];
    return $events;
  }

}

mymodule.services.yml

services:
  mymodule.view_ajax_response_subscriber:
    class: Drupal\mymodule\EventSubscriber\ViewAjaxResponseSubscriber
    tags:
      - { name: event_subscriber }
1
  • adding the service did the trick thank you. Commented Jun 11, 2019 at 18:28
1

Views::getView is just a shortcut to loading the entity via the usual methods - it doesn't execute the View, so you won't see any results with the object you have.

I'm not sure there's an elegant way to do what you need (although I hope I'm wrong, it seems like there would be). One simple-ish workaround would be to hook into the View's build process after its execution, stash the data you need somewhere, then pick it up in the page preprocess hook.

A service seems like as a good a place as any for the storage; that might look something like this:

mymodule/src/CurrentPageView.php

namespace Drupal\mymodule;

use Drupal\views\ViewExecutable;

class CurrentPageView {

    $view = NULL;

    public function getView() {
      return $this->view;
    }

    public function setView(ViewExecutable $view) {
      $this->view = $view;
    }

}

(with the appropriate entry in mymodule.services.yml)

Then in hook_views_post_execute() stash the View:

function mymodule_views_post_execute(\Drupal\views\ViewExecutable $view) {
  if ($view->id() == 'myviewId') && $view->current_display == 'page_1') {
    \Drupal::service('mymodule.currentpageview')->setView($view);
  }
}

And pick it up again in the preprocess:

function mymodule_preprocess_page($variables) {
  $view_id = \Drupal::routeMatch()->getParameter('view_id');
  $display_id = \Drupal::routeMatch()->getParameter('display_id');

  if($view_id == 'myviewId' && $display_id = 'page_1') {
    // first check we are on view page.
    $view = \Drupal::service('mymodule.currentpageview')->getView();
    ksm($view->total_results);
  }
}
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.