Как отфильтровать ноды по дате без учета года с помощью Views в Drupal 7?

Главные вкладки

Аватар пользователя NoMax NoMax 18 ноября 2016 в 17:13

Здравствуйте. На своей работе веду разработку внутреннего информационного ресурса для образовательного учреждения. Решил реализовать что-то типа календаря знаменательных дней (день в истории) и поздравление именинников. создал типы материалов (памятная дата и персона), где даты хранятся в обычном друпаловском поле формата date.

Проблема резко всплыла с выводом нод по определённым критериям. стандартные средства фильтров views не дают возможности вывода нод без учета года. т.е. вывести ноды с учетом сравнения дат с текущей (меньше/больше с учетом года и текущего времени) - это вполне реализуемо. а вот отсечь год и сравнивать даты без его учета - с этим проблема.

для блока именинников хочу сделать так - если сегодняшний день и месяц совпадают со значением даты из поля ноды (без учета года) - выводить эти ноды.

для блока календаря знаменательных дат хочу реализовать вывод нод не по условию "историческая дата" == "сегодня", а по условию, чтобы выводилась ближайшая памятная дата: "историческая дата" >= "сегодня".

Копался в интернетах, находил советы сделать с помощью контекстного фильтра "предоставлять значение из даты", выставлял гранулирование "день", но это не работает.

Искал модули, удовлетворяющие моим потребностям - модуль birthdays, но много кто жалуется на его нестабильную работу и модуль просит для материалов добавить свой тип поля - не факт, что это поможет, а материал уже забит с другой структурой полей. хочу все же сделать чтобы из поля типа Date шли сравнение и фильтрация.

Пробовал заюзать модуль Views PHP - добавлял критерий фильтрации "глобальный PHP", расчехлял $row->field_hd_date['und'][0]['value'], переводил значение поля в дату, создавал переменную с сегодняшней датой, сравнивал даты в формате date("j') и date("m") от этих дат (и даже по date("z") - если бы сработало, можно было и котыль с високосными годами запилить), но обработка вьюхи начинала громоздко работать и появлялось куча предупреждений друпала. и в целом, представление нестабильное было.

Добрался до хуков, но не до конца понимаю как, куда и с чем это едят.

Может быть кто-то сталкивался с подобными задачами и смог решить их элегантно, легко и просто?

Заранее спасибо за подсказки.

Лучший ответ

Аватар пользователя NoMax NoMax 23 ноября 2016 в 14:37
1

В общем, всё остальное было от лукавого и левыми путями решения - реально, что помогло - это реализация хука mymodule_views_query_alter()

ниже будет много букв - мало доступной инфы в интернетах по поставленному вопросу, если кто-то вновь с таким столкнётся - надеюсь, ему поможет моя попытка пояснить что и с чем тут едят.

много времени было потрачено на поиск легких путей решения - через обычные фильтры views, контекстные фильтры, поиск годного модуля, попытка реализовать фильтр с помощью модуля Views PHP - однако, легкого и простого сравнения не получалось реализовать. пытался зацепиться за форматы дат, и сравнение по дню года, но потом отсёк данный вариант т.к. есть високосные года, которые смещают соответствие. костыли к этому крутить не хотелось. Обратился к великому и могучему SQL, нарвался на его функцию DATE_FORMAT(), которая позволяла вывести и сравнить дату в нужном мне формате. Вот тут-то и решил, что получать сразу нужную выборку будет гораздо кошернее, чем заниматься fetch-ингом и подобным. Составил тестовый SQL-запрос, проверил - работает. Причём работает именно так, как нужно:

SELECT eor_node.nid, eor_node.title FROM eor_node, eor_field_data_field_birthday WHERE eor_node.nid=eor_field_data_field_birthday.entity_id AND DATE_FORMAT(eor_field_data_field_birthday.field_birthday_value,'%d-%m')=DATE_FORMAT(NOW(),'%d-%m') ORDER BY DATE_FORMAT(eor_field_data_field_birthday.field_birthday_value,'%d')

есть таблица с нодами, есть таблица поля, которое хранит дату. между этими таблицами есть связь - каждое поле даты связано с определенной записью в таблице нод, для которой оно создано.
забираем id ноды и её заголовок из таблицы с нодами где есть связка нода-поле и форматированная дата (день-месяц) равна форматированной дате сегодняшнего дня

теперь оставалось самое интересное - изменить sql-запрос, который формирует views.

В общем, как и было предложено на просторах гугла - создал свой модуль добавил эту чудесную функцию, немного потупил с тем, как вносить правки в запрос, наткнулся на некоторые особенности, но их удалось победить. Справочной и полноценной инфы как конкретно добавлять/менять условия sql-запроса для views я не нашёл (может плохо искал). моим ориентиром были обрывочные примеры кода из найденных в интернете реализаций, абстрактное понятие как это может работать с точки зрения программирования и метода научного тыка.

Источники из интернетов предлагали использовать DEVEL и dpm(), но у меня как-то не сложилось с таким представлением отладки. помог сам модуль Views, а точнее пунктик в его настройках (структура->представления->настройки). Замечательный чек-бокс аля "Показывать SQL-запрос в представлении" помог мне отладить новоиспеченный модуль и не работать вслепую".

Теперь в окне редактирования представления ниже функциональных инструментов появился блок, выводящий SQL-запрос, формирующийся для вьюхи. Дальше пошло самое интересное - попытки научного тыка правильно добавлять условия в SQL-запрос вьюхи. Метода была такая: Правим код в файле модуля с хуком, сохраняем, переходим в браузер и жмакаем в окне редактирования вьюхи "предосмотр", чтобы увидеть формируемый SQL-запрос и ниже результат выборки. В общем все итерации научного тыка отложу, а приведу код, который "взлетел":

<?php
function myhook_views_query_alter(&$view, &$query) {
  if (
$view->name == 'persons') {
    
//добавляем в запрос таблицу, в которой хранятся поля с датой рождения
    
$query->add_table('field_data_field_birthday');
   
//добавляем условие, что день и месяц в дате рождения именинника должен совпадать с сегодняшним днем и месяцем
    
$query->add_where_expression('AND',"DATE_FORMAT(field_data_field_birthday.field_birthday_value,'%m')=DATE_FORMAT(NOW(),'%m')");
   
//условие взаимосвязи записей из таблицы нод и поля, хранящего даты ДР
    
$query->add_where_expression('AND',"nid=field_data_field_birthday.entity_id");
  }
};
?>

методом научного тыка выявлено:

  • при добавлении таблицы в запрос с помощью $query->add_table() префикс таблицы в бд писать не нужно;
  • чаще встречающееся в примерах использование конструкции для добавления where-условия в запрос $query->add_where() в данном случае невозможно - она парсит значения, режет скобки и служебные символы функций SQL - тем самым "портит" запрос, поэтому используется конструкция add_where_expression(), которую советуют не сильно использовать, но она реально работает и не портит компоненты запроса.

честно говоря, так глубоко в drupal я раньше не погружался, но рад, что всё же смог решить поставленный вопрос, который изначально вообще не виделся проблемным. полученная инфа для решения вопроса собиралась по крохам из других примеров. как обращаться с $query и какие конструкции к нему применять - единого и полноценного источника справочной инфы не нашёл, всё лишь багаж личных знаний и примерное понимание как это может работать. ещё смущает, что пока конструкция if ($view->name == 'persons') видоизменяет запрос для всего представления, а не для его конкретного блока или страницы - буду искать как это решить.

Всем спасибо за советы, отдельное спасибо xandeadx - не дал решения, но намекнул в какую сторону копать)

Комментарии

Аватар пользователя NoMax NoMax 23 ноября 2016 в 14:36

Видимо, Вы не внимательно читали - обычный функционал не даёт отсечь сравнение дат без учета года.

Аватар пользователя NoMax NoMax 23 ноября 2016 в 14:37
1

В общем, всё остальное было от лукавого и левыми путями решения - реально, что помогло - это реализация хука mymodule_views_query_alter()

ниже будет много букв - мало доступной инфы в интернетах по поставленному вопросу, если кто-то вновь с таким столкнётся - надеюсь, ему поможет моя попытка пояснить что и с чем тут едят.

много времени было потрачено на поиск легких путей решения - через обычные фильтры views, контекстные фильтры, поиск годного модуля, попытка реализовать фильтр с помощью модуля Views PHP - однако, легкого и простого сравнения не получалось реализовать. пытался зацепиться за форматы дат, и сравнение по дню года, но потом отсёк данный вариант т.к. есть високосные года, которые смещают соответствие. костыли к этому крутить не хотелось. Обратился к великому и могучему SQL, нарвался на его функцию DATE_FORMAT(), которая позволяла вывести и сравнить дату в нужном мне формате. Вот тут-то и решил, что получать сразу нужную выборку будет гораздо кошернее, чем заниматься fetch-ингом и подобным. Составил тестовый SQL-запрос, проверил - работает. Причём работает именно так, как нужно:

SELECT eor_node.nid, eor_node.title FROM eor_node, eor_field_data_field_birthday WHERE eor_node.nid=eor_field_data_field_birthday.entity_id AND DATE_FORMAT(eor_field_data_field_birthday.field_birthday_value,'%d-%m')=DATE_FORMAT(NOW(),'%d-%m') ORDER BY DATE_FORMAT(eor_field_data_field_birthday.field_birthday_value,'%d')

есть таблица с нодами, есть таблица поля, которое хранит дату. между этими таблицами есть связь - каждое поле даты связано с определенной записью в таблице нод, для которой оно создано.
забираем id ноды и её заголовок из таблицы с нодами где есть связка нода-поле и форматированная дата (день-месяц) равна форматированной дате сегодняшнего дня

теперь оставалось самое интересное - изменить sql-запрос, который формирует views.

В общем, как и было предложено на просторах гугла - создал свой модуль добавил эту чудесную функцию, немного потупил с тем, как вносить правки в запрос, наткнулся на некоторые особенности, но их удалось победить. Справочной и полноценной инфы как конкретно добавлять/менять условия sql-запроса для views я не нашёл (может плохо искал). моим ориентиром были обрывочные примеры кода из найденных в интернете реализаций, абстрактное понятие как это может работать с точки зрения программирования и метода научного тыка.

Источники из интернетов предлагали использовать DEVEL и dpm(), но у меня как-то не сложилось с таким представлением отладки. помог сам модуль Views, а точнее пунктик в его настройках (структура->представления->настройки). Замечательный чек-бокс аля "Показывать SQL-запрос в представлении" помог мне отладить новоиспеченный модуль и не работать вслепую".

Теперь в окне редактирования представления ниже функциональных инструментов появился блок, выводящий SQL-запрос, формирующийся для вьюхи. Дальше пошло самое интересное - попытки научного тыка правильно добавлять условия в SQL-запрос вьюхи. Метода была такая: Правим код в файле модуля с хуком, сохраняем, переходим в браузер и жмакаем в окне редактирования вьюхи "предосмотр", чтобы увидеть формируемый SQL-запрос и ниже результат выборки. В общем все итерации научного тыка отложу, а приведу код, который "взлетел":

<?php
function myhook_views_query_alter(&$view, &$query) {
  if (
$view->name == 'persons') {
    
//добавляем в запрос таблицу, в которой хранятся поля с датой рождения
    
$query->add_table('field_data_field_birthday');
   
//добавляем условие, что день и месяц в дате рождения именинника должен совпадать с сегодняшним днем и месяцем
    
$query->add_where_expression('AND',"DATE_FORMAT(field_data_field_birthday.field_birthday_value,'%m')=DATE_FORMAT(NOW(),'%m')");
   
//условие взаимосвязи записей из таблицы нод и поля, хранящего даты ДР
    
$query->add_where_expression('AND',"nid=field_data_field_birthday.entity_id");
  }
};
?>

методом научного тыка выявлено:

  • при добавлении таблицы в запрос с помощью $query->add_table() префикс таблицы в бд писать не нужно;
  • чаще встречающееся в примерах использование конструкции для добавления where-условия в запрос $query->add_where() в данном случае невозможно - она парсит значения, режет скобки и служебные символы функций SQL - тем самым "портит" запрос, поэтому используется конструкция add_where_expression(), которую советуют не сильно использовать, но она реально работает и не портит компоненты запроса.

честно говоря, так глубоко в drupal я раньше не погружался, но рад, что всё же смог решить поставленный вопрос, который изначально вообще не виделся проблемным. полученная инфа для решения вопроса собиралась по крохам из других примеров. как обращаться с $query и какие конструкции к нему применять - единого и полноценного источника справочной инфы не нашёл, всё лишь багаж личных знаний и примерное понимание как это может работать. ещё смущает, что пока конструкция if ($view->name == 'persons') видоизменяет запрос для всего представления, а не для его конкретного блока или страницы - буду искать как это решить.

Всем спасибо за советы, отдельное спасибо xandeadx - не дал решения, но намекнул в какую сторону копать)

Аватар пользователя marazmus marazmus 23 ноября 2016 в 15:53

Навскидку - еще можно было:

  • добавить вычисляемое поле,
  • запихать в него обработку-перевод даты в нужный формат,
  • в VBO пересохранить все нужные ноды, чтобы вычисляемое поле заполнилось данными
  • во views получить профит для решения по крайней мере задачи:

"отсечь год и сравнивать даты без его учета"

Задачу прочитал по диагонали, поэтому с нужным профитом могу ошибаться Smile