0

Views page show content. How to redirect to content page if views has only one result?

2 Answers 2

1

In your theme's themename.theme file add:

At the very top add:

use Drupal\views\ViewExecutable;
use Symfony\Component\HttpFoundation\RedirectResponse;

Then add:

/*
 * Implements hook_views_pre_render()
 */
function THEMENAME_views_pre_render(ViewExecutable $view) {
    if($view->id() == "view_name" && $view->current_display == 'view_machine_name') {
      if (!empty($view->result) && count($view->result) == 1) {
        $url = $view->result[0]->_entity->toUrl()->toString();
        $response = new RedirectResponse($url);
        $response->send();
      }
    }
}

Clear the cache so changes kick in.


my_module/src/Controller/ViewPageController.php

namespace Drupal\my_module\Controller;

use Drupal\Core\Cache\CacheableRedirectResponse;
use Drupal\Core\Controller\ControllerBase;
use Drupal\views\Views;

/**
 * Defines HelloController class.
 */
class ViewPageController extends ControllerBase {

  public function content() {

    $view = Views::getView('view_name');
    $view->setDisplay('view_machine_name');
    $view->execute();

    if (!empty($view->result) && count($view->result) == 1) {

      $url = $view->result[0]->_entity->toUrl()->toString(TRUE);
      $response = new CacheableRedirectResponse($url->getGeneratedUrl());
      $response->getCacheableMetadata()->addCacheTags($view->getCacheTags());
      $response->addCacheableDependency($url);
      return $response;

    }
    else {

      return [
        '#type' => 'view',
        '#name' => 'view_name',
        '#view' => $view,
        '#display_id' => 'view_machine_name',
        '#embed' => TRUE,
        '#cache' => $view->getCacheTags(),
      ];

    }

  }

}

This is the view_machine_name.

17
  • 1
    You need to clear the cache every time ;-). It's never a good idea to send a response instead of returning it. But for this use case when a redirect depends on database content I think a custom controller which either returns the view or a cacheable redirect response containing the correct cache metadata would be the best approach.
    – 4uk4
    May 16, 2019 at 8:57
  • @4k4 You need to clear the cache every time Doesn't the view get invalidated via cache tag? Or are you referring to when adding a new hook?
    – No Sssweat
    May 16, 2019 at 9:06
  • 1
    Yes, but the redirect needs to get invalidated, too, when the view result is no longer 1. The problem with sending is you have to responses, one you've sent and one which is getting cached, which makes this quite unpredictable what happens.
    – 4uk4
    May 16, 2019 at 9:11
  • @4k4 you're right, I could've sworn it worked for me without having to clear the cache, but now it won't work unless I clear the cache. Oh I see now, I prob got a false positive due to having multiple view blocks on the page. I didn't do a view_name conditional.
    – No Sssweat
    May 16, 2019 at 9:32
  • 1
    No, $response->addCacheableDependency($url); doesn't work when this is already applied.
    – 4uk4
    May 16, 2019 at 12:53
0

There's a LOT better way. Use hook_views_post_render() instead. See my comment here.

and the documentation at views.api.php

/**
 * Post-process any render data.
 *
 * The module or theme may add, modify or remove elements in $output after
 * rendering.
 *
 * If a module wishes to act on the rendered HTML of the view rather than the
 * structured content array, it may use this hook to add a #post_render
 * callback:
 * @code
 * // The object must implement \Drupal\Core\Security\TrustedCallbackInterface.
 * $output['#post_render'][] = '\Drupal\my_module\View::postRender';
 * @endcode
 *
 * See \Drupal\Core\Render\RendererInterface::render() for #post_render
 * documentation.
 *
 * Alternatively, it could also implement hook_preprocess_HOOK() for
 * the particular view template, if there is one.
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The view object being processed.
 * @param array $output
 *   A structured content array representing the view output. The given array
 *   depends on the style plugin and can be either a render array or an array of
 *   render arrays.
 * @param \Drupal\views\Plugin\views\cache\CachePluginBase $cache
 *   The cache settings.
 *
 * @see \Drupal\views\ViewExecutable
 */
function hook_views_post_render(ViewExecutable $view, array &$output, CachePluginBase $cache) {

  // When using full pager, disable any time-based caching if there are fewer
  // than 10 results.
  if ($view->pager instanceof Drupal\views\Plugin\views\pager\Full && $cache instanceof Drupal\views\Plugin\views\cache\Time && count($view->result) options['results_lifespan'] = 0;
    $cache->options['output_lifespan'] = 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.