From 91b4eed7d7d380edfd567bd4439168d32b389955 Mon Sep 17 00:00:00 2001
From: s j <sj@1729.be>
Date: Sun, 14 Nov 2021 08:06:20 +0100
Subject: [PATCH] add date period filter

---
 .../views_date_period.filter.schema.yml       |  10 ++
 src/Plugin/views/filter/DatePeriodFilter.php  | 150 ++++++++++++++++++
 views_date_period.views.inc                   |  58 +++++++
 3 files changed, 218 insertions(+)
 create mode 100644 config/schema/views_date_period.filter.schema.yml
 create mode 100644 src/Plugin/views/filter/DatePeriodFilter.php
 create mode 100644 views_date_period.views.inc

diff --git a/config/schema/views_date_period.filter.schema.yml b/config/schema/views_date_period.filter.schema.yml
new file mode 100644
index 0000000..5e044d1
--- /dev/null
+++ b/config/schema/views_date_period.filter.schema.yml
@@ -0,0 +1,10 @@
+views.filter_value.views_date_period_filter:
+  type: mapping
+  label: 'Date Period'
+  mapping:
+    period:
+      type: string
+      label: 'Period'
+    year:
+      type: string
+      label: 'Year'
diff --git a/src/Plugin/views/filter/DatePeriodFilter.php b/src/Plugin/views/filter/DatePeriodFilter.php
new file mode 100644
index 0000000..9b57674
--- /dev/null
+++ b/src/Plugin/views/filter/DatePeriodFilter.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace Drupal\views_date_period\Plugin\views\filter;
+
+use Drupal\Core\Datetime\DateHelper;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\views\Plugin\views\display\DisplayPluginBase;
+use Drupal\views\Plugin\views\filter\Equality;
+use Drupal\views\ViewExecutable;
+
+/**
+ * Filters a date field by (predefined) period.
+ *
+ * @ingroup views_filter_handlers
+ *
+ * @ViewsFilter("views_date_period_filter")
+ */
+class DatePeriodFilter extends Equality {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
+    parent::init($view, $display, $options);
+    $this->valueTitle = t('Period');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function defineOptions() {
+    $options = parent::defineOptions();
+    $options['periods']['default'] = [
+      'quarter' => 1,
+      'month' => 1,
+      'halfyear' => 1,
+    ];
+    return $options;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
+    parent::buildOptionsForm($form, $form_state);
+
+    $form['periods'] = [
+      '#type' => 'checkboxes',
+      '#title' => $this->t('Periods'),
+      '#default_value' => $this->options['periods'],
+      '#weight' => -90,
+      '#options' => [
+        'quarter' => $this->t("Quarter"),
+        'halfyear' => $this->t("Halfyear"),
+        'month' => $this->t("Month"),
+      ],
+    ];
+  }
+
+  /**
+   * Display the filter on the administrative summary.
+   */
+  public function adminSummary() {
+    return $this->operator . ' ' . $this->value['year'] . '-' . $this->value['period'];
+  }
+
+  /**
+   * Override the query.
+   */
+  public function query() {
+    if (!empty($this->value['year'])) {
+      $this->ensureMyTable();
+
+      $year = $this->value['year'];
+      if (!$year) {
+        $year = date('Y');
+      }
+
+      if (empty($this->value['period'])) {
+        $start = date("Y-m-d", strtotime("{$year}-1-1"));
+        $end = date("Y-m-d", strtotime("{$year}-12-1 last day of this month"));
+      }
+      elseif (substr($this->value['period'], 0, 1) === 'Q') {
+        $quarter = substr($this->value['period'], 1, 1);
+        $month = 1 + 3 * ($quarter - 1);
+        $start = date("Y-m-d", strtotime("{$year}-{$month}-1"));
+        $month = 3 * $quarter;
+        $end = date("Y-m-d", strtotime("{$year}-{$month}-1 last day of this month"));
+      }
+      else {
+        $start = date("Y-m-d", strtotime("{$year}-{$this->value['period']}-1"));
+        $end = date("Y-m-d", strtotime("{$year}-{$this->value['period']}-1 last day of this month"));
+      }
+
+      $this->query->addWhereExpression(
+        0,
+        "$this->tableAlias.$this->realField BETWEEN '{$start}' and '{$end}'",
+        []
+      );
+    }
+  }
+
+  /**
+   * Skip validation if no options have been chosen.
+   */
+  public function validate() {
+    if (!empty($this->value)) {
+      parent::validate();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function valueForm(&$form, FormStateInterface $form_state) {
+    parent::valueForm($form, $form_state);
+
+    $form['value']['#tree'] = TRUE;
+    $form['value']['#type'] = 'fieldgroup';
+
+    $months = DateHelper::monthNamesAbbr(TRUE);
+    $options = [
+      (string) $this->t("Quarter") => [
+        'Q1' => 'Q1',
+        'Q2' => 'Q2',
+        'Q3' => 'Q3',
+        'Q4' => 'Q4',
+      ],
+      (string) $this->t("Halfyear") => [
+        'H1' => 'H1',
+        'H2' => 'H2',
+      ],
+      (string) $this->t("Month") => $months,
+    ];
+    $form['value']['period'] = [
+      '#type' => 'select',
+      '#options' => $options,
+      '#empty_value' => '_none',
+    ];
+    $form['value']['year'] = [
+      '#type' => 'textfield',
+      '#size' => 4,
+      '#maxlength' => 4,
+      '#placeholder' => $this->t("Year"),
+    ];
+
+    return $form;
+  }
+
+}
diff --git a/views_date_period.views.inc b/views_date_period.views.inc
new file mode 100644
index 0000000..691c322
--- /dev/null
+++ b/views_date_period.views.inc
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Provides views handlers for views_date_period.
+ */
+
+use Drupal\Core\Entity\ContentEntityTypeInterface;
+
+/**
+ * Implements hook_views_data_alter().
+ */
+function views_date_period_views_data_alter(array &$data) {
+
+  // Get all entity types to next handling.
+  $entity_types = \Drupal::entityTypeManager()->getDefinitions();
+  // Entity field manager.
+  /** @var \Drupal\Core\Entity\EntityFieldManager $entity_field_manager */
+  $entity_field_manager = \Drupal::service('entity_field.manager');
+
+  // Process all entity types.
+  foreach ($entity_types as $entity_type_id => $entity_type) {
+    // We need to process only content entity types.
+    if ($entity_type instanceof ContentEntityTypeInterface) {
+      // Base fields what we need to process.
+      $base_field_definitions = $entity_field_manager->getBaseFieldDefinitions(
+        $entity_type_id);
+      /* $timestamp_field_types = ['timestamp', 'created', 'changed']; */
+      $timestamp_field_types = ['datetime'];
+
+      foreach ($base_field_definitions as $base_field_id => $base_field_definition) {
+        if (in_array($base_field_definition->getType(), $timestamp_field_types)) {
+          // The table name where the data stored for specific entity.
+          $table_name = $entity_type->getDataTable() ?: $entity_type->getBaseTable();
+          // Field label for next manipulations.
+          $base_field_label = (string) $base_field_definition->getLabel();
+
+          // Add period fileter.
+          $data[$table_name][$base_field_id . '_date_period'] = [
+            'title' => t('@label period', ['@label' => $base_field_label]),
+            'help' => t('@label period filter.', ['@label' => $base_field_label]),
+            /*
+            'argument' => [
+              'field' => $base_field_id,
+              'id' => 'views_dates_date_generic',
+            ],
+            */
+            'filter' => [
+              'field' => $base_field_id,
+              'id' => 'views_date_period_filter',
+            ],
+          ];
+        }
+      }
+    }
+  }
+
+}
-- 
GitLab