Последовательность наших действий будет следующей:
Этого будет вполне достаточно чтобы продемонстрировать один из подходов реализации вывода данных с различными ограничениями при помощи Limb3.
Файл shop/src/controller/ProductController.class.php:
<?php class ProductController extends lmbController { } ?>
Во фронтальной части мы должны обеспечить следующее условие - отображению подлежат только «доступные» товары, то есть те, у которых стоит флаг 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); } }
С классом lmbCriteria вы должны быть уже знакомы по реализации функционала по управлению пользователями. Здесь же мы использовали класс lmbSQLRawCriteria для вставки условия в запрос «как есть» (as is). Мы сразу решили использовать объектную форму критерии при вызове метода lmbActiveRecord :: find(), так как знаем, что нам придется в ближайшее время расширить этот функционал.
Файл shop/template/product/display.html:
<core:set title='Products'/> <core:WRAP file="page.html" as="content"> <active_record:fetch using='src/model/Product' find='for_front' target="products" navigator='pager'/> <core:include file='pager.html'/> <list:list id="products"> <table cellpadding="0" cellspacing="0" class='list'> <thead> </thead> <list:item> <tr> <td> <dl> <dt> <b>{$title}</b><br /> Price:<b>${$price|number:2, '.'}</b><br/> </dt> <dd> <img src='{$image_path}' class='img'/> {$description|nl2br|raw} </dd> </dl> </td> </tr> </list:item> </table> </list:list> </core:wrap>
Обратите внимание на атрибут find тега <active_record:fetch>. Он указывает, какой метод должен быть вызван у ActiveRecord для получения данных. В нашем случае for_front указывает на метод findForFront(). Таким образом, во фронтальной части мы будем отображать только доступные товары.
Теперь можете попробовать зайти на страницу /product вашего приложения. Вы должны увидеть список товаров, разделенный на страницы (если элементов достаточно много).
Теперь мы приступим к реализации поиска товаров по первым буквам. Это потребует отображения списка букв в шаблоне, а также наложения ограничений на выборку товаров, название которых начинается с выбранной буквы.
Здесь мы разберем использование так называемых fetcher-ов, о которых мы упоминали в шаге 4.4(Отображение профайла пользователя). Fetcher-ы - это такие классы, которые можно использовать для получения данных прямо в шаблонах при помощи тега <fetch>.
Для вывода списка букв алфавита мы создадим специальный класс AlphabetFetcher, который будет создавать итератор с буквами.
Итак, список букв мы будем формировать при помощи класса AlphabetFetcher. Если какая-либо буква является выбранной на данный момент, мы ее также помечаем определенным образом (поле current).
Файл shop/src/fetcher/AlphabetFetcher.class.php:
<?php lmb_require('limb/core/src/lmbCollection.class.php'); lmb_require('limb/web_app/src/fetcher/lmbFetcher.class.php'); class AlphabetFetcher extends lmbFetcher { function _createDataset() { $current_letter = ''; $request = lmbToolkit :: instance()->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(), из которого они должны обязательно возращать итератор.
Теперь мы будем использовать новый AlphabetFetcher в шаблоне для вывода списка букв. Для этого нам необходимо немного модифицировать шаблон product/display.html.
Файл shop/template/product/display.html:
<core:set title='Products'/> <core:WRAP file="page.html" as="content"> <active_record:fetch using='src/model/Product' find='for_front' target="products" navigator='pager'/> <core:include file='pager.html'/> <p/> <fetch using="src/fetcher/AlphabetFetcher" target="alphabet" /> <route_url params='controller:product'>Display all</route_url> <list:LIST id="alphabet"> <list:ITEM> <core:OPTIONAL for='current'> <b>{$letter|uppercase}</b> </core:OPTIONAL> <core:DEFAULT for='current'> <a href='/product?letter={$letter}'>{$letter|uppercase}</a> </core:DEFAULT> </list:ITEM> </list:LIST> <list:list id="products"> <table cellpadding="0" cellspacing="0" class='list'> [...]
Теперь нам необходимо, что метод Product :: findForFront() учитывал ограничения по первой букве названия товара, если необходимо.
Файл shop/src/model/Product.class.php:
<?php class Product extends lmbActiveRecord { [...] static function findForFront() { $request = lmbToolkit :: instance()->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 условия необходимо расставлять самостоятельно.
Сейчас можете попробовать этот новый функционал. Вы должны увидеть список букв и при нажатии на какую-либо из них, выборка товаров будет соответствующим образом ограничена:
Добавим новую форму для поиска товаров. Поиск будет доступен по названию продукта, а также по цене (больше какого-либо значения и(или) меньше).
Модификация файла shop/template/product/display.html:
[...] <p/><b>Search the products:</b> <form method="POST" id='search_form' runat='server' action='/product'> <label for='product'>Product title:</label> <input type="text" name="product" id="product" size='10'/> <label for='price_greater'>Price greater:</label> <input type="text" name="price_greater" id="price_greater" type="text" size='4'/> <label for='price_less'>Price less:</label> <input type="text" name="price_less" id="price_less" type="text" size='4'/> <input type='submit' name='search' value="Search!" class='button'/><br/> <input type='hidden' name='action' value='search' runat='client'/> </form> <list:list id="products"> <table cellpadding="0" cellspacing="0" class='list'> [...]
Обратите внимание на hidden поле action со значением search. При помощи этого поля мы фактически заставим контроллер ProductController выполнять действие search, а не display.
Для того, чтобы после отправки данных с формы поиска товаров у нас отраватывал все тот же product/display.html шаблон и чтобы форма поиска содержала те данные, которые были с нее отправлены, нам необходимо модифицировать контроллер ProductController.
Файл shop/src/controller/ProductController.class.php:
<?php class ProductController extends lmbController { function doSearch() { $this->setTemplate('product/display.html'); $this->useForm('search_form'); $this->setFormDatasource($this->request); } } ?>
Мы реализовали doSearch() метод, который выполняется при действии search. При помощи метода lmbController :: setTemplate($template_path) мы установили нужное нам название шаблона. Две другие строки метода связывают $request с формой в шаблоне, что позволяет отображать значения в полях формы после ее отправки.
Теперь можно модифицировать метод Product :: findForFront() для того, чтобы он учитывал данные, пришедшие с формы поиска.
Файл shop/src/model/Product.class.php:
<?php class Product extends lmbActiveRecord { [...] static function findForFront() { $request = lmbToolkit :: instance()->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) { [...] } } ?>
Надеемся, что наши последние действия в комментариях не требуются, и все понятно.
У нас уже есть список доступных к продаже товаров, теперь настало время добавить потенциальным покупателям добавлять приглянувшиеся им товары к корзину и оформлять заказ.
Итак, следующий шаг: Шаг6. Работа покупателей с корзиной заказа
Обсуждение