Problem/Motivation

I wish to programmatically add a filter to a view using hook_views_pre_view() in Drupal 9.

Steps to reproduce

In previous Drupal versions, it was done like this (using $view->add_item):

      $view->add_item(
        $view->current_display,
        'filter',
        'node',
        'nid',
        array(
          'operator' => '=',
          'value' => '59',
          'group' => 1
        )
      );

In Drupal 9, the above code now generates this error:

Call to undefined method Drupal\views\ViewExecutable::add_item()

Proposed resolution

Can someone provide an example of how to programmatically add a filter to a view using hook_views_pre_view() in Drupal 9?

CommentFileSizeAuthor
#4 dayblockfilter.png57.53 KBsteven snedker

Comments

SomebodySysop created an issue. See original summary.

lendude’s picture

Status: Active » Closed (works as designed)
Issue tags: -views, -hooks

Please check out the tests that exist for Views, quite a lot of code on how to dynamically add options/filters/fields can be found there.

For example from: \Drupal\Tests\views\Functional\Plugin\ExposedFormTest::testExposedIdentifier

    $view->displayHandlers->get('default')->overrideOption('filters', [
      'type' => [
        'exposed' => TRUE,
        'field' => 'type',
        'id' => 'type',
        'table' => 'node_field_data',
        'plugin_id' => 'in_operator',
        'entity_type' => 'node',
        'entity_field' => 'type',
        'expose' => [
          'identifier' => $identifier,
          'label' => 'Content: Type',
          'operator_id' => 'type_op',
          'reduce' => FALSE,
          'description' => 'Exposed overridden description',
        ],
      ],
    ]);

What works in which hook can be little hard to predict, so your milage may vary.

somebodysysop’s picture

Where do I find Views tests? When I google hook_views_pre_view() and search for examples of adding filters (not exposed filters, but adding a filter that programmatically modifies results), ALL the examples I have been able to find so far use $view->add_item(), which is deprecated in Drupal 9.

Perhaps the whole notion of using hook_views_pre_view() to add filters has been deprecated as well -- that is what I was hoping to discover some clue to here.

For others who might stumble onto this with a similar issue, I was able to add the filter I wanted using hook_views_query_alter():

function sbn_views_query_alter($view, $query) {
   if ($view->id() == 'draft_moderation_state') {    
      $field = 'node_field_data.nid';
	  // Get array of draft nids
	  $state = 'draft';
	  $value = sbn_get_all_nodes_in_moderation_state($state);
      $operator = 'IN';
      $query->where[1]['conditions'][] = [
        "field"    => $field,
        "value"    => $value,
        "operator" => $operator,
      ];
  }
}

Does exactly what I was looking to do. However, it would still be nice to know if, in Drupal 9, there is a direct replacement for $view->add_item() in hook_views_pre_view().

steven snedker’s picture

StatusFileSize
new57.53 KB

Well, SomebodySysop. I'm sorry that noone gave you a good answer in time and that the documentation for Drupal 9 is so awful. I try do document my own modules well, at least.

Anyway, here how I solved programmatically adding a filter to a view in Drupal 9.5. It may solve a problem for you in the future or bring some help to all the other lost developers looking for useful documentation and working examples.

Howto
I made a view (the format is Full Calender Display, but that doesn't matter) showing the jobs assigned to a specific car on a specific day.

My wish was to programmatically load and show a view block 10 times - once for each car on that day.

So in the views UI I added a filter for field_car_term set to 11.

Then I made this code:

function home() {
    $terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree('cars'); // Get all terms from vocabulary
    foreach ($terms as $term) { // Loop through them
      $view = Views::getView('job_calendar');
      $view->setDisplay('block_1');

      unset($view->display_handler->options['filters']['field_car_term_target_id']['value']['11']); // Unset the hardcoded value
      $view->display_handler->options['filters']['field_car_term_target_id']['value'][$term->tid] = $term->tid; // Set the dynamic value

      $ret[$term->tid]['cal'] = $view->buildRenderable(); // Add the render array of the view to an array
    }

    return $ret;     // Return render array
}

Notes

The filter has to exist in the view. Otherwise the above code will not work.

The filter should be hardcoded to something that you later unset. Here it's term id 11.

Setting the filter, you have a surprising amount of freedom

$view->display_handler->options['filters']['field_car_term_target_id']['value'][$term->tid] = $term->tid;
$view->display_handler->options['filters']['field_car_term_target_id']['value'][3] = $term->tid;
$view->display_handler->options['filters']['field_car_term_target_id']['value']['drupalisawfullydocmented'] = $term->tid;

all work well.

The saner looking

$view->display_handler->options['filters']['field_car_term_target_id']['value'] = $term->tid;

does not work whatsoever. So avoid that.

I'd be thrilled if there was a better place to read about how to programatically add filters to a view. A place with more and better examples. But alas. Looking for hours I did not find such a place.