В статье рассмотрим как устроены 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 достаточно изучить, что это такое, несколько ссылок:

Автор: Виталий Бутурлин