Views - How to exclude nodes marked for menu display
The client request was to build a View node listing for the home page (content type = page) but exclude nodes that were flagged to appear in the menu (any menu). I has to use hook_views_query_alter() for this and here is the full example:
/**
* Implements hook_views_query_alter().
*
* Alters the query to filter out nodes flagged to appear in a menu.
*/
function MODULE_views_query_alter($view, $query) {
// Check if this is the specific View you want to alter.
if ($view->id() === 'page_listing' && $view->current_display === 'block_1') {
// Define the join configuration.
$configuration = [
'type' => 'LEFT', // Join type.
'table' => 'menu_link_content_data', // Table to join.
'field' => 'link__uri', // Field in the joined table.
'left_table' => 'node_field_data', // Base table.
'left_field' => 'nid', // Field in the base table.
'operator' => '=', // Join operator.
'left_formula' => "CONCAT('entity:node/', node_field_data.nid)", // Custom formula for the left side.
];
// Create the join plugin instance.
$join = Views::pluginManager('join')->createInstance('standard', $configuration);
// Add the join to the query.
$query->addRelationship('mlc', $join, 'node_field_data');
// Add a condition to exclude nodes that have a menu link.
$query->addWhereExpression(
0, // Group index (use 0 for the default group).
'mlc.link__uri IS NULL' // Exclude rows with a menu link.
);
}
}