Инструменты пользователя

Инструменты сайта


limb3_2007_4:ru:tutorials:shop:step8-2

Шаг 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:

<?php
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();
  }
 
  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:

<?php
class CartController extends lmbController
{
  [...]
  function doCheckout()
  {
    $cart = $this->_getCart();
    $this->performCommand('src/controller/cart/CartCheckoutCommand', $cart);
  }
  [...]
}
?>

Метод lmbController :: performCommand() принимает в качестве агрументов любое количество параметров (один или больше). Первый параметр указывает на путь до команды, второй и другие - будут переданы в том же порядке в конструктор команды. В нашем случае мы передали в команду объект корзины $cart.

Шаблон /cart/checkout.html

Наконец, приведем код шаблона по оформлению заказа cart/checkout.html

Файл shop/template/cart/checkout.html:

<core:set title='Checkout'/>
<core:WRAP file="page.html" as="content">
 
<core:datasource from='#cart'>
Your cart contains {$items_count} items.
 
<list:LIST from="items">
<table cellpadding="0" cellspacing="0" class='list'>
  <thead>
  <tr>
    <th>Title</th>
    <th>Price</th>
    <th>Quantity</th>
    <th>Summ</th>
  </tr>
  </thead>
  <list:item>
  <tr class='{$Parity}'>
   <td>{$product.title}</td>
   <td>${$price|number:2, '.'}</td>
   <td>{$quantity}</td>
   <td>${$summ|number:2, '.'}</td>
  </tr>
  </list:item>
</table>
</list:LIST>
 
Total summ is : <b>${$total_summ|number:2, '.'}</b>
<br/>
 
<form name='checkout_form' id='checkout_form' method='POST' runat='server'>
 
  <label for='address'>Delivery address:</label><br/>
  <textarea type="text" name="address" id="address" title="Delivery address"></textarea><br/>
 
  <input type='submit' class='button' name='submitted' value="Finish order" class='button'/><br/>
</form>
 
</core:datasource>
 
</core:wrap>

Шаблон содержит отображение списка товарных позиций корзины, также как и в шаблоне /cart/display.html.

Ниже находится форма, которая позволяет ввести адрес доставки. Помните, что мы передали в эту форму данные текущего пользователя, поэтому если пользователь ранее ввел адрес доставки при регистрации или на странице профайла, то это значение будет отображено в соответствующем поле.

Предварительные итоги

Ссылка на действие по оформлению заказа уже должны была у нас быть на странице /cart.

Вот так должна выглядеть страница /cart/checkout, если мы предварительно положим что-нибудь в корзину:

}

Далее

Наши пользователи могут теперь добавлять новые заказы. Осталость совсем немного из того, что мы запланировали:

  • работа с заказами в панели управления,
  • просмотр покупателями своих заказов.

Итак, следующий шаг: Шаг9. Работа с заказами.

Обсуждение

Ваш комментарий. Вики-синтаксис разрешён:
   ____   ____   ____  __  __   __  ___
  / __/  / __/  /  _/ / / / /  /  |/  /
 _\ \   / _/   _/ /  / /_/ /  / /|_/ / 
/___/  /_/    /___/  \____/  /_/  /_/
 
limb3_2007_4/ru/tutorials/shop/step8-2.txt · Последние изменения: 2010/11/10 10:02 (внешнее изменение)