By usernameabc


2019-06-07 16:23:42 8 Comments

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 comments

@4k4 2019-06-09 09:46:14

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 this only works for the first page load. To update the results when the View gets refreshed you need to add an AJAX command in a response event subscriber. Getting the executed View then is straightforward, it is stored in the AJAX response: 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 }

@usernameabc 2019-06-11 18:28:14

adding the service did the trick thank you.

@Clive 2019-06-07 18:26:29

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);
  }
}

Related Questions

Sponsored Content

0 Answered Questions

Description metatag from first row of the view

  • 2019-03-27 18:50:15
  • menelai
  • 45 View
  • 0 Score
  • 0 Answer
  • Tags:   views 8 meta-tags

3 Answered Questions

[SOLVED] Programmatically add a footer to a view

  • 2016-06-29 06:09:44
  • Malabya Tewari
  • 2555 View
  • 2 Score
  • 3 Answer
  • Tags:   8 views

1 Answered Questions

[SOLVED] How to get a 'result summary' of the rows effectively showed?

  • 2018-03-23 10:07:20
  • Katia
  • 54 View
  • -1 Score
  • 1 Answer
  • Tags:   views

4 Answered Questions

[SOLVED] Alter a view to show the first N results

  • 2016-09-17 04:41:20
  • user2137454
  • 4975 View
  • 0 Score
  • 4 Answer
  • Tags:   8 hooks views

1 Answered Questions

How do I obtain the result summary from within a form on the view page?

  • 2017-02-03 15:56:34
  • Kevin
  • 99 View
  • 0 Score
  • 1 Answer
  • Tags:   views 8 forms

1 Answered Questions

[SOLVED] How to add a result count in exposed filters?

  • 2016-11-07 18:18:53
  • oknate
  • 396 View
  • 1 Score
  • 1 Answer
  • Tags:   8 views

2 Answered Questions

[SOLVED] How to get total number of results in Search API View?

  • 2014-10-07 15:29:59
  • kufeiko
  • 1686 View
  • 1 Score
  • 2 Answer
  • Tags:   7 search views

2 Answered Questions

[SOLVED] Views Exposed form in block : how to add a ajax results counter?

  • 2016-04-15 10:20:56
  • Kojo
  • 889 View
  • 4 Score
  • 2 Answer
  • Tags:   ajax views blocks

0 Answered Questions

Serve 404 page on view with contextual filter?

1 Answered Questions

[SOLVED] Change layout of views page based on taxonomy term field

Sponsored Content