Русский
Русский
English
Статистика
Реклама

События в OpenCart

Статья нашего сотрудника из его личного блога.

В статье речь идет об OpenCart версии> =2.3, а именно рассматриваются 2.3 и 3.0

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

Система событий OpenCart это генерируемые события до и после загрузки файлов движка/модулей.

Например, рассмотрим контроллерadmin/controller/catalog/product.phpу которого на адрес/admin/index.php?route=catalog/productбудет вызван методindex:

public function index() {    $this->load->language('catalog/product');     $this->document->setTitle($this->language->get('heading_title'));     $this->load->model('catalog/product');     $this->getList();}

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

Какие события есть в OpenCart 2.3+?

Как мы определили ранее, заранее определенного списка событий нет. Однако предполагаемые события можно узнать в файлеsystem/engine/loader.php.$this->loadи есть объектLoader.

Просматривая файл можно увидеть что события генерируются ($this->registry->get('event')->trigger) при загрузке:

  • контроллеров

  • моделей

  • представлений

  • конфигов

  • переводов

Нас интересуют не все объекты. Так как OpenCart построен по MVCl архитектуре, то в нем есть 4 вида загружаемых файлов, на основании загрузки, которых можно изменить/добавить логику движку. MVCl вкратце:

  • Model - файлы моделей, те что работают с БД,admin/modelилиcatalog/model

  • View - файлы представлений, интерфейс/верстка,admin/viewилиcatalog/view

  • Controller - файлы обработчики роутов(путей по которым админы ходят в админке, а клиенты в клиентской части),admin/controllerилиcatalog/controller

  • language - файлы переводов,admin/languageилиcatalog/language

При загрузке каждого такого объекта(кроме конфигов)движка генерируются события:

  • before- до загрузки

  • after- после загрузки

То есть, мы можем изменить(или вообще заменить, но об этом позже)логику при загрузке файла.

Логика работы событий

В момент, когда мы открываем страницу админки, или клиент просматривает товар, или со страницы сайта происходит ajax запрос, движок запускает первый контроллерstartup/router, который в свою очередь на основании get параметраrouteвыполняетactionцелевого контроллера(путь которого указан в route).

Однако, контроллерstartup/routerне выполняет загрузку через$this-> load, а самостоятельно генерирует событиеbefore, получая от него результат, и если этот результатnull, тогда целевой контроллер будет выполнен и наступит событиеafter(куcок кода изadmin/controller/startup/router.phpOpenCart 3.0):

// Trigger the pre events$result = $this->event->trigger('controller/' . $route . '/before', array(&$route, &$data)); if (!is_null($result)) {    return $result;} // We dont want to use the loader class as it would make an controller callable.$action = new Action($route); // Any output needs to be another Action object.$output = $action->execute($this->registry);  // Trigger the post events$result = $this->event->trigger('controller/' . $route . '/after', array(&$route, &$data, &$output)); if (!is_null($result)) {    return $result;} return $output;

Иными словами,

OpenCart 2.3+ позволяет полностью переопределить поведение запроса приbeforeилиafterсобытии.

Для загрузчиков файлов действует другая логика.

Если в событииbeforeодин из обработчиков возвращает неnull, тогда загрузка файла не будет происходить, ивместо результата загрузки файлабудетрезультат выполнения обработчика, вернувшего неnull. При этом событиеafterбудет сгенерировано и если один из обработчиков вернет неnullтогда результат его работы заменит предыдущий. Это можно увидеть на примере загрузки контроллера (основная логика аналогична и для представлений/моделей/переводов):

public function controller($route, $data = array()) {  // Sanitize the call  $route = preg_replace('/[^a-zA-Z0-9_\/]/', '', (string)$route);     // Keep the original trigger  $trigger = $route;     file_put_contents($_SERVER['DOCUMENT_ROOT']."/loader-controller.txt", $trigger."\n", FILE_APPEND);     // Trigger the pre events  $result = $this->registry->get('event')->trigger('controller/' . $trigger . '/before', array(&$route, &$data));     // Make sure its only the last event that returns an output if required.  if ($result != null && !$result instanceof Exception) {    $output = $result;  } else {    $action = new Action($route);    $output = $action->execute($this->registry, array(&$data));  }     // Trigger the post events  $result = $this->registry->get('event')->trigger('controller/' . $trigger . '/after', array(&$route, &$data, &$output));     if ($result && !$result instanceof Exception) {    $output = $result;  }     if (!$output instanceof Exception) {    return $output;  }  }

Любой из обработчиков событияbeforeилиafter, может переопределить результат выполнения события.

Вsystem/engine/event.php Event::triggerопределено: если какой-либо обработчик события(вbeforeилиafter)возвращает неnull, тогда после него не будут запущены другие обработчики этого события(дляbeforeилиafter).

Аргументы обработчиков событий

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

Аргументы событий передаются по ссылке, а значит изменения значений аргументов будут видны во всех местах где они используются. При загрузке MVCl файлов аргументы:

  • приbeforeсобытии:&$routeи&$data

  • приafterсобытии:&$route,&$dataи&$output

Описание аргументов(дополнительно можно посмотреть здесьsystem/engine/loader.php):

  • &$routeсодержит данные о пути данного события, безcontroller|view|model|languageи безbefore|after, например, для событияcatalog/model/checkout/order/addOrderHistory/afterв&$routeбудетcheckout/order/addOrderHistory

  • &$dataсодержит массив данных для работы события(либо пустой массив), например, для файлов представления это данные для подстановки в tpl/twig файлах

  • &$outputсодержит результат работы самого события(или обработчика, который определил возвращаемое значение), например, для файлов представления это обработанное содержимое файла представления

Для видов в&$dataпередается ассоциативный массив для использования в tpl/twig файлах видов. В&$outputверстка загруженного вида, где данные из&$dataуже вставлены. Изменения&$dataприbeforeсобытии могут не иметь смысла, так как данные уже обработаны. Это относится ко всем загружаемым файлам.

Изменять&$outputприafterсобытии представления можно различными способами, один из которых используя библиотеку Simple Html DOM.

Удаление данных из&$dataприbeforeсобытии может быть критичным для следующих обработчиков, а добавление данных может не иметь смысла если обработчики событий не знают этих данных!

Хранение обработчиков событий в OpenCart

Системные обработчики хранятся в php файлахsystem/config/admin.phpиsystem/config/catalog.phpв ассоциативном массиве$_['action_event'], где ключ это загружаемый файл, а значение обработчик. Как видно из ключей этого массива, вместо полного пути загружаемого файла, можно указывать * в качестве реакции "на все". Таким образом часть событий проходит через "движковые обработчики".

В OpenCart 3.0 появились приоритеты работы обработчиков(чем меньше значение, тем выше приоритет)и объекты моделей больше не обрабатываются "движковыми" обработчиками.

Пользовательские(от модулей)обработчики событий хранятся в БД в таблицеevent:

  • event_id- идентификатор (автоинкремент)

  • code- код обработчика, один и тот же код может быть у нескольких обработчиков, сюда записывается название модуля

  • trigger- событие, например,admin/view/catalog/product_form/after- после загрузки формы товара

  • action- обработчик, напримерextension/module/productmarkedfield/eventProductFormAfter

  • status- включен или нет обработчик (1/0)

  • sort_order- порядок сортировки (приоритет выполнения обработчика)

Добавление обработчиков событий

Работа с событиями заключается в:

  • регистрации обработчиков событий при инсталляции модуля(методinstall)

  • удалении всех обработчиков событий при деинсталляции модуля(методuninstall)

Обязательно надо удалять обработчики событий при удалении модуля, иначе движок будет запускать обработчики из модуля, а при отсутствии файлов удаленного модуля будет ошибка!

Для работы с событиями на стороне админки, для OpenCart 2.3 есть модельextension/event, а для OpenCart 3.0setting/event.

Метод регистрации в обоих версиях одинаковый, за исключением параметра$sort_order(кусок кода из моделиeventдля OpenCart 3.0):

public function addEvent($code, $trigger, $action, $status = 1, $sort_order = 0) {    $this->db->query("INSERT INTO `" . DB_PREFIX . "event` SET `code` = '" . $this->db->escape($code) . "', `trigger` = '" . $this->db->escape($trigger) . "', `action` = '" . $this->db->escape($action) . "', `sort_order` = '" . (int)$sort_order . "', `status` = '" . (int)$status . "'");     return $this->db->getLastId();}

Отличается только названием обращения к объекту, через который ведется регистрация.

Разберем значение аргументов:

  • code- идентификатор группы обработчиков, обычно это название модуля

  • trigger- обрабатываемое событие, напримерadmin/view/catalog/product_form/after

  • action- контроллер обработчик события, например,extension/module/productmarkedfield/eventProductFormAfterэто путь до файла обработчика, относительно того контекста для которого устанавливается обработчик, при этом в указанном файле должен быть класс имя, которого формируется из

"Controller" . $sRelPath . $sFileName

гдеsRelPathэто относительный путь до файла, аsFileNameэто имя файла. Для данного примера имя класса контроллера будетControllerExtensionModuleProductmarkedfield

  • status- статус вкл/выкл, по умолчанию включен

  • sort_order- порядок сортировки (OpenCart 3.0), чем меньше значение, тем выше приоритет выполнения, по умолчанию 0

Отдельно стоит рассказать про отношениеtriggerиaction.triggerуказывается полным путем(почти)до файла(и в случае контроллера или модели еще и указанием выполняемого метода)вместе с контекстомadminилиcatalog, аactionуказывается относительным, безadminилиcatalog. Например,

  • trigger-admin/view/catalog/product_form/after,action-extension/module/productmarkedfield/eventProductFormAfter, полный путь до файла обработчикаadmin/controller/extension/module/productmarkedfield.phpметодControllerExtensionModuleProductmarkedfield::eventProductFormAfter

  • trigger-catalog/model/checkout/order/addOrderHistory/after,action-extension/module/productmarkedfield/eventaddOrderHistoryAfter, полный путь до файла обработчикаcatalog/controller/extension/module/productmarkedfield.phpметодControllerExtensionModuleProductmarkedfield::eventaddOrderHistoryAfter

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

Код регистрации обработчика событий для OpenCart 2.3 будет выглядеть так:

$this->load->model('extension/event'); //событие "после загрузки формы товара" - для показа дополнительного поля товара (обязательна маркировка или нет)$this->model_extension_event->addEvent(  'productmarkedfield', //название модуля  'admin/view/catalog/product_form/after', //событие   'extension/module/productmarkedfield/eventProductFormAfter' //обработчик

А для OpenCart 3.0 так:

$this->load->model('setting/event'); //событие "после загрузки формы товара" - для показа дополнительного поля товара (обязательна маркировка или нет)$this->model_setting_event->addEvent(  'productmarkedfield',  'admin/view/catalog/product_form/after',  'extension/module/productmarkedfield/eventProductFormAfter'

Если количество обязательных аргументов обработчика события превышает количество передаваемых аргументов роутером, то обработчик не будет запущен!

Количество обязательных аргументов обработчика имеет значение.system/engine/action.php Action::executeпри помощи рефлексии определяет количество необходимых аргументов и если их в обработчике больше чем может передать объектactionтогда ожидаемException:

$reflection = new ReflectionClass($class);         if ($reflection->hasMethod($this->method) && $reflection->getMethod($this->method)->getNumberOfRequiredParameters() <= count($args)) {    return call_user_func_array(array($controller, $this->method), $args);} else {    return new \Exception('Error: Could not call ' . $this->route . '/' . $this->method . '!');}

Удаление обработчиков событий

Методы удаления уже имеют значительную разницу.

В OpenCart 2.3 у моделиextension/eventметодdeleteEventудаляет все обработчики событии модуля по коду(кусок кода из OpenCart 2.3):

public function deleteEvent($code) {    $this->db->query("DELETE FROM `" . DB_PREFIX . "event` WHERE `code` = '" . $this->db->escape($code) . "'");}

OpenCart 3.0 предоставляет немного больше. МетодdeleteEventудаляет обработчик события по его идентификатору, а методdeleteEventByCodeудаляет все обработчики события по коду, какdeleteEventв OpenCart 2.3(кусок кода OpenCart 3.0):

public function deleteEvent($event_id) {    $this->db->query("DELETE FROM `" . DB_PREFIX . "event` WHERE `event_id` = '" . (int)$event_id . "'");} public function deleteEventByCode($code) {    $this->db->query("DELETE FROM `" . DB_PREFIX . "event` WHERE `code` = '" . $this->db->escape($code) . "'");}

Таким образом удаление обработчиков событий для OpenCart 2.3 будет выглядеть так:

$this->load->model('extension/event');$this->model_extension_event->deleteEvent('productmarkedfield');

А для OpenCart 3.0:

$this->load->model('setting/event');$this->model_setting_event->deleteEvent('productmarkedfield');

Итог

Система событий OpenCart достаточна интересна, она позволяет многое и гибко, но не без недостатков. Больше всего смущает тот факт, что для изменения интерфейса(события загрузки представлений)необходимо вручную работать с DOM.

Для понимания содержимого аргументов события нужно изучать исходный код загружаемых файлов, а в случае представлений также необходимо изучать контроллер, который передает в это представление данные. Однако со временем этот факт перерастает из "недостатка в достоинство" раскрывая прелесть движка OpenCart :)

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

Источник

Источник: habr.com
К списку статей
Опубликовано: 24.12.2020 12:19:48
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Php

Программирование

Opencart

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru