Когда делали Консерваторию, была задача сделать блок/страницу юбиляров. То есть выводить не просто людей, у которых скоро день рождения, а с круглыми значениями возраста. Заканчивающимися на 5 и 0. Сделать это чисто коробочными вьюзами не особо получается, поэтому пришлось написать немножко кода, о чем и расскажу.
Но сначала небольшое отступление. Как известно, в Drupal 8 модуль Views включили в ядро и это прекрасно. Действительно, контриб, который автоматически ставят во все проекты, не должен оставаться просто контрибом. Но некоторые вещи, сопутствующие включению/порту на D8, несколько раздражают.
Особенно сильно раздражает отсутствие привычного, традиционного, знакомого еще нашим дедам диалога Theme Information. Ну да, в D8 теперь Twig, но ссылка на Twig Debugging, где прямо написано «хер его знает, будет ли оно у вас нормально работать» — это какая-то не совсем замена диалогу. Можно было хотя бы примерные правила обозначить в текстовой форме, и то было бы полезно.
Понятно, что система большая, проблемы при разработке неизбежны, тем более при переходе на серьезный фреймворк и т.д. и т.п. Но, блин, когда что-то работает — ты ожидаешь, что во всяком случае оно будет и дальше работать, а не внезапно исчезнет без адекватной замены. Впрочем, конкретно по этому поводу есть иссуй, по нему есть прогресс и даже патч. Надеюсь, скоро и в очередную версию вкрутят.
Еще удалось столкнуться с тем, что названия терминов таксономии, будучи вставленными в заголовок вьюхи через контекстные фильтры — не переводятся. По этому поводу тоже есть патч, он работает (во всяком случае, у меня), но тесты пока не проходит и не закоммичен.
Теперь, собственно, про реализацию задуманного. Чтобы отфильтровать из всех людей только юбиляров, нужно сделать примерно следующее:
- Вычислить возраст, наступающий/наступивший в текущем году.
- Взять отстаток от его деления на 5.
- Оставить только тех, у кого остаток будет равен 0.
- Отсортировать выборку по дате рождения без учета года.
Конечно, сделать все это желательно в рамках запроса к БД, который выполняет вьюшка. Для этого всегда подходил 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()
— она позволит отсортировать дни рождения без учета года (функция возвращает номер дня в году).
На выходе получится отсортированный список юбиляров в текущем году.