====== Шаг5. Отображение списка товаров для покупателей. Поиск товаров ====== Последовательность наших действий будет следующей: * Мы создадим контроллер **ProductController** и реализуем базовый шаблон **product/display.html** для отображения списка товаров. * Мы создадим специальный статический метод Product :: **findForFront()**, который будет возвращать только список доступных товаров ($is_available == true). Именно эти товары мы будем отображать на фронтальной части. * Мы создадим **AlphabetFetcher**, который вернет список букв алфавита. В шаблоне product/display.html мы предоставим возможность покупателям просматривать товары, начинающиеся с какой-либо конкретной буквы. В методе Product :: findForFront() мы сделаем соответствующие изменения, чтобы он анализировал пришедшие ограничения. * Мы создадим форму для поиска товаров, а в методе Product :: findForFront() добавим анализ дополнительных ограничивающих параметров. Для полноценной поддержки формы поиска мы также добавим немного кода в контроллер ProductController. Этого будет вполне достаточно чтобы продемонстрировать один из подходов реализации вывода данных с различными ограничениями при помощи Limb3. ===== Первоначальный вывод товаров ===== ==== Контроллер ProductController ==== Файл shop/src/controller/ProductController.class.php: ==== Изменения в классе Product ==== Во фронтальной части мы должны обеспечить следующее условие - отображению подлежат только "доступные" товары, то есть те, у которых стоит флаг is_available. Для этого мы создадим дополнительный статический метод findForFront(): class Product extends lmbActiveRecord { [...] static function findForFront() { lmb_require('limb/dbal/src/criteria/lmbSQLRawCriteria.class.php'); $criteria = new lmbSQLRawCriteria('is_available = 1'); return lmbActiveRecord :: find('Product', $criteria); } } С классом [[..:..:packages:dbal:criteria|lmbCriteria]] вы должны быть уже знакомы по реализации функционала по управлению пользователями. Здесь же мы использовали класс **lmbSQLRawCriteria** для вставки условия в запрос "как есть" (as is). Мы сразу решили использовать объектную форму критерии при вызове метода lmbActiveRecord :: find(), так как знаем, что нам придется в ближайшее время расширить этот функционал. ==== Шаблон product/display.html ==== Файл shop/template/product/display.html:
{$title}
Price:${$price|number:2, '.'}
{$description|nl2br|raw}
Обратите внимание на атрибут **find** тега [[..:..:packages:wact:tags:lmb_fetch_tags:lmb_active_record_fetch_tag|]]. Он указывает, какой метод должен быть вызван у ActiveRecord для получения данных. В нашем случае for_front указывает на метод findForFront(). Таким образом, во фронтальной части мы будем отображать только доступные товары. Теперь можете попробовать зайти на страницу /product вашего приложения. Вы должны увидеть список товаров, разделенный на страницы (если элементов достаточно много). ===== Реализация ограничений по первой букве ===== Теперь мы приступим к реализации поиска товаров по первым буквам. Это потребует отображения списка букв в шаблоне, а также наложения ограничений на выборку товаров, название которых начинается с выбранной буквы. Здесь мы разберем использование так называемых **fetcher**-ов, о которых мы упоминали в шаге [[limb3_2007_4:ru:tutorials:shop:step4-4|4.4(Отображение профайла пользователя)]]. Fetcher-ы - это такие классы, которые можно использовать для получения данных прямо в шаблонах при помощи тега . Для вывода списка букв алфавита мы создадим специальный класс AlphabetFetcher, который будет создавать итератор с буквами. ==== Класс AlphabetFetcher для вывода алфавита ==== Итак, список букв мы будем формировать при помощи класса AlphabetFetcher. Если какая-либо буква является выбранной на данный момент, мы ее также помечаем определенным образом (поле current). Файл shop/src/fetcher/AlphabetFetcher.class.php: getRequest(); if($request->hasAttribute('letter')) $current_letter = $request->get('letter'); return new lmbCollection($this->_getEnglishLetters($current_letter)); } function _getEnglishLetters($current_letter = '') { $result = array(); for($i = 'A'; $i <= 'Z'; $i++) { if(strlen($i) == 2) break; $result[$i]['letter'] = $i; if ($i == $current_letter) $result[$i]['current'] = true; } return $result; } } ?> Класс AlphabetFetcher отнаследован от класса lmbFetcher. **lmbFetcher** - это базовый класс, от которого наследуются все остальные fetcher-ы. Дочерние классы перекрывают метод _creataDataset(), из которого они должны обязательно возращать итератор. ==== Изменения в шаблоне product/display.html ==== Теперь мы будем использовать новый AlphabetFetcher в шаблоне для вывода списка букв. Для этого нам необходимо немного модифицировать шаблон product/display.html. Файл shop/template/product/display.html:

Display all {$letter|uppercase} {$letter|uppercase} [...] [[..:..:packages:wact:fetch_tags|Подробнее о fetcher-ах и о применении тега ]]. ==== Изменения в классе Product для учитывания ограничений ==== Теперь нам необходимо, что метод Product :: findForFront() учитывал ограничения по первой букве названия товара, если необходимо. Файл shop/src/model/Product.class.php: getRequest(); lmb_require('limb/dbal/src/criteria/lmbSQLFieldCriteria.class.php'); lmb_require('limb/dbal/src/criteria/lmbSQLRawCriteria.class.php'); $criteria = new lmbSQLRawCriteria('is_available = 1'); self :: _appendLetterCriteria($criteria, $request); return lmbActiveRecord :: find('Product', $criteria); } static protected function _appendLetterCriteria($criteria, $request) { if($letter = $request->get('letter')) { $criteria->addAnd(new lmbSQLFieldCriteria('title', $letter . '%', lmbSQLFieldCriteria :: LIKE)); return true; } } } ?> Метод Product :: _appendLetterCriteria($criteria, $request) добавляет в $criteria дополнительное условие, если в запросе $request есть поле letter. Мы использовали класс lmbSQLFieldCriteria для дополнительного условия. Обратите внимание, что знаки **%** для LIKE условия необходимо расставлять самостоятельно. Сейчас можете попробовать этот новый функционал. Вы должны увидеть список букв и при нажатии на какую-либо из них, выборка товаров будет соответствующим образом ограничена: {{{limb3_2007_4:ru:tutorials:shop:alphabet.png|поиск по алфавиту}}} ===== Реализация формы поиска товаров ===== ==== Добавление формы поиска в шаблон product/display.html ==== Добавим новую форму для поиска товаров. Поиск будет доступен по названию продукта, а также по цене (больше какого-либо значения и(или) меньше). Модификация файла shop/template/product/display.html: [...]

Search the products:

[...] Обратите внимание на hidden поле **action** со значением search. При помощи этого поля мы фактически заставим контроллер ProductController выполнять действие search, а не display. ==== Модификация контроллера ProductController ==== Для того, чтобы после отправки данных с формы поиска товаров у нас отраватывал все тот же product/display.html шаблон и чтобы форма поиска содержала те данные, которые были с нее отправлены, нам необходимо модифицировать контроллер ProductController. Файл shop/src/controller/ProductController.class.php: setTemplate('product/display.html'); $this->useForm('search_form'); $this->setFormDatasource($this->request); } } ?> Мы реализовали **doSearch()** метод, который выполняется при действии search. При помощи метода lmbController :: **setTemplate($template_path)** мы установили нужное нам название шаблона. Две другие строки метода связывают $request с формой в шаблоне, что позволяет отображать значения в полях формы после ее отправки. ==== Изменения в классе Product для учета новых критерий ==== Теперь можно модифицировать метод Product :: findForFront() для того, чтобы он учитывал данные, пришедшие с формы поиска. Файл shop/src/model/Product.class.php: getRequest(); lmb_require('limb/dbal/src/criteria/lmbSQLFieldCriteria.class.php'); lmb_require('limb/dbal/src/criteria/lmbSQLRawCriteria.class.php'); $criteria = new lmbSQLRawCriteria('is_available = 1'); if(!self :: _appendLetterCriteria($criteria, $request)) self :: _appendSearchCriteria($criteria, $request); return lmbActiveRecord :: find('Product', $criteria); } static protected function _appendSearchCriteria($criteria, $request) { if(!$request->get('search')) return false; if($product = $request->get('product')) $criteria->addAnd(new lmbSQLFieldCriteria('title', '%' . $product . '%', lmbSQLFieldCriteria :: LIKE)); if($price_less = $request->get('price_less')) $criteria->addAnd(new lmbSQLFieldCriteria('price', $price_less, lmbSQLFieldCriteria :: LESS)); if($price_greater = $request->get('price_greater')) $criteria->addAnd(new lmbSQLFieldCriteria('price', $price_greater, lmbSQLFieldCriteria :: GREATER)); } static protected function _appendLetterCriteria($criteria, $request) { [...] } } ?> Надеемся, что наши последние действия в комментариях не требуются, и все понятно. ===== Предварительные итоги ===== Небольшой скриншот того, как должен выглядеть список товаров при использовании формы поиска: {{{limb3_2007_4:ru:tutorials:shop:search_products.png|поиск продуктов}}} ===== Далее ===== У нас уже есть список доступных к продаже товаров, теперь настало время добавить потенциальным покупателям добавлять приглянувшиеся им товары к корзину и оформлять заказ. Итак, следующий шаг: [[step6|Шаг6. Работа покупателей с корзиной заказа]]