Hi everyone, I am facing a weird issue on Drupal 11. I am trying to hide a specific Views Block (let's call it block_5) and show a custom markup message instead, but only if the current logged-in user has already created a node of a specific content type (preventivo_tenute_meccaniche) that references the current page node ID (field_riferimento_richiesta_tenu).
The issue is that whatever hook I use, the view keeps rendering normally for users who already submitted the node, and it's completely hidden for users who haven't submitted anything yet (the exact opposite of what I need). Even if I invert the PHP logic (empty() vs !empty()), nothing changes at all on the screen.
When doing a diagnostic check with hook_block_view_alter(), the page only intercepts generic blocks like system_main_block or page_title_block. The views_block:richieste-block_5 string is never intercepted directly as a standalone block configuration ID, meaning the view is probably rendered inside the main content element (e.g., via Layout Builder, an Entity Reference field, or Eva/Views Field View).
Here is the custom module code I tried using:
PHP
<?php
use Drupal\Core\Block\BlockPluginInterface;
function controllo_bottoni_block_view_alter(array &$build, BlockPluginInterface $block) {
if ($block->getConfiguration()['id'] === 'system_main_block') {
$current_user_id = \Drupal::currentUser()->id();
if ($current_user_id == 0) return;
$node = \Drupal::routeMatch()->getParameter('node');
if ($node instanceof \Drupal\node\NodeInterface) {
$richiesta_nid = $node->id();
} else {
return;
}
$query = \Drupal::entityQuery('node')
->condition('type', 'preventivo_tenute_meccaniche')
->condition('field_riferimento_richiesta_tenu', $richiesta_nid)
->condition('uid', $current_user_id)
->accessCheck(FALSE)
->range(0, 1);
$ids = $query->execute();
if (!empty($ids)) {
// Logic to hide the view inside $build['#content'] or inject CSS
// Neither recursive scanning of #content nor injecting a <style> tag to hide the view class worked.
}
}
}
Every time I update the code, I clear the cache via performance admin UI, but the block behavior remains completely unaligned.
What is the correct way or hook to intercept a View display injected inside the main node rendering array to dynamically hide it based on an entity query? Thanks in advance!
Comments
Generally for situations like
Generally for situations like this, I would just create my own block plugin, that handles the logic. Make sure you add the 'user' cache context, so the block renders separately per user.
Contact me to contract me for D7 -> D10/11 migrations.
Block is partially working.
Thank you very much for the answer.
I applied your solution. The block is now correctly detected and registered by Drupal's UI (the class is parsed without issues and the service injection is correct), but I'm still facing a runtime issue regarding its dynamic behavior.
Here is what we did/tested based on your version, and the current status:
1. Modifications and tests applied compared to your version
Cache Variation (
getCacheContexts): To prevent the block from getting "frozen" on the first page the user visits, we integrated the cache contexts by adding both the user and the route (the current page). This forces Drupal to re-evaluate the logic on every URL change:PHP
Testing the View Render Array (
build): We tested the#argumentskey. Since therichiesteview (displayblock_5) already has a contextual filter configured in the Views UI to "Provide default value -> Content ID from URL", we tried both explicitly passing the argument ('#arguments' => [$node->id()]) and removing that line entirely to let Views handle it natively.2. Current situation and the issue encountered
Currently, the code runs, but the output on the page is not correct:
Case 1 (User has ALREADY submitted the quote): It works. The code enters the
elsebranch, correctly detects the existing node, and displays our custom markup message on the screen.Case 2 (Supplier has NOT yet submitted the quote): It doesn't work. When the array is empty (
empty($existing_nodes)), the entire block completely disappears from the page. The View with the button is not rendered, and even the block title vanishes from the region, leaving the layout completely blank.It seems that when the
build()method returns a view render array (#type => 'view'), the Drupal rendering pipeline or the Views contextual filter validation fails, causing Drupal to treat the block as "empty" and hide it entirely due to layout cleanup rules.Do you have any idea why a view render array behaves this way or gets emptied when returned conditionally inside another block's
build()method?Additionally, do you know if there is a way to achieve this entire conditional behavior using only the View itself, without any custom PHP code or additional modules? For example, by leveraging Views relationships, contextual filters, or "No Results Behavior" to check if the current user has already created a 'preventivo_tenute_meccaniche' node linked to the current 'richiesta' node?
Lino
Solved with Views UI
Following up on my previous post where I was struggling to pass the current Request ID to a prepopulated form when a View returns no results—I finally solved it! After days of fighting with missing row tokens and URL aliases, here is the clean, definitive solution.
Why the usual methods failed:
No Row Tokens: Since the View is empty (zero results), Drupal doesn't generate any tokens for fields.
No Contextual Filters: If your View architecture doesn't use contextual filters, Twig variables like
{{ view.args }}remain completely empty.URL Aliases: If your site uses clean URLs (e.g.,
/requests/my-test-title), a basic browser JavaScript regex looking for/node/IDfails because the actual ID is hidden.The Solution
Instead of forcing Drupal to look for server-side tokens, we can solve this entirely on the client side (in the user's browser). We can insert a small HTML + Vanilla JavaScript snippet inside a Global: Text area under the No Results Behavior of the View.
Crucial step: Make sure the Text format under the text area is set to Full HTML, otherwise Drupal will strip out the
<script>tags.The script bypasses the visible URL and directly queries Drupal’s global system object
window.drupalSettings(or falls back to the<link rel="shortlink">tag in the page HEAD). It extracts the raw numeric Node ID, cleans it, and injects it straight into the button link.The Code
Paste this directly into your View's No Results Text Area:
HTML
Why this is the best approach:
Zero Server Overhead: It doesn't trigger extra database queries or heavy token modules.
Alias-Proof: It works seamlessly whether your path is a raw system path (
/node/307) or a customized semantic alias (/quotes/industrial-seal-maintenance).Clean Data Input: By using
match(/\d+/), it strips out any trailing text or duplicates (avoiding buggy strings likenode/307,307), ensuring your entity prepopulate module receives a flawless, clean integer.Hopefully, this saves someone else from the headache I went through!