You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

D8中如何通过编程实现视图的日、月、年份筛选?

我太懂这种刚需的痛苦了——Drupal 8确实把D7视图里那种直接按日/月/年拆分筛选的原生功能给砍掉了,折腾了好一阵才找到靠谱的编程实现方式,给你分享两种亲测有效的方案:

方案一:自定义模块添加专属的日/月/年视图筛选器

这是最灵活的方案,能完全自定义筛选逻辑,而且可以复用在多个视图里,适合长期项目使用。

  1. 先创建一个自定义模块(比如叫date_part_filter),在模块目录下新建date_part_filter.views.inc文件,实现hook_views_data()来注册我们的筛选器:
<?php

/**
 * Implements hook_views_data().
 */
function date_part_filter_views_data() {
  $data = [];

  // 这里以节点的创建日期为例,你可以替换成自己的自定义日期字段机器名
  $data['node_field_data']['created_month'] = [
    'title' => t('Created month'),
    'help' => t('Filter nodes by the month of their creation date.'),
    'filter' => [
      'id' => 'date_part_month',
      'field' => 'created',
    ],
  ];

  $data['node_field_data']['created_year'] = [
    'title' => t('Created year'),
    'help' => t('Filter nodes by the year of their creation date.'),
    'filter' => [
      'id' => 'date_part_year',
      'field' => 'created',
    ],
  ];

  $data['node_field_data']['created_day'] = [
    'title' => t('Created day'),
    'help' => t('Filter nodes by the day of their creation date.'),
    'filter' => [
      'id' => 'date_part_day',
      'field' => 'created',
    ],
  ];

  return $data;
}
  1. 接着在模块目录下创建src/Plugin/views/filter/DatePartFilter.php文件,写筛选器的核心处理类:
<?php

namespace Drupal\date_part_filter\Plugin\views\filter;

use Drupal\views\Plugin\views\filter\NumericFilter;
use Drupal\Core\Form\FormStateInterface;

/**
 * Base filter class for date parts (month/year/day).
 */
abstract class DatePartFilter extends NumericFilter {

  /**
   * Get the date part we're filtering on (month/year/day).
   */
  abstract protected function getDatePart();

  /**
   * {@inheritdoc}
   */
  public function query() {
    $this->ensureMyTable();
    $field = "$this->tableAlias.$this->realField";
    // 这里适配MySQL,如果你用PostgreSQL,把DATE_FORMAT换成EXTRACT(MONTH FROM $field)这类写法
    $date_part = $this->getDatePart();
    $expression = "DATE_FORMAT($field, '%$date_part')";
    $this->query->addWhereExpression($this->options['group'], "$expression = :value", [':value' => $this->value['value']]);
  }
}

/**
 * Filter by month of a date field.
 *
 * @ViewsFilter("date_part_month")
 */
class DatePartMonth extends DatePartFilter {
  protected function getDatePart() {
    return 'm';
  }
}

/**
 * Filter by year of a date field.
 *
 * @ViewsFilter("date_part_year")
 */
class DatePartYear extends DatePartFilter {
  protected function getDatePart() {
    return 'Y';
  }
}

/**
 * Filter by day of a date field.
 *
 * @ViewsFilter("date_part_day")
 */
class DatePartDay extends DatePartFilter {
  protected function getDatePart() {
    return 'd';
  }
}

注意:如果你的站点用PostgreSQL数据库,记得把DATE_FORMAT函数替换成PostgreSQL的EXTRACT语法,比如筛选月份就用EXTRACT(MONTH FROM $field),对应的格式符也要调整哦。

  1. 启用这个自定义模块后,去视图的「Filter Criteria」里就能找到「Created month」「Created year」「Created day」这几个新筛选器了,添加之后直接输入数字就能筛选(比如月份填1代表1月,年份填2024就行)。
方案二:用hook_form_alter扩展现有日期筛选器

如果不想写太多类,这个轻量方案更适合快速上手——直接改造视图里已有的日期筛选表单,添加下拉选项让用户选择按日/月/年筛选。

在你的自定义模块的.module文件里添加以下代码:

<?php

/**
 * Implements hook_form_alter().
 */
function custom_date_filter_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // 匹配视图的暴露筛选表单ID,格式一般是views_exposed_form_[视图名]_[显示名]
  if (strpos($form_id, 'views_exposed_form') !== FALSE) {
    foreach ($form as $key => $element) {
      // 找到日期类型的筛选元素,这里以创建日期为例,你可以换成自己的字段名
      if (isset($element['#type']) && $element['#type'] == 'date' && strpos($key, 'created') !== FALSE) {
        // 添加筛选类型下拉菜单
        $form[$key . '_part'] = [
          '#type' => 'select',
          '#title' => t('Filter by'),
          '#options' => [
            'full' => t('Full date'),
            'year' => t('Year'),
            'month' => t('Month'),
            'day' => t('Day'),
          ],
          '#default_value' => 'full',
          '#weight' => $element['#weight'] - 0.1,
        ];

        // 添加自定义提交处理函数,修改视图查询逻辑
        $form['#submit'][] = 'custom_date_filter_form_submit';
      }
    }
  }
}

/**
 * Custom submit handler for the date filter form.
 */
function custom_date_filter_form_submit(&$form, FormStateInterface $form_state) {
  $values = $form_state->getValues();
  $view = $form_state->get('view');
  
  foreach ($values as $key => $value) {
    // 匹配日期字段的筛选控件,排除我们添加的_part后缀字段
    if (strpos($key, 'created') !== FALSE && strpos($key, '_part') === FALSE && !empty($value)) {
      $part_key = $key . '_part';
      $date_part = $values[$part_key] ?? 'full';
      
      if ($date_part != 'full') {
        $date = new \DateTime($value);
        // 找到对应的视图筛选器
        foreach ($view->filter as $filter) {
          if ($filter->field == str_replace('filter_', '', $key)) {
            // 清空原筛选条件,添加日期部分筛选逻辑
            $filter->value = [];
            $field = "$filter->tableAlias.$filter->realField";
            
            switch ($date_part) {
              case 'year':
                $expression = "DATE_FORMAT($field, '%Y') = :year";
                $args = [':year' => $date->format('Y')];
                break;
              case 'month':
                $expression = "DATE_FORMAT($field, '%m') = :month";
                $args = [':month' => $date->format('m')];
                break;
              case 'day':
                $expression = "DATE_FORMAT($field, '%d') = :day";
                $args = [':day' => $date->format('d')];
                break;
              default:
                return;
            }
            
            $view->query->addWhereExpression($filter->options['group'], $expression, $args);
            // 阻止原筛选器执行默认逻辑
            $filter->setWhereGroup('OR');
            $filter->value = ['min' => '', 'max' => ''];
          }
        }
      }
    }
  }
}

这个方案的好处是不用额外注册视图数据,直接修改现有表单——用户在前端选择按年/月/日筛选后,输入任意一个对应日期(比如选「Month」后输入2024-05-01,系统会自动提取5月来筛选)。

两种方案都能完美满足你的需求,方案一更适合需要长期复用的场景,方案二更适合快速解决问题。如果是自定义日期字段,只需要把代码里的created替换成你的字段机器名就可以了。

内容的提问来源于stack exchange,提问作者Alan Yong

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:04:49