Нодус:Селекторы
Итак, в Нодусе есть простейший селектор кошек и котов. Но нам сейчас понадобится не простейший, а более сложный.
Задача
- Для типа данных 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();