Нодус:Навигация по дате

Компонент: Nodus

Рассмотрим часто встречающуюся задачу. Есть новости, и требуется выводить их не единой лентой, а с навигацией по годам (или даже месяцам). С одной стороны, в Нодусе есть все средства для этого: это ведь обычный селектор с аргументами и дополнительной навигацией сверху. С другой стороны, поскольку эта задача встречается довольно часто, то было решено сделать для этого стандартные средства: выборки и навигацию по календарю.

Итак, описываем последовательность действий.

Определяем календарное поле в типе материала

Прежде всего определяем, какое поле материала будет определять дату и отвечать за календарь. В стандартном типе "Новости" это - 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)

Возвращает адрес выборки за год/месяц/день

26.06.2014
Все статьи