Drupal 8 Views и вывод юбилеев

Когда делали Консерваторию, была задача сделать блок/страницу юбиляров. То есть выводить не просто людей, у которых скоро день рождения, а с круглыми значениями возраста. Заканчивающимися на 5 и 0. Сделать это чисто коробочными вьюзами не особо получается, поэтому пришлось написать немножко кода, о чем и расскажу.

Но сначала небольшое отступление. Как известно, в Drupal 8 модуль Views включили в ядро и это прекрасно. Действительно, контриб, который автоматически ставят во все проекты, не должен оставаться просто контрибом. Но некоторые вещи, сопутствующие включению/порту на D8, несколько раздражают.

Особенно сильно раздражает отсутствие привычного, традиционного, знакомого еще нашим дедам диалога Theme Information. Ну да, в D8 теперь Twig, но ссылка на Twig Debugging, где прямо написано «хер его знает, будет ли оно у вас нормально работать» — это какая-то не совсем замена диалогу. Можно было хотя бы примерные правила обозначить в текстовой форме, и то было бы полезно.

Понятно, что система большая, проблемы при разработке неизбежны, тем более при переходе на серьезный фреймворк и т.д. и т.п. Но, блин, когда что-то работает — ты ожидаешь, что во всяком случае оно будет и дальше работать, а не внезапно исчезнет без адекватной замены. Впрочем, конкретно по этому поводу есть иссуй, по нему есть прогресс и даже патч. Надеюсь, скоро и в очередную версию вкрутят.

Еще удалось столкнуться с тем, что названия терминов таксономии, будучи вставленными в заголовок вьюхи через контекстные фильтры — не переводятся. По этому поводу тоже есть патч, он работает (во всяком случае, у меня), но тесты пока не проходит и не закоммичен.

Теперь, собственно, про реализацию задуманного. Чтобы отфильтровать из всех людей только юбиляров, нужно сделать примерно следующее:

  1. Вычислить возраст, наступающий/наступивший в текущем году.
  2. Взять отстаток от его деления на 5.
  3. Оставить только тех, у кого остаток будет равен 0.
  4. Отсортировать выборку по дате рождения без учета года.

Конечно, сделать все это желательно в рамках запроса к БД, который выполняет вьюшка. Для этого всегда подходил hook_views_query_alter(), который есть и в Drupal 8. Но с этим хуком нас поджидает ловушка. Об нее легко могут сломать мозг те, кто не полностью и/или не супер-внимательно читал документацию и комментарии к ней.

Дело в том, что хуки hook_views_*_alter() раньше размещались в файле mymodule.views.inc. И если разместить в этом файле хук сейчас, он тоже будет работать. Но — только при очистке кэша. Не в смысле, что надо почистить кэш и заработает. А в смысле чистим кэш со страницы нашей вьюхи — хук срабатывает. Обновляем страницу после чистки кэша (сколько угодно раз) — не срабатывает. Снова чистим — работает. Обновляем — опять не работает. Штука в том, что в D8 этот хук нужно размещать в файле mymodule.views_execution.inc, только тогда он будет срабатывать как надо.

Преодолев этот нюанс, можем наконец написать код. Поле с датой рождения в данном примере называется field_birthdate:

use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\ViewExecutable;

/**
 * Implements hook_views_query_alter().
 * @param \Drupal\views\ViewExecutable $view
 * @param \Drupal\views\Plugin\views\query\QueryPluginBase $query
 */
function mymodule_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
  if ($view->id() != 'jubilees') {
    return;
  }

  // фильтруем юбилеи
  $query->addField('node__field_birthdate', 'field_birthdate_value');
  $field = 'node__field_birthdate.field_birthdate_value';
  $age_value = "(YEAR(CURRENT_TIMESTAMP) - YEAR($field))";
  $jubilee_expression = "((MOD($age_value, 5)=0) AND ($age_value >= 10))";
  $query->addWhereExpression(0, $jubilee_expression);

  // сортировка по дате без учета года
  $query->addOrderBy(NULL, "DAYOFYEAR($field)", 'ASC', 'order_field');
}

Для отбора только юбиляров мы вычисляем возраст (как разницу текущего года и года рождения) и фильтруем по остатку от деления на 5. Для сортировки по возрастанию дня рождения, пользуемся функцией mysql DAYOFYEAR() — она позволит отсортировать дни рождения без учета года (функция возвращает номер дня в году).

На выходе получится отсортированный список юбиляров в текущем году.

Комментарии