Sometimes out-of-the-box views functionality does not enough or the logic of the application very specific.
For instants, we have to create a custom filter. Well, let's have a look how to create a custom view's filter.
First of all, let's create a module with the structure like this:
dw_view_filter
├── src
│ └── Plugin
│ └── views
│ └── filter
│ └── ExampleViewsFilter.php
├── dw_view_filter.info.yml
└── dw_view_filter.views.inc
File dw_view_filter.views.inc content:
<?php
/**
* Implements hook_views_data_alter().
*/
function dw_view_filter_views_data_alter(array &$data): void {
$data['node_field_data']['example_date_filter'] = [
'title' => t('Example date filter'),
'filter' => [
'title' => t('Example date filter'),
'help' => t('Example date filter'),
'field' => 'nid',
'id' => 'example_date_filter'
],
];
}
File dw_view_filter.info.yml content:
name: 'View Filter'
type: module
description: 'This module provides example of custom view filter.'
core_version_requirement: ^9
package: 'Drupal Way'
dependencies:
- drupal:views
- drupal:node
File ExampleViewsFilter.php content:
<?php
namespace Drupal\dw_view_filter\Plugin\views\filter;
use Drupal\views\Annotation\ViewsFilter;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
use Drupal\views\Plugin\ViewsHandlerManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Datetime\DrupalDateTime;
/**
* Field handler to example data filter.
*
* @ingroup views_field_handlers
*
* @ViewsFilter("example_date_filter")
*/
class ExampleViewsFilter extends FilterPluginBase {
/**
* Views Handler Plugin Manager.
*
* @var \Drupal\views\Plugin\ViewsHandlerManager
*/
protected ViewsHandlerManager $joinHandler;
/**
* ExampleViewsFilter constructor.
*
* @param array $configuration
* @param $plugin_id
* @param $plugin_definition
* @param \Drupal\views\Plugin\ViewsHandlerManager $join_handler
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, ViewsHandlerManager $join_handler) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->joinHandler = $join_handler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): ExampleViewsFilter {
return new static(
$configuration, $plugin_id, $plugin_definition,
$container->get('plugin.manager.views.join')
);
}
/**
* {@inheritdoc}
*/
public function query(): void {
$this->ensureMyTable();
$table = 'node__field_date';
$join = $this->joinHandler->createInstance('standard', [
'table' => $table,
'field' => 'entity_id',
'left_table' => 'node_field_data',
'left_field' => 'nid',
]);
$this->query->addRelationship($table, $join, 'node_field_data');
$this->query->addWhere($this->options['group'], $table . '.field_date_value', (new DrupalDateTime())->format('Y-m-d\TH:i:s'), '<=');
}
}
The code is filtering nodes with field field_date.
So, a few notes to be more clear:
The field node__field_date is the table of your date field in Manage fields like field_date
Thi is column field_date_value of field_date table
Basically, this is it. But we can improve the filter's logic.
We can add exposed form for this filter and use two option Upcoming and Past:
<?php
use Drupal\Core\Form\FormStateInterface;
/**
* @param $form
* @param \Drupal\Core\Form\FormStateInterface $form_state
*/
protected function valueForm(array &$form, FormStateInterface $form_state): void {
if ($form_state->get('exposed')) {
$form['value']['example_date_filter'] = [
'#title' => $this->t('Filter by date'),
'#type' => 'select',
'#default_value' => 'past',
'#options' => [
'upcoming' => $this->t('Upcoming'),
'past' => $this->t('Past'),
],
];
}
}
And we have to modify function query:
<?php
/**
* {@inheritdoc}
*/
public function query(): void {
$this->ensureMyTable();
$id = $this->options['expose']['identifier'];
$input = $this->view->getExposedInput();
$table = 'node__field_date';
$join = $this->joinHandler->createInstance('standard', [
'table' => $table,
'field' => 'entity_id',
'left_table' => 'node_field_data',
'left_field' => 'nid',
]);
// Default value for "Past".
$operator = '<=';
if (isset($input[$id]) && $input[$id] === 'past') {
$operator = '<=';
}
if (isset($input[$id]) && $input[$id] === 'upcoming') {
$operator = '>=';
}
$this->query->addRelationship($table, $join, 'node_field_data');
$this->query->addWhere($this->options['group'], $table . '.field_date_value', (new DrupalDateTime())->format('Y-m-d\TH:i:s'), $operator);
}