====== Шаг 8.2. Отправка заказа ====== ===== Что такое команды ===== Реализация действий контроллеров не обязательно должна находиться в самом контроллере. Limb3 содержит также специальный набор классов, который позволяет создавать отдельные классы для каждого действия. Такие классы называются **команды**, и для них существует специальный интерфейс **lmbCommand** (limb/web_app/src/command/lmbCommand.interface.php). Классы команд можно найти в папке **limb/web_app/src/command/**. Те или иные команды "умеют" практически все, что умеет lmbController. Например, есть класс **lmbActionCommand**, который используется для создания команд, реализующих действия. Также есть **lmbFormCommand**, который используется для обработки форм (он отнаследован от lmbActionCommmand). Команды на исполнение запускаются при помощи метода **perform()** без параметров. Обычно вызов команд производится из соответсвующего метода контроллера. Команды удобно использовать в случае, когда есть какой-либо код, который необходимо использовать повторно в различных контроллерах, однако выделять для этого общего предка не слишком удобно. Для демонстрации использования команд, мы реализуем действияе **checkout** контроллера CartController в виде команды **CartCheckoutCommand**. Потом мы реализуем вызов этой команды из контроллера CartController. ===== Класс CartCheckoutCommand ===== Приведем код класса сначала целиком, а потом поясним все моменты. Файл shop/src/controller/cart/CartCheckoutCommand.class.php: cart = $cart; $this->user = $this->toolkit->getUser(); } function _onBefore() { if(!$this->cart->getItemsCount()) { $this->flashMessage('Your cart is empty! Nothing to checkout!'); $this->redirect('/'); $this->halt(); } if(!$this->user->getIsLoggedIn()) { $this->flashMessage('Your are not logged in yet! Please login or register to checkout!'); $this->redirect(); $this->halt(); } } function _onShow() { $this->view->set('cart', $this->cart); $this->setViewFormDatasource($this->user); } function _onValid() { $order = Order :: createForCart($this->cart); $order->setAddress($this->request->get('address')); $order->setUser($this->user); if($order->trySave($this->error_list)) { $this->cart->removeAll(); $this->flashMessage('Your order has been sent. Your cart is now empty.'); $this->redirect('/'); $this->halt(); } } } ?> Итак, начнем наши пояснения. lmb_require('limb/web_app/src/command/lmbFormCommand.class.php'); lmb_require('src/model/Order.class.php'); class CartCheckoutCommand extends lmbFormCommand { protected $cart; protected $user; function __construct($cart) { parent :: __construct(null, 'checkout_form'); $this->cart = $cart; $this->user = $this->toolkit->getUser(); } [...] В качестве базового класса для нашего CartCheckoutCommand мы выбрали lmbFormCommand, так как оформление заказа связано с заполнением формы. Конструктор lmbFormCommand принимает 3 параметра: * имя шаблона * название формы в шаблоне * объект-валидатор данных В нашем случае мы указали только 2-й параметр. Имя шаблона lmbFormCommand определит точно также как и lmbController - по текущему действию и контроллеру. Строго говоря, имя шаблона будет угадано классом lmbActionCommand, от которого отнаследован lmbFormCommand. Валидатор нам не потребуется, так как мы собираемся использовать валидатор, встроенный в класс Order. В некоторых случаях название формы в шаблоне можно также не указывать - lmbFormCommand попробует его угадать, приблизительно так же, как и название шаблона. Угадывание производится по имени текущего контроллера. Например, если текущий контроллер NewsController, то имя формы будет угадано как news_form. Конструктор нашего класса CartCheckoutCommand принимает объект корзины $cart. Внутри lmbFormCommand доступны следующие уже знакомые нам объекты: * toolkit * request * response * error_list * view Передача ошибок валидации в шаблон производится автоматически. Движемся дальше... class CartCheckoutCommand extends lmbFormCommand { [...] function _onBefore() { [...] } function _onShow() { [...] } function _onValid() { [...] } } Класс lmbFormCommand уже содержит реализацию метода perform(), и по ходу выполнения он вызывает некоторые другие методы, которые дочерние классы могут перекрывать. Назовем кратко эти методы: * _onBefore() - вызывается самой первой. * _onShow() - если форма будет отображена только в первый раз * _onBeforeValidate() - если форма была отослана. До валидации. * _onValid() - если форма была отослана и процесс валидации прошел верно. Валидация при помощи переданного в контруктор класса lmbFormCommand объекта-валидатора. * _onError() - если форма была отослана, но данные не прошли валидацию. * _onAfterValidate() - вызывается после валидации, вне зависимости от результатов валидации * _onAfter() - вызывается самой последней, вне зависимости была ли форма отправлена или же будет отображаться в первый раз. В нашем случае достаточно перекрыть методы _onBefore(), _onShow() и _onValid(). class CartCheckoutCommand extends lmbFormCommand { [...] function _onBefore() { if(!$this->cart->getItemsCount()) { $this->flashMessage('Your cart is empty! Nothing to checkout!'); $this->redirect('/'); $this->halt(); } if(!$this->user->getIsLoggedIn()) { $this->flashMessage('Your are not logged in yet! Please login or register to checkout!'); $this->redirect(); $this->halt(); } } [...] } В методе _onBefore() мы делаем 2 проверки: * корзина должна содержать хотя бы одну товарную позицию. * пользователь должен быть залогинен. Если какое-либо из этих условий не выполняется, мы даем знать пользователю об этом при помощи метода flashMessage($message) и перебрасываем его на главную страницу сайта при помощи метода redirect(). Эти оба метода имеют полностью аналогичный смысл как и в классе lmbController. Теперь о вызове метода **halt()**. Методы вида _onBefore() и т.д. не возвращают из себя ничего, поэтому прервать ход выполнения команды можно только сгенерировав исключение. Именно для этого и существует метод halt() - он генерирует иключение специального типа, которое тут же ловится в lmbFormCommand, при этом выполенения метода perform() завершается. class CartCheckoutCommand extends lmbFormCommand { [...] function _onShow() { $this->view->set('cart', $this->cart); $this->setViewFormDatasource($this->user); } [...] Если форма будет отображена в первый раз (не вообще в первый раз, имеется ввиду отображение начального состояния формы, а не тогда когда она была отправлена), мы должны заполнить ее содержимое данными текущего пользователя. Плюс мы передаем в шаблон объект корзины для того, чтобы иметь возможность вывести содержимое корзины на страницы. И последнее... class CartCheckoutCommand extends lmbFormCommand { [...] function _onValid() { $order = Order :: createForCart($this->cart); $order->setAddress($this->request->get('address')); $order->setUser($this->user); if($order->trySave($this->error_list)) { $this->cart->removeAll(); $this->flashMessage('Your order has been sent. Your cart is now empty.'); $this->redirect('/'); $this->halt(); } } Мы создали новый объект класса Order при помощи статического метода createForCart($cart), заполнили поле адреса доступки address и указали пользователя. Если заказ был успешно сохранен, мы ошищаем корзину мне помощи метода **removeAll()**, даем знать пользователю, что операция прошла успешно и перебрасываем его на главную страницу. ===== Вызов команды из CartController ===== Теперь нам необходимо вызвать только что созданную команду из контроллера CartController Файл shop/src/controller/CartController.class.php: _getCart(); $this->performCommand('src/controller/cart/CartCheckoutCommand', $cart); } [...] } ?> Метод lmbController :: performCommand() принимает в качестве агрументов любое количество параметров (один или больше). Первый параметр указывает на путь до команды, второй и другие - будут переданы в том же порядке в конструктор команды. В нашем случае мы передали в команду объект корзины $cart. ===== Шаблон /cart/checkout.html ===== Наконец, приведем код шаблона по оформлению заказа cart/checkout.html Файл shop/template/cart/checkout.html: Your cart contains {$items_count} items.
Title Price Quantity Summ
{$product.title} ${$price|number:2, '.'} {$quantity} ${$summ|number:2, '.'}
Total summ is : ${$total_summ|number:2, '.'}



Шаблон содержит отображение списка товарных позиций корзины, также как и в шаблоне /cart/display.html. Ниже находится форма, которая позволяет ввести адрес доставки. Помните, что мы передали в эту форму данные текущего пользователя, поэтому если пользователь ранее ввел адрес доставки при регистрации или на странице профайла, то это значение будет отображено в соответствующем поле. ===== Предварительные итоги ===== Ссылка на действие по оформлению заказа уже должны была у нас быть на странице /cart. Вот так должна выглядеть страница /cart/checkout, если мы предварительно положим что-нибудь в корзину: {{{limb3_2007_2:ru:tutorials:shop:checkout.png|}}} ===== Далее ===== Наши пользователи могут теперь добавлять новые заказы. Осталость совсем немного из того, что мы запланировали: * работа с заказами в панели управления, * просмотр покупателями своих заказов. Итак, следующий шаг: [[step9|Шаг9. Работа с заказами]].