3

Currently I have a boolean field field_private_content, if that field is set, the content should only be shown to authenticated users, anonymous users may only see the teaser and a login form. So far it's working well, also with views where I added the filter on that field. The filter get's removed automatically by the implementation of hook_views_pre_view() in a custom module:

function custom_module_views_pre_view(ViewExecutable $view, $display_id, array &$args) {
  // Remove filter on field_private_content != TRUE for authenticated users.
  if ($view->getHandler($view->current_display, 'filter', 'field_private_content') && \Drupal::currentUser()->isAuthenticated()) {
    $view->removeHandler($view->current_display, 'filter', 'field_private_content');
  }
}

Although that works very well, the behavior is not obvious from views ui. I would prefer to have a duplicate of the field_private_content filter which should have the mentioned behavior by default and could be labeled properly - the original filter should keep it's original behavior.

How can that be achieved?

How to add a custom views filter handler for a specific field?

8
+50

This require a little effort so I will only put here the main parts and a reference to an article that I used to create a custom Views filter in Drupal 8.

First you need to declare the filter inside the hook_views_data_alter() implementation:

$data['node_field_data']['nodes_titles'] = array(
  'title' => t('Node titles'),
  'filter' => array(
    'title' => t('Node titles'),
    'help' => t('Specify a list of titles a node can have.'),
    'field' => 'title',
    'id' => 'd8views_node_titles'
  ),
)

Since we are filtering on the title column, we are extending on the node_field_data table but with the title column as the real field to be used. Additionally, this time we are creating a plugin to handle the filtering identified as d8views_node_titles. Now it follows to create this class:

src/Plugin/views/filter/NodeTitles.php:

    /**
     * @file
     * Definition of Drupal\d8views\Plugin\views\filter\NodeTitles.
     */

    namespace Drupal\d8views\Plugin\views\filter;

    use Drupal\views\Plugin\views\display\DisplayPluginBase;
    use Drupal\views\Plugin\views\filter\InOperator;
    use Drupal\views\ViewExecutable;

    /**
     * Filters by given list of node title options.
     *
     * @ingroup views_filter_handlers
     *
     * @ViewsFilter("d8views_node_titles")
     */
    class NodeTitles extends InOperator {

      /**
       * {@inheritdoc}
       */
      public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
        parent::init($view, $display, $options);
        $this->valueTitle = t('Allowed node titles');
        $this->definition['options callback'] = array($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() {
        // Array keys are used to compare with the table field values.
        return array(
          'my title' => 'my title',
          'another title' => 'another title',
        );
      }

    }

Inside the plugin, you need to override three methods, as explained in Creating a custom Views filter in Drupal 8. Here is a quote from it:

Inside init(), we specify the title of the set of filter options and the callback that generates the values for options. This callback has to be a callable and in this case we opted for the generateOptions() method on this class. The latter just returns an array of options to be presented for the users, the keys of which being used in the query alteration. Alternatively, we could have also directly created the options inside the init() method by filling up the $this->valueOptions property with our available titles. Using a callback is cleaner though as you can perform various logic in there responsible for delivering the necessary node titles.

The point of overriding the query() and validate() methods was to prevent a query and validation from happening in case the user created the filter without selecting any title. This way the filter has no effect on the results rather than returning 0 results. It's a simple preference meant to illustrate how you can override various functionality to tailor your plugins to fit your needs.

  • Any insight for applying this to a fieldable field, as opposed to a base field, would be greatly appreciated. – jhchnc Feb 15 at 19:46
  • @user2890 is the same process – Adrian Cid Almaguer Feb 16 at 13:39
  • True. True. The key bit I was missing was to replace node_field_data with the table name of the field I was targeting. node_field_data seems to get you access to the base fields. I may be missing something, however. – jhchnc Feb 18 at 13:52
  • Getting a node field would be node__field_MYFELD instead of node_field_data. – simesy yesterday

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.