Как работает корзина Opencart?

Корзина в OpenCart, как много в этом слове… и сколько готовых модулей, сколько вопросов в интернете, сколько головных болей, сколько заказов на freelance…

Как правило, все хотят урезать корзину, убрать лишние шаги. Но что-бы править корзину на OpenCart, нужно знать как она работает. Не найдя в интернете (даже в официальной документации) информацию на этот счет, я решил стать первым и «перебрать косточки» корзине. Прочтя данный пост вы получите полное представление о внутреннем устройстве корзины на OpenCart. Делитесь этим постом с окружающими.

P.S. Данный пост писался по анализу исходного кода OpenCart 2.x.

Меньше слов, ближе к делу.
Определимся, какие в OpenCart есть страницы связанные с осуществлением заказа, есть страница:

  • «корзина» это страница где вы мы можете посмотреть ваш текущий заказ (перейти к разделу поста);
  • «оформление заказа» это страница где вы оформляете свой заказ, вы указываете свои данные, выбираете способ доставки, способ оплаты и т.п.

С «корзиной» все в порядке, в ней мы можем отредактировать заказ (удалить товары, изменить их количество).

Как работает корзина Opencart?

А вот «оформление заказа» в OpenCart своей громоздкостью меня раздражает (думаю многих), для окончательного оформления заказа, приходится выполнять много действий по заполнению полей и выбора способов оплаты/доставки. Как следствие, в интернете появилось много платных (меньше бесплатных) модулей для OpenCart «упрощенной корзины», которая состоит из списка товаров, поля ввода «фамилии», «e-mail» и «телефона». Периодически мне также приходят заказы, на «упрощение корзины» в OpenCart.

Как работает корзина Opencart?

«Корзина» и «оформление заказа» в OpenCart это прежде всего директория \catalog\controller\checkout
Пока в ней нас интересует два файла:

cart.php
checkout.php

Как работает корзина Opencart – cart.php?

За «корзину» отвечает контроллер «cart.php», который состоит из методов:

  • index() – Метод формирует данные и выводит «корзину» через файл представления default/template/checkout/cart.tpl
  • add() – Метод добавляет товар в корзину. Принимает post параметры
  • product_id — идентификационный номер продукта
  • quantity — количество товара
  • option — список опций
  • recurring_id — идентификационный номер периодичности (если используются профили периодичности, которые позволяет например через определенное время выставлять счет покупателю)

Когда вы нажимаете на «Купить» на странице описания товара (или на странице списка товаров в категории) срабатывает javascript обработчик, который шлет параметры product_id и quantity в метод add() контроллера cart.php, в результате чего он возвращает ответ (json) о состояние (добавлен товар в корзину или нет).

Как работает корзина Opencart - cart.php?

edit() – Метод изменяет количество товара в корзине. Принимает только post параметр «quantity». Когда вы нажимаете кнопку «Обновить» в корзине, javascript обработчик отсылает число из поля «Количество» в метод edit() контроллера cart.php.

Как работает корзина Opencart - cart.php?

remove() – Метод удаляет товар из корзины. Принимает post параметр «key», который представляет собой id номер позиции в корзине для вашей сесии, он определяет в таблице oc_cart: id сессии, id товара, опции, количество и дату.

При нажатие на кнопку «удалить» (выделил синим), javascript обработчик отправляет методу remove() параметр key.

Как работает корзина Opencart - cart.php?

Подытожим выше описанное:

Как работает корзина Opencart - cart.php?

Контроллер cart.php, позволяет добавлять товар в корзину с помощью add(), позволяет редактировать количество товара с помощью edit(), позволяет удалять позиции из корзины с помощью remove() и позволяет выводить саму корзину с помощью index().

За «оформление заказа» отвечает контроллер «checkout.php», который состоит из методов:

index() – метод формирует данные и выводит страницу «оформление заказа» с помощью файла представления default/template/checkout/checkout.tpl. Фактически это вывод страницы оформления с шагами.

country() – метод принимает номер страны и выдает список регионов.

customfield() – метод принимает номер группы клиента при регистрации и возвращает дополнительные поля для заполнения.

Наглядно, как работает оформление заказа в OpenCart можно изобразить так.

Как работает корзина Opencart - cart.php?

Когда вы заходите на страницу оформления заказа, ваш браузер запрашивает метод index() контроллера checkout.php, в результате вы получаете шаблон checkout.tpl, который, после своей загрузки, автоматически вызывает контроллер login.php. Рамкой я обозначил данный момент, т.к. в нем заключается вся суть оформления заказа. Методы country() и customfield() несут вспомогательную роль.

Контроллер login.php работает с некоторым количеством контроллеров, назовем их ядром страницы оформления заказа (обозначил овалом), но об этом позже. Сейчас достаточно знать, что каждый из контроллеров в «ядре» отвечает за свой шаг при оформление заказа и использует свой файл представления за вывод содержимого шага.

На некотором шаге происходит запрос к методу country() для вывода списка регионов согласно выбранной стране, на некотором другом шаге происходит запрос к customfield() для вывода дополнительных полей при регистрации (что это, будет рассказано позже).

Это взгляд из далека, для более полного понимания читайте ниже.

index()

Это сложная часть поста, для более полного понимания, я решил прокомментировать фрагменты php кода метода index() контроллера checkout.php. Желательно, что бы вы также открыли файл catalog/controller/checkout/checkout.php для своей версии OpenCart и проводили аналогию (если содержимое контроллеров будут отличаться).

Итак начало метода index()

// Validate cart has products and has stock.
if ((!$this->cart->hasProducts() && empty($this->session->data[‘vouchers’])) || (!$this->cart->hasStock() && !$this->config->get(‘config_stock_checkout’))) {
$this->response->redirect($this->url->link(‘checkout/cart’));
}

// Validate minimum quantity requirements.
$products = $this->cart->getProducts();

foreach ($products as $product) {
$product_total = 0;

foreach ($products as $product_2) {
if ($product_2[‘product_id’] == $product[‘product_id’]) {
$product_total += $product_2[‘quantity’];
}
}

if ($product[‘minimum’] > $product_total) {
$this->response->redirect($this->url->link(‘checkout/cart’));
}
}

Данный код, нужен для проверки, имеет ли корзина товар, достигнуто ли минимальное требование количества, если требования не выполняются, то нас отправляют на контроллер cart.php, т.е. просто на страницу корзины. Этот фрагмент не особо интересен, т.к. нет его необходимости править при сокращение «оформления заказа» в OpenCart.

Далее идет, подключение языкового файла checkout/checkout.php:

$this->load->language(‘checkout/checkout’);

Установки заголовка страницы из параметра «heading_title» выше подключенного языкового файла:

$this->document->setTitle($this->language->get(‘heading_title’));

Подключение js и css файлов:

$this->document->addScript(‘catalog/view/javascript/jquery/datetimepicker/moment.js’);
$this->document->addScript(‘catalog/view/javascript/jquery/datetimepicker/bootstrap-datetimepicker.min.js’);
$this->document->addStyle(‘catalog/view/javascript/jquery/datetimepicker/bootstrap-datetimepicker.min.css’);

// Required by klarna
if ($this->config->get(‘klarna_account’) || $this->config->get(‘klarna_invoice’)) {
$this->document->addScript(‘http://cdn.klarna.com/public/kitt/toc/v1.0/js/klarna.terms.min.js’);
}

Формирование массива «хлебных крошек»:

$data[‘breadcrumbs’] = array();

$data[‘breadcrumbs’][] = array(
‘text’ => $this->language->get(‘text_home’),
‘href’ => $this->url->link(‘common/home’)
);

$data[‘breadcrumbs’][] = array(
‘text’ => $this->language->get(‘text_cart’),
‘href’ => $this->url->link(‘checkout/cart’)
);

$data[‘breadcrumbs’][] = array(
‘text’ => $this->language->get(‘heading_title’),
‘href’ => $this->url->link(‘checkout/checkout’, ”, ‘SSL’)
);

Далее определяются переменные для файла представления, данные берутся из языкового файла.

$data[‘text_checkout_option’] = $this->language->get(‘text_checkout_option’);
$data[‘text_checkout_account’] = $this->language->get(‘text_checkout_account’);
$data[‘text_checkout_payment_address’] = $this->language->get(‘text_checkout_payment_address’);
$data[‘text_checkout_shipping_address’] = $this->language->get(‘text_checkout_shipping_address’);
$data[‘text_checkout_shipping_method’] = $this->language->get(‘text_checkout_shipping_method’);
$data[‘text_checkout_payment_method’] = $this->language->get(‘text_checkout_payment_method’);
$data[‘text_checkout_confirm’] = $this->language->get(‘text_checkout_confirm’);

Далее проверяется есть ли у нас ошибки в переменной сессии, если есть, то мы выводим текст ошибки в переменную.

if (isset($this->session->data[‘error’])) {
$data[‘error_warning’] = $this->session->data[‘error’];
unset($this->session->data[‘error’]);
} else {
$data[‘error_warning’] = ”;
}

Данный код определяет вошел ли в свою учетную запись покупатель или нет.

$data[‘logged’] = $this->customer->isLogged();

if (isset($this->session->data[‘account’])) {
$data[‘account’] = $this->session->data[‘account’];
} else {
$data[‘account’] = ”;
}

В зависимости от значений logged и account в представление

catalog\view\theme\default\template\checkout\checkout.tpl будет выбран вариант шага №2 при оформление покупки. Он может быть двух вариантов:

«Шаг 2: Профиль & Платежная информация»

Для случая, когда покупатель не зарегистрирован, в шаге №2, ему необходимо зарегистрироваться, ввести о себе данные и пароль.

Как работает корзина Opencart?

или

«Шаг 2: Платежная информация»

Для случае, если посетитель уже авторизовался

Как работает корзина Opencart?

В данном варианте, платежные данные это данные аккаунта, но посетитель может использовать и свой произвольный адрес.

Далее следует переменная, которая определяет, будут ли в оформление шаги, связанные с доставкой или нет. Т.е. будет ли шаг 3 «адрес доставки» и шаг 4 «способ доставки».

$data[‘shipping_required’] = $this->cart->hasShipping();

Значение данной переменной берется из настроек (выделил красным) в карточке товара, на вкладке «данные».

Как работает корзина Opencart?

Повторюсь, файл представления оформления заказа (catalog/view/theme/default/template/checkout/checkout.tpl) в зависимости от флага в shipping_required выводит или не вывод шаг №4 и шаг №5.

Как работает корзина Opencart?

Теперь важный момент, если вы посмотрите в checkout.tpl, то вы заметите, что шаблон в основном состоит из div контейнеров шагов оформления, и важно то, что содержимого у них нет, например:

<div id=”content” class=”<?php echo $class; ?>”><?php echo $content_top; ?>
<h1><?php echo $heading_title; ?></h1>
<div class=”panel-group” id=”accordion”>
<div class=”panel panel-default”>
<div class=”panel-heading”>
<h4 class=”panel-title”><?php echo $text_checkout_option; ?></h4>
</div>
<div class=”panel-collapse collapse” id=”collapse-checkout-option”>
<div class=”panel-body”></div>
</div>
</div>
<?php if (!$logged && $account != ‘guest’) { ?>
<div class=”panel panel-default”>
<div class=”panel-heading”>
<h4 class=”panel-title”><?php echo $text_checkout_account; ?></h4>
</div>
<div class=”panel-collapse collapse” id=”collapse-payment-address”>
<div class=”panel-body”></div>
</div>
</div>
<?php } else { ?>
<div class=”panel panel-default”>
<div class=”panel-heading”>
<h4 class=”panel-title”><?php echo $text_checkout_payment_address; ?></h4>
</div>
<div class=”panel-collapse collapse” id=”collapse-payment-address”>
<div class=”panel-body”></div>
</div>
</div>
<?php } ?>
<?php if ($shipping_required) { ?>
<div class=”panel panel-default”>
<div class=”panel-heading”>
<h4 class=”panel-title”><?php echo $text_checkout_shipping_address; ?></h4>
</div>
<div class=”panel-collapse collapse” id=”collapse-shipping-address”>
<div class=”panel-body”></div>
</div>
</div>
<div class=”panel panel-default”>
<div class=”panel-heading”>
<h4 class=”panel-title”><?php echo $text_checkout_shipping_method; ?></h4>
</div>
<div class=”panel-collapse collapse” id=”collapse-shipping-method”>
<div class=”panel-body”></div>
</div>
</div>
<?php } ?>
<div class=”panel panel-default”>
<div class=”panel-heading”>
<h4 class=”panel-title”><?php echo $text_checkout_payment_method; ?></h4>
</div>
<div class=”panel-collapse collapse” id=”collapse-payment-method”>
<div class=”panel-body”></div>
</div>
</div>
<div class=”panel panel-default”>
<div class=”panel-heading”>
<h4 class=”panel-title”><?php echo $text_checkout_confirm; ?></h4>
</div>
<div class=”panel-collapse collapse” id=”collapse-checkout-confirm”>
<div class=”panel-body”></div>
</div>
</div>
</div>
<?php echo $content_bottom; ?></div>
<?php echo $column_right; ?></div>
</div>

Откуда берется содержимое шагов? Ведь переменная shipping_required и аналогичные ей из контроллера checkout.php, определяет выводить данный div или нет?

Оказывается, когда вы нажимаете на каждом шаге кнопку –

Как работает корзина Opencart?

запускается javascript обработчик (определен в шаблоне checkout.tpl), который выполняет ajax запрос к контроллерам, а они в свою очередь возвращают tpl шаблон, с содержимым шага.

Вопрос, к каким контроллерам происходит ajax запрос?

Все зависит от шага, чья эта кнопка…

На этом моменте я остановлюсь поподробнее, если вы загляните в директорию catalog/controller/checkout, то вы увидите следующие файлы контроллеров.

Как работает корзина Opencart?

Я дам краткое пояснение, кто за что отвечает.

Повторюсь, как я говорил ранее, контроллер cart.php отвечает за корзину, а checkout.php за оформление заказа. Все остальные файлы отвечают за вывод информации того или иного шага при оформление заказа.

Рассмотрим остальные:

login.php – отвечает за шаг №1 «Способ оформление заказа», где пользователь выбирает будет он регистрироваться или нет, или он хочет войти в личный кабинет. За внешний вид отвечает файл представления login.tpl.

Как работает корзина Opencart?

registr.php – отвечает за шаг №2 «Профиль & Платежная информация», где пользователь вводит данные для регистрации. За внешний вид отвечает файл представления registr.tpl.

Как работает корзина Opencart?

guest.php – отвечает также за шаг №2 «Платежная информация», если пользователь выбрал на предыдущем шаге «Оформить заказ без регистрации». Отличается тем, что отсутствует поле ввода пароля. За внешний вид отвечает файл представления guest.tpl.

Как работает корзина Opencart?

payment_address.php – отвечает также за шаг №2, если пользователь на предыдущем шаге №2 «Платежная информация» убрал галку «Мой адрес доставки совпадает с платежным» и нажал «Продолжить». Пользователь или соглашается с введённым на предыдущем шаге адресе или вводит новый адрес.

За внешний вид отвечает файл представления payment_address.tpl.

Как работает корзина Opencart?

Возможно это баг OpenCart. Иногда бывает так, что, когда вы уберете галку «Мой адрес доставки совпадает с платежным» и нажмете «Продолжить» разворачивается шаг №3 вместо обновленного шага №2. В это случае вы можете, его сами развернуть.

shipping_address.php – отвечает за шаг №3 «Адрес доставки», где пользователь любо соглашается с ранее введённым адресом или вводит новый. За внешний вид отвечает файл представления shipping_address.tpl.

Как работает корзина Opencart?

guest_shipping.php – отвечает за шаг №3 «Адрес доставки», но для пользователя без регистрации. Пользователь вводит адрес доставки, за внешний вид отвечает guest_shipping.tpl.

Как работает корзина Opencart?

shipping_method.php – отвечает за шаг №4 «Способ доставки», где пользователь выбирает предложенные ему способы доставки. За внешний вид отвечает файл shipping_method.tpl.

Как работает корзина Opencart?

payment_method.php – отвечает за шаг №5 «Способ оплаты», где пользователь выбирает доступные ему способы оплаты. За внешний вид отвечает файл payment_method.tpl.

Как работает корзина Opencart?

confirm.php – отвечает за шаг №6 «Подтверждение заказа», где пользователю демонстрируется список товара на оформление с итоговой стоомостью. За внешний вид отвечает confirm.tpl.

Как работает корзина Opencart?

Нажимая на «Подтверждение заказа» данные отправляются в метод confirm() контроллера payment/cod. В случае успеха, вызывается контроллер success.php, который в свою очередь выводит результат через шаблон catalog/view/theme/default/template/common/success.tpl

Как работает корзина Opencart?

Что бы представлять ситуацию полностью я изобразил вот такую схему:

Как работает корзина Opencart?

У каждого контроллера для каждого шага, есть два метода:

index() для вывода файла представления (для получения шаблона) и save() для приема информации с файла представления.

Продолжаем просматривать далее checkout.php.

Далее определяются контроллеры для вывода левой, правой части и т.п. Данный фрагмент кода, есть в любом «корневом» контроллере.

$data[‘column_left’] = $this->load->controller(‘common/column_left’);
$data[‘column_right’] = $this->load->controller(‘common/column_right’);
$data[‘content_top’] = $this->load->controller(‘common/content_top’);
$data[‘content_bottom’] = $this->load->controller(‘common/content_bottom’);
$data[‘footer’] = $this->load->controller(‘common/footer’);
$data[‘header’] = $this->load->controller(‘common/header’);

Далее определяется файл представления для страницы оформления заказа.

if (file_exists(DIR_TEMPLATE . $this->config->get(‘config_template’) . ‘/template/checkout/checkout.tpl’)) {
$this->response->setOutput($this->load->view($this->config->get(‘config_template’) . ‘/template/checkout/checkout.tpl’, $data));
} else {
$this->response->setOutput($this->load->view(‘default/template/checkout/checkout.tpl’, $data));
}

Следующим метод сountry()

Метод сountry() получает country_id, это номер страны в выпадающем списке (select) «Страна» на вкладке «платежной информации». При изменения select-а запускается javascript обработчик, который формирует GET запрос с параметром «country_id» и шлет его контроллеру:
index.php?route=checkout/checkout/country&country_id=значение_с_select

Структурно в HTML обработчик располагается в 2-ом div-e «panel panel-default» в div-e «panel-group».

Как работает корзина Opencart?

Метод сountry() принимает «country_id» и передает его в качестве аргумента метода getCountry() модели localisation/country. Далее метод сountry() делает запрос к БД (конкретно к таблицам oc_zone и oc_country) и возвращает json ответ с полями.

‘country_id’ => $country_info[‘country_id’],
‘name’ => $country_info[‘name’],
‘iso_code_2’ => $country_info[‘iso_code_2’],
‘iso_code_3’ => $country_info[‘iso_code_3’],
‘address_format’ => $country_info[‘address_format’],
‘postcode_required’ => $country_info[‘postcode_required’],
‘zone’ => $this->model_localisation_zone->getZonesByCountryId($this->request->get[‘country_id’]),
‘status’ => $country_info[‘status’]

Обработчик разворачивает поле zone в option «Регион / Область»

Как работает корзина Opencart?

Метод customfield()

Метод используется customfield() данный метод редко, могу только сказать, что он использует метод getCustomFields модели account/custom_field, который обращается к таблицам в БД:

custom_field
custom_field_customer_group

В OpenCart, есть такое понятие как группа клиентов. Это означает, что в панели администратора вы можете создать различные группы клиентов, и посетитель при регистрации сможет выбрать, к какой группе ему относиться.

Для этого в настройках выбираем вкладку «Группа клиентов».

Как работает корзина Opencart?

Создаем группу, далее заполняем поле название, описание.

Как работает корзина Opencart?

В настройках -> опции где «Аккаунт» отмечаем галкой новую группу клиентов.

Как работает корзина Opencart?

Теперь, когда клиент будет регистрироваться, он получит выбор к какой группе себя отнести.

Как работает корзина Opencart?

И для каждой группы можно создать дополнительное поле, например для группы «Новая группа» при регистрации нужно добавить поле с датой. Для этого нужно зайти на вкладку «Настраиваемые поля»

Как работает корзина Opencart?

Добавить поле

Как работает корзина Opencart?

Задать, что поле будет иметь тип «даты» с именем «Тестовое поле» и отметить группы клиентов, для которых его нужно отображать, и для кого оно обязательно при заполнение. Отметим, что данное поле нужно показывать для «Новой группы».

Теперь при регистрации, если пользователь выберет «Новая группа» он увидит новое поле «Тестовое поле» с датой.

Как работает корзина Opencart?

К чему этот длинный разговор о группах клиентов?

Дело в том, что метод customfield() отвечает за вывод дополнительных полей. Т.е. когда пользователь нажимает на одну из radio кнопок, происходит запрос к контроллеру customfield(), который в свою очередь отвечает, какие поля вывести.