3

In Drupal 9, I have a View called News.

The View is displayed at the following path:

/news

I can edit it at the following path:

/admin/structure/views/view/news/edit

It has the following Contextual filter:

Content datasource: Content type

The contextual filter values are provided by the URL.

When the filter value is NOT in the URL: Provide default value

Type: Fixed

Fixed value: all

Exception value: all

If this value is received, the filter will be ignored; i.e, "all values"

This works well for content types like article and blog_post.

When the View is displayed for the path /news/article, the contextual filter shows only Content type article.

When the View is displayed for the path /news/blog_post, the contextual filter shows only Content type blog_post.

So far, so good.

Now, the client has a custom content type called "Youtube Video" with machine name youtube_video.

When this View is displayed for the path /news/youtube_video. the contextual filter shows only Content type youtube_video.

But the client wants the path to be /news/video.

What are my options?

Some constraints:

  • The client definitely wants the path to be /news/video, and not something like /news?type=video.
  • I don't think that it's trivial to match /news/foo and substitute bar for foo as the input for a contextual filter.
  • I know how to use positional parameters in a route, but I don't think I can easily override the route for just /news/foo while preserving the contextual filter as it is for the other content types.

Some flexibility:

  • The Drupal 9 site is brand new, and I'm about to migrate the content from Drupal 7 via Migrate API, so it's possible to change the machine name of the youtube_video content type to video during the migration. Then this View would work without modification. (The problem would be figuring out everything else that references the machine name youtube_video and changing that to video also -- potentially a lot of effort.)
  • It may be possible to alter the query parameters conditionally using hook_views_pre_view() or hook_views_query_alter(), but I only want to do this if a specific parameter is matched.

2 Answers 2

3

Would hook_views_pre_view() do the trick?

function hook_views_pre_view(ViewExecutable $view, $display_id, array &$args) {
  if ($view->id() == 'my_view' &&
    $display_id == 'my_display' &&
    $args[0] == 'news' &&
    $args[1] == 'video') {
    $args[1] = 'youtube_video';
  }
}

Replace my_view and my_display with the appropriate values.

1
  • Thanks @uberhacker. I accepted your answer, which basically set me on the right path. Commented Apr 20, 2022 at 17:36
1

Here's my first working proof-of-concept based on the answer above:

/**
 * Implements hook_view_pre_view().
 */
function my_module_views_pre_view(ViewExecutable $view, $display_id, array &$args) {
  if ($view->id() == 'news' && isset($args[0])) {
    if ($args[0] == 'video') {
      $args[0] = 'youtube_video';
    }
  }
}

The first arg in the path /news/video actually just calls the View with $view_id = 'news', so the contextual filter parameters don't start until the second segment in that path, which is $arg[0].

And it's important to check that $arg[0] is set before checking its value; otherwise, the /news path will throw errors when no filters are passed.

Finally, in my case there's no need to check for a specific $display_id, since I want the string substitution to occur for all displays of this particular View.

Other than that, the answer above got it just right.

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.