Нодус:Селекторы

Компонент: Nodus

Итак, в Нодусе есть простейший селектор кошек и котов. Но нам сейчас понадобится не простейший, а более сложный.

Задача

  • Для типа данных cat ввести атрибут "Половая принадлежность" (выбор: кот или кошка)
  • По адресу /cats/male/ выводить котов
  • По адресу /cats/female/ выводить кошек

Решение

Добавляем атрибут в Component.Nodus.App.Datatype.Cat.Type:

// app/components/Nodus/app/Datatype/Cat/Type.php
'gender' => array(
  'sqltype' => 'int index',
  'type' => 'select',
  'items' => array(1 => 'Кот', 2 => 'Кошка'),
  'caption' => 'Половая принадлежность',
  'tab' => 'default',
),

После добавления поля сбросьте кеш, пройдитесь по записям и установите пол для каждого представителя. Теперь нам надо создать класс селектора.

Селектор с жестко заданными параметрами

Идем в каталог app/components/Nodus/app/Selector и создаем там файл MaleCats.php

//  app/components/Nodus/app/Selector/MaleCats.php
Core::load('Component.Nodus.Selector');

class Component_Nodus_App_Selector_MaleCats extends Component_Nodus_Selector implements Core_ModuleInterface
{

		public function title()
	{
		return 'Коты';
	}

	public function url()
	{
		return '/cats/male/';
	}

	public function mapper()
	{
		return $this->base_mapper()
				->datatype('cat')
				->published_or_admin()
				->columns_in_select('image','price')
				->where('gender=:gender',1);
	}
}

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

  • base_mapper: функция селектора, возвращающая базовый ORM-маппер (без применения дополнительных условий)
  • datatype: - выбираем записи конкретного типа
  • published_or_admin: выбираем опубликованные записи, а если текущий посетитель - админ, то и неопубликованные. Если на сайте нет компонента "Зарегистрированные пользователи", то это работает только как выборка опубликованных.
  • columns_in_select: добавляем колонки в выборку. Если их не указать, то пользовательские поля, характерные для типа, в выборку не попадут, и следовательно картинка и цена в списке записей не отразятся.
  • where: выбираем записи, у которых поле gender равно единице.

Вот и все. Теперь по адресу /cats/male/ мы увидим список котов. Список кошек можно сделать по аналогии.

Селектор с аргументами

Вышеописанный способ страдает существенным недостатком. Он, конечно, хорош в одиночку, но когда мы имеем два аналогичных селектора (для котов и кошек), то решение уже представляется некрасивым. Эти два селектора будут отличаться друг от друга весьма незначительно, и потому хотелось бы не плодить лишний код, а попытаться обойтись одним селектором. Здесь нам поможет селектор с аргументами.

Первым делом нам требуется разрулить URL, по которому отзывается селектор. В данном случае нам нужно, чтобы один и тот же селектор отзывался по двум адресам: /cats/male/ и /cats/female/. Можно функцию url в селекторе записать так:

//  app/components/Nodus/app/Selector/MaleCats.php
public function url()
{
  return '/cats/(male|female)/';
}

Это - регулярное выражение, только без начальной-конечной скобок и модификаторов, которые будут подставлены автоматически. Первую часть проблемы мы решили: селектор откликается на оба адреса. Но теперь нужно решить другую проблему: маппер должен быть разным в зависимости от адреса. Можно, конечно, в функции mapper рассматривать REQUEST_URI и в зависимости от его содержимого модифицировать маппер. Однако, есть более красивый способ. Мы воспользуемся одной не слишком известной фичей регулярных выражений - именованными подстроками. Т.е. если в регулярном выражении будут именованные подстроки, то они будут переданы селектору в качестве аргументов и будут доступны через массив $this->args или функцию $this->arg($name). Наш селектор будет иметь следующий вид:

//  app/components/Nodus/app/Selector/GenderCats.php
Core::load('Component.Nodus.Selector');

class Component_Nodus_App_Selector_GenderCats extends Component_Nodus_Selector implements Core_ModuleInterface
{

	public function title()
	{
		return $this->arg('gender')=='male'? 'Коты' : 'Кошки';
	}

	public function url()
	{
		return '/cats/(?<gender>male|female)/';
	}

	public function mapper()
	{
		$gender = $this->arg('gender')=='male'? 1 : 2;
		return $this->base_mapper()
				->datatype('cat')
				->published_or_admin()
				->columns_in_select('image','price')
				->where('gender=:gender',$gender);
	}

}

Вот и все. Регулярное выражение выдает подстроку с именем gender, и в зависимости от ее содержимого мы модифицируем ORM-маппер и заголовок страницы.

Селектор с поисковой формой

Часто встречающаяся задача: есть записи, есть параметры поиска, нужно реализовать листинг записей по параметрам с формой наверху. Здесь я ограничусь лишь общими словами, ибо решение очевидно. Переопределяем шаблон selector-page-top и рисуем в нем форму, которая передает селектору аргументы в GET-запросе (сессии, куках - нужное подчеркнуть). В селекторе реализуем функцию mapper так, чтобы она учитывала GET-параметры (сессию, куки - нужное подчеркнуть). И все.

Модификаторы селекторов

У селектора есть такая особенность: вызовы всех неизвестных функций переадресуются ORM-мапперу. Таким образом возможно делать запросы, для которых специального селектора не существует. Например, нам нужно найти самого дешевого кота среди тех, у кого установлена цена (не нулевая). Делаем так:

$selector = Component_Nodus::selector('cat')->where('price>0')->order_by('price');
$cat = $selector->first();
Метки: Нодус
26.06.2014
Все статьи