====== Шаг 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->setFormDatasource($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->setFormDatasource($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_4:ru:tutorials:shop:checkout.png|}}}
===== Далее =====
Наши пользователи могут теперь добавлять новые заказы. Осталость совсем немного из того, что мы запланировали:
* работа с заказами в панели управления,
* просмотр покупателями своих заказов.
Итак, следующий шаг: [[step9|Шаг9. Работа с заказами]].