Нодус:Навигация по дате
Рассмотрим часто встречающуюся задачу. Есть новости, и требуется выводить их не единой лентой, а с навигацией по годам (или даже месяцам). С одной стороны, в Нодусе есть все средства для этого: это ведь обычный селектор с аргументами и дополнительной навигацией сверху. С другой стороны, поскольку эта задача встречается довольно часто, то было решено сделать для этого стандартные средства: выборки и навигацию по календарю.
Итак, описываем последовательность действий.
Определяем календарное поле в типе материала
Прежде всего определяем, какое поле материала будет определять дату и отвечать за календарь. В стандартном типе "Новости" это - date_publish. Прежде всего укажем для него параметр calendar:
'date_publish' => array( 'type' => 'sqldatestr', 'sqltype' => 'datetime index', 'datepicker' => true, 'tab' => 'default', 'caption' => 'Дата новости', 'weight' => -85000, 'calendar' => true, ),
Что это дает? В базе данных наряду с полем date_publish автоматически создаются числовые поля:
- date_publish_year
- date_publish_month
- date_publish_day
Они заполняются при сохранении записи, причем работают независимо от формата поля date_publish: оно может быть как DATETIME, так и INT. Все выборки будут работать именно по этим полям.
Календарных полей может быть и несколько.
Определяем календарное поле в селекторе
Это нужно сделать для того, чтобы стали возможными выборки по дате. И это нужно сделать отдельно от типа, поскольку селекторов, работающих с одним типом, может быть несколько, но не во всех нужны выборки по календарю, а если и нужны, то не обязательно по одному и тому же полю.
class Component_Nodus_App_Datatype_News_Selector extends Component_Nodus_Selector { public function url() { return '/news/'; } public function calendar_field() { return 'date_publish'; } }
Теперь мы можем вызывать селектор по следующим адресам:
- /news/2011/ - показать новости за 2011 год
- /news/2011/08/ - показать новости за август 2011 года
- /news/2011/08/21/ - показать новости за 21 августа 2011 года
Изменяем заголовок на списке новостей
Хочется нам, чтобы по адресу /news/2011/ был заголовок: "Новости на 2011 год". Нет проблем, к нашим услугам функция title в селекторе:
public function title() { $title = 'Новости'; if (isset($this->args['_year'])) $title .= ' за '.$this->args['_year'].' год'; return $title; }
Аналогично делаем с месяцем и днем. При использовании календарных выборок в массиве аргументов могут присутствовать элементы _year, _month и _day. Последние два - не всегда, а только при выборках за месяц и день.
Выводим навигацию по годам
Переопределяем шаблон, в котором будет отображаться навигация. Например selector-navigator-top-news.phtml. В нем воспользуемся хелпером для отображения навигации:
print $this->nodus->calendar_years($selector);
Выводим навигацию по месяцам
Теперь нам нужна двухуровневая навигация: по годам, а ниже - по месяцам внутри этого года. Сначала все просто. Воспользуемся аналогичным хелпером, и выведем его ниже навигации по годам:
print $this->nodus->calendar_months($selector);
Обратите внимание, что этот хелпер работает только в том случае, если выбран год в навигации по годам. В противном случае он вернет пустую строку.
Вроде бы все работает, но есть один недостаток. При выборе года мы попадаем на выборку за год, а нам нужно при этом попадать на выборку за последний месяц этого года. Для этого у хелпера calendar_years есть второй необязательный параметр (булевский), указывающий использовать ли адресацию с учетом месяцев. Поэтому полностью наша навигация будет выглядеть так:
print $this->nodus->calendar_years($selector,true); print $this->nodus->calendar_months($selector);
В вышеописанном способе навигация происходит следующим образом: пользователь выбирает год, страница перезагружается, далее пользователь выбирает месяц, и снова происходит перезагрузка страницы. Для того, чтобы избежать лишних перезагрузок страницы, существует комплексный вывод месяцев. При использовании этого метода месяцы выводятся сразу за все года в скрытом виде, а отображаются только те, которые принадлежат выбранному году. Выбор текущего года происходит без перезагрузки страницы. В шаблоне навигации используйте метод calendar_complex:
print $this->nodus->calendar_years($selector,true); print $this->nodus->calendar_complex($selector);
Избавляемся от полной ленты новостей
По условию задачи страницы /news/ (полная лента) у нас нет. По ссылке "Новости" надо попасть на выборку по последнему году (месяцу). Для начала сделаем редирект со страницы /news/ на нужную нам страницу. Для этого стоит воспользоваться событием cms.actions.dispatch. Для этого, например, в index.php напишем:
Events::add_listener('cms.actions.dispatch',function($url) { if ($url=='/news/') { $url_news = Component_Nodus::selector('news')->calendar_last_year_url(); // если нужен последний месяц, то воспользуйтесь calendar_last_month_url(); if ($url_news) return Net_HTTP::redirect_to($url_news); } });
Но нас это не устраивает. Нам нужно, чтобы в навигации была ссылка непосредственно на нужную выборку. А редирект пусть останется для тех, кто ссылку ручками набрал. Для этого подменим ссылку в момент построения навигации через событие cms.navigation.add:
Events::add_listener('cms.navigation.add',function($title,$data,$url) { if ($url=='/news/') { $url = Component_Nodus::selector('news')->calendar_last_year_url(); // если нужен последний месяц, то воспользуйтесь calendar_last_month_url(); } });
Кастомизация навигатора
Прежде всего постарайтесь обойтись средствами CSS. Они поистине велики. Если не помогает, то можете переопределить шаблоны selector-calendar-years.phtml и selector-calendar-months.phtml. А если уж и этого мало, то рисуйте что хотите, воспользовавшись следующим набором функций селектора:
calendar_years($month_urls=false)
Возвращает массив информации по годам с адресами и количеством по годам. Если $month_urls==true, то адреса будут на последние месяцы.
calendar_months($year=false)
Возвращает аналогичный массив по месяцам. Можно передать год, за который нужна информация. Если год не передан, то будет взят год текущей выборки.
calendar_last_date()
Возвращает массив с информацией о дате последнего материала
calendar_url($year=false,$month=false,$day=false)
Возвращает адрес выборки за год/месяц/день