0

I am using Drupal 8.7.11 and have created a custom views exposed filter which provides users the option to filter an entity reference field using a checkbox list of names instead of autocomplete.

At the moment I have hardcoded the query conditions to build the list in a entity query with certain content checks e.g. date is in the future

However I would like to reuse the filter in a different view which doesn't need to check if a date is in the future.

Is it possible to either get the node ids from the view result, which I can then build the option list from or get the existing views filter criteria that the view uses to put into my list query?

Here is the code I have so far which works for on specific view.

views_mods.module

function views_mods_views_data_alter(&$data) {
  $data['node__field_event_tutor']['tutor'] = array(
    'title' => t('Tutor'),
    'filter' => [
      'title' => t('Tutor'),
      'help' => t('Select by tutor - provided by views mods module.'),
      'field' => 'field_event_tutor_target_id',
      'id' => 'views_mods_tutor',
      'group' => 'Content'
    ],
  );
}

src/Plugin/views/filter/TutorViewsFilter.php

<?php
namespace Drupal\views_mods\Plugin\views\filter;

use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\user\Entity\User;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Plugin\views\filter\ManyToOne;
use Drupal\views\ViewExecutable;

/**
 * Filters by tutor.
 *
 * @ingroup views_filter_handlers
 *
 * @ViewsFilter("views_mods_tutor")
 */
class TutorViewsFilter extends ManyToOne {
  /**
  * {@inheritdoc}
  */
  public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
    parent::init($view, $display, $options);
    $this->valueTitle = t('Tutor');
    $this->definition['options callback'] = [$this, 'generateOptions'];
  }

  /**
  * Override the query so that no filtering takes place if the user doesn't
  * select any options.
  */
  public function query() {
    if (!empty($this->value)) {
      parent::query();
    }
  }

  /**
  * Skip validation if no options have been chosen so we can use it as a
  * non-filter.
  */
  public function validate() {
    if (!empty($this->value)) {
      parent::validate();
    }
  }

  /**
  * Helper function that generates the options.
  * @return array
  */
  public function generateOptions() {
    $tutor_options = [];

    $time = new DrupalDateTime('now');
    // use entity query to get nids - this respects group memberships
    // so won't show tutors that are attached to nodes the user does not have
    // access to.
    $eq = \Drupal::entityQuery('node');
    $or_type = $eq->orConditionGroup()
      ->condition('type', 'tutorial')
      ->condition('title', 'workshop');
    $or_exclude = $eq->orConditionGroup()
      ->condition('field_exclude_from_listings', 0)
      ->notExists('field_exclude_from_listings'); // IS NULL
    $or_eas = $eq->orConditionGroup()
      ->condition('field_legacy_eas', 0)
      ->notExists('field_legacy_eas'); // IS NULL
    $nids = $eq
      ->condition('status', 1)
      ->condition($or_type)
      ->condition($or_exclude)
      ->condition($or_eas)
      ->condition('field_event_time', $time->format('Y-m-d\TH:i:s'), '>=')
      ->execute();

    $dq = \Drupal::database()->select('node__field_event_tutor', 'fe_tutor');
    $dq->addField('fe_tutor', 'field_event_tutor_target_id', 'uid');
    $dq->addField('f_name', 'field_prof_first_name_value', 'fname');
    $dq->addField('l_name', 'field_prof_last_name_value', 'lname');
    $dq->leftJoin('profile', 'profile', "fe_tutor.field_event_tutor_target_id = profile.uid AND profile.type = 'employee'");
    $dq->leftJoin('profile__field_prof_first_name', 'f_name', 'profile.profile_id = f_name.entity_id');
    $dq->leftJoin('profile__field_prof_last_name', 'l_name', 'profile.profile_id = l_name.entity_id');
    $dq->condition('fe_tutor.entity_id', $nids, 'IN');
    $dq->groupBy('fe_tutor.field_event_tutor_target_id');
    $dq->groupBy('f_name.field_prof_first_name_value');
    $dq->groupBy('l_name.field_prof_last_name_value');
    $tutor_list = $dq->execute()->fetchAll();

    if ($tutor_list) {
      foreach ($tutor_list as $index => $user) {
        $name = trim($user->fname) . ' ' . trim($user->lname);
        if (empty($user->lname)) {
          $account = User::load($user->uid);
          $name = $account->getAccountName();
        }
        $tutor_options[$user->uid] = $name;
      }
    }

    return $tutor_options;
  }
}

2 Answers 2

2

Based on the following article, I have a suggestion: https://zanzarra.com/blog/custom-views-filter-plugin-drupal-8-bounding-box-geofield . I haven't tried this out myself, but I think the following should work:

  • Override the valueForm and valueSubmit methods in your filter class:
    • Create a form that allows you to configure whether or not to include entities in the past (and whatever extra settings you wish to provide)
    • Roll all your values together in an encoded string of some sort: json, serialized array, whatever
  • Get your encoded string in the query override out of the value property, decode it, and use yuor settings as parameters for your generateOptions method

Obviously the valueForm is the form that allows yu to configure a filter when you add it to a view.

1
  • Thank you for the advice, I will have to look into the form override option and see if it is easier to get the filter criteria from views.
    – inertiahz
    Jan 9, 2020 at 15:05
0

I started working on a way to achieve this, however I am sure this is not the best way to do this, but it is working for my purposes.

In src/Plugin/views/filter/TutorViewsFilter.php I have replaced the hardcoded entity query conditions and created some semi logic to generate them from the views handlers.

    $eq = \Drupal::entityQuery('node');

    $filters = $this->view->getHandlers('filter');

    foreach ($filters as $field => $field_data) {
      $condition = [];
      if ($field_data['exposed'] == TRUE) {
//        @TODO: Add option to filter results by user input
        continue;
      } else {
        if ($field_data['table'] == 'node_field_data') {
          $condition = [$field_data["field"], $field_data["value"], $field_data['operator']];
        } elseif($field_data["plugin_id"] == 'datetime') {
          if ($field_data["value"]["value"] == 'now') {
            $time = new DrupalDateTime('now');
            $time_value = $time->format('Y-m-d\TH:i:s');
          } else {
            $time_value = $field_data["value"]["value"];
          }
          $condition = [substr($field_data["field"], 0, -6), $time_value, $field_data['operator']];
        } elseif ($field_data["plugin_id"] == 'numeric') {
          $condition = [substr($field_data["field"],0, -10), $field_data['value']['value'], $field_data['operator']];
        } else {
          $condition = [substr($field_data['table'], 6), $field_data['value'], $field_data['operator']];
        }

        switch ($field_data['operator']) {
          case 'not empty':
            $eq->exists($condition[0], $condition[1], $condition[2]); // IS NOT NULL
            break;
          default:
            $eq->condition($condition[0], $condition[1], $condition[2]);
        }
      }
    }
    $nids = $eq->execute();

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.