In Drupal 8, I implemented a hook_node_access function in my module to control access for a certain role under certain conditions and it works fine.

However, all of that content type are still shown on a view when the user views it. This is expected behavior according to the documentation:

In node listings (lists of nodes generated from a select query, such as the default home page at path 'node', an RSS feed, a recent content block, etc.), ... hook_node_access() is not called on each node for performance reasons and for proper functioning of the pager system. When adding a node listing to your module, be sure to use an entity query, which will add a tag of "node_access". This will allow modules dealing with node access to ensure only nodes to which the user has access are retrieved, through the use of hook_query_TAG_alter(). See the Entity API topic for more information on entity queries.


I understand why the view does not check each individual content, but I do not understand what this documentation is asking me to do to fix that. The documentation for hook_query_TAG_alter and on entity queries is not very helpful either.

How do I ensure that only items of this content type that the current user has view access to are shown in a view?


2 Answers 2


having run into the same problem as you I hope I can save you a headache. Depending on the amount of results you have I can provide 2 ways to tackle this:

1. Works when you don't need pagination

HOOK_views_post_execute. This will have the rows in the variable. In here you can check the access and unset() the rows not allowed.

2. Works when you need pagination

HOOK_views_query_alter. I've used this solution for a view I needed myself with around 10000 results. I do a custom permissions check based on the user and the groups they are part of. Baed on this I generate a list of ID's of nodes they user is allowed to see. I add these ID's as an "IN" query to the view:

$cond["field"] = "media_field_data.mid";
$cond["operator"] = "IN";
$query->addWhere('1', $cond["field"], $mids, $cond["operator"]);

Where $mids has all the allowed Mids in there. Hope this helps you get along!


I had the same issue when using a custom hook_ENTITY_TYPE_access

... just putting an example here to save people time:

 * Implements hook_views_post_execute().
function MODULE_views_post_execute(ViewExecutable $view) {
  if ($view->id() == 'view_name') {
   $result = $view->result;
   $account = $view->getUser();
   $user = \Drupal::entityTypeManager()->getStorage('user')->load($account->id());
   $filter_results = [];
   foreach ($result as $key => $value) {
     $entity = $value->_entity;
       /// opp can be create, view, delete, update. 
       if ($entity->access('view', $user) == TRUE) {
        $filter_results[$key] = $value;
   $view->result = $filter_results;

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.