Ajax, REST API OpenCart
В статье рассмотрим как устроены ajax запросы в OpenCart, в том числе запросы через api OpenCart, познакомимся с новым понятием front controller и немного коснемся темы ajax REST API.
Клиент
Клиентская часть OpenCart работает с использованием jquery, а значит можно использовать $.ajax
из этой библиотеки. Ссылка на документацию. Примеры ajax запросов на клиентской части можно посмотреть в admin/view/template/sale/order_form.tpl
(.twig для OpenCart 3.0).
Сервер
Просматривая все тот же файл admin/view/template/sale/order_form.tpl
(для OpenCart 2.3) можно понять, что в качестве адреса вызова используется классическая схема роутинга OpenCart. Посмотрим на один из запросов:
$.ajax({
url: 'index.php?route=customer/customer/autocomplete&token=<?php echo $token; ?>&filter_name=' + encodeURIComponent(request),
...
Все просто: url - путь до контроллера и, если надо, имя метода этого контроллера.
То есть, нам нужно создать класс контроллера, затем из файлов представления можно вызывать методы этого контроллера ajax запросами.
Создадим контроллер нашего нового тестового модуля по пути admin/controller/extension/module/myajax.php:
class ControllerExtensionModuleMyAjax extends Controller
{
public function index()
{
$this->response->addHeader('Content-Type: application/json');
$this->response->setOutput(json_encode(
[
"success" => true,
"message" => "ok",
"data" => []
]
));
}
}
В классе контроллера есть объект response
, это экземпляр класса Response
, который расположен по пути system/library/response.php
. Он позволяет управлять ответом сервера. Нас интересуют только 2 метода:
addHeader($header)
- добавить http заголовок,header
строковый аргументsetOutput($output)
- установить данные для вывода,output
строковый аргумент
Для формирования ответа на запрос в методе контроллера можно использовать -
$this->response
Так как OpenCart имеет 2 режима доступа/контекста (admin, catalog), то передаваемые данные в запросах разные:
admin
- требует токен вget
параметре (получить можно из объекта класса контроллера):для OpenCart 2.3
token
, который берется из$this->session->data['token']
для OpenCart 3.0 ¨C4C, который берется из¨C14C¨C5C
catalog
- в общем случае не требует токена, но есть нюансы о которых позже
Теперь чтобы осуществить ajax запрос достаточно в файл представления (читай в html) подставить js (код для OpenCart 2.3):
$.ajax({
url: '<?php echo $admin; ?>index.php?route=extesion/module/myajax&token=<?php echo $token; ?>',
type: 'get',
dataType: 'json',
success: function(json) {
alert("success: "+json["success"]+"\n"+"message: "+json["message"]);
},
error: function(xhr, ajaxOptions, thrownError) {
alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
}
});
В этом коде в url admin
это путь указывающий контекст запроса (admin или catalog). Для контекста есть 2 дефайна, определенных в admin/config
:
HTTP_SERVER
илиHTTP<b style="box-sizing: border-box;">S</b>_SERVER
- путь до директорииadmin
(проще - админка), где будет осуществлен поиск контроллера для выполнения запросаHTTP_CATALOG
или ¨C6C¨C15C- корень сайта, однако контроллеры будут браться из директории¨C16C¨C7C
Ajax API
Просматривая файл представления admin/view/template/sale/order_form.tpl
(OpenCart 2.3), можно увидеть что из админки осуществляются ajax запросы на catalog
контекст, с использованием особого токена.
Сначала объявляется глобальная переменная token
, затем ajax запрос на адрес /index.php?route=api/login
, который отвечает json данными, в которых есть ключ token
:
var token = '';
// Login to the API
$.ajax({
url: '<?php echo $catalog; ?>index.php?route=api/login',
type: 'post',
data: 'key=<?php echo $api_key; ?>',
dataType: 'json',
crossDomain: true,
success: function(json) {
//...
if (json['token']) {
token = json['token'];
}
},
error: function(xhr, ajaxOptions, thrownError) {
alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
}
});
Контроллер этого запроса находится в catalog/controller/api/login.php ControllerApiLogin::index
. Он:
создает новую сессию
(
catalog/model/account/api.php - ModelAccountApi::addApiSession
) игенерирует для нее случайный токен (функция
token
находится вsystem/helper/general.php
),
который возвращается в json этого ajax запроса, если доступ по api (api_key
) разрешен для текущего пользователя (Админка-Система-API).
Дальше разбирая представление admin/view/template/sale/order_form.tpl
можно увидеть, что последующие ajax запросы, которые по адресу route=api/...
используют этот самый token
для определения права доступа, таким образом (в каждом api файле, в каждом методе)¨C25C существует такой кусок кода для определения права осуществлять запрос:
if (!isset($this->session->data['api_id'])) {
$json['error']['warning'] = $this->language->get('error_permission');
} else {
...
}
Ajax запросы через
catalog
контекст можно осуществлять с использованиемtoken
для безопасного доступа
А теперь копнем глубже и выясним, как это происходит внутри движка, ведь можно отправлять ajax запросы и без токена.
Просматривая код файла index.php
отправляемся в system/startup.php
, оттуда следуем в system/framework.php
в самый конец и видим такое вот:
// Front Controller
$controller = new Front($registry);
// Pre Actions
if ($config->has('action_pre_action')) {
foreach ($config->get('action_pre_action') as $value) {
$controller->addPreAction(new Action($value));
}
}
Здесь видим новое понятие front controller, код которого находится в system/engine/front.php
в классе Front
.
Ниже следует мое субъективное определение этого понятия :)
Подробных комментариев найти не удалось, но судя по коду front controller это главный/передний контроллер, он запускает общий контроллер startup/router
относительно директории controller
контекста (admin/controller
или catalog/controller
), который выполняет первичные контроллеры, указанные в $_['action_pre_action'];
в файле system/config/catalog.php
.
В коде выше происходит только добавление первичных контроллеров во front controller, а их исполнение осуществляется кодом ниже, в методе dispatch
(внутри метода перед выполнением action указанного в $config->get('action_router')
):
// Dispatch
$controller->dispatch(new Action($config->get('action_router')), new Action($config->get('action_error')));
Среди первичных контроллеров есть startup/session
относительно catalog/controller
, где в ControllerStartupSession::index
находится интересующий нас код для авторизации в api через токен. Вкратце:
происходит проверка обращения к
api/
и наличияget
параметраtoken
удаление старых api сессий
выборка актуальной api сессии на основании ip адреса запросившего и его токена
старт сессии с id из
$_COOKIE["api"]
обновление времени модификации сессии, (чтобы она осталась жива, то есть не была устаревшей)
Теперь, когда исполнение кода дойдет до целевого контроллера,
$this->session->data['api_id']
уже будет инициализировано, если указана актуальная комбинация токена и ip адреса.
Ajax REST API
Данная глава описывает возможный вариант создания и встроенные средства реализации REST API в OpenCart.
Мы рассмотрели реализацию ajax запросов OpenCart для admin
и catalog
контекстов.
Если говорить об admin
, то предполагается более рациональным реализовывать контроллеры именно в admin
контексте. Однако, такое не всегда возможно. Иногда один и тот же код контроллера (возможно речь о методе контроллера) должен использоваться в обработчике catalog
события (например при изменении заказа), так и отдельно непосредственно при работе с заказом через админку. Чтобы устранить такие случаи можно реализовать контроллеры в catalog
контексте и организовать для них безопасный доступ (о чем говорится в предыдущей главе).
Для реализации REST API в OpenCart есть все необходимое:
объект для работы с ответом сервера в контроллере
$this->response
, а именно методыaddHeader
иsetOutput
безопасная работа с административным доступом через
catalog
¨C22Cконтекстединая точка входа api через¨C24C¨C10C¨C25C контекст, в директорию¨C26C¨C11C¨C27C, можно размещать свои файлы контроллеров и при помощи ajax осуществлять к ним запросы
На стороне сервера надо создать контроллеры в catalog/controller/api/
, а на стороне клиента добавить ajax запросы в нужные файлы представлений с использованием токена, полученного в результате ajax запроса api/login
. Если в этих файлах нет такого ajax запроса, тогда необходимо добавить его, например, взяв из admin/view/template/sale/order_form.tpl.
Теперь чтобы сделать REST API достаточно изучить, что это такое, несколько ссылок: