Реализация событий в Opencart 2.3, 3.x, 4.x
Всем привет, дорогие друзья!
Статья предназначена для разработчиков дополнений. Пишу без воды, сухо и по делу. Немного истории, нюансов, список аргументов для обработчиков событий, также приведу пример простой и понятной реализации большого кол-ва событий в ваших дополнениях для OpenCart 2.3, 3.x, 4.x (скачать примеры модулей можно будет в конце статьи).
Как все начиналось
Для изменения кодовой базы движка нам всегда нужно было либо вмешиваться в код, либо использовать vqmod. Использование vqmod порождало проблемы при работе нескольких модификаторов с одним участком кода, а также другие, касающиеся поддержки дополнений. Использование хуков помогло бы решить часть из них. Вспоминаю первую, известную мне реализацию «Override Engine» (2012 г), а также тему «hook pre render Идея и примерная реализация«. Начиная с версии 2.0 (2014 г) в движке появился первый встроенный механизм событий (хуков), а концепция vqmod была реализована в самом движке и получила название ocmod. С версии 2.2 (2016 г) в событиях изменились пути триггеров, они стали аналогичны роутам. В версии 3.x (2017 г) механизм событий и ocmod обошлись без существенных изменений. А с версии 4.x поддержки ocmod больше не будет.
«Embrace, extend and extinguish«. Однако, у нас всегда будет vqmod.
Зачем нужны события?
События позволяют запускать пользовательские функции до/после вызова какой-либо функции в парадигме MVCL+Config+Library для изменения входных/выходных данных. По задумке мэйнтэйнера движка они должны заменить vqmod/ocmod.
P.S. Я уже переписал некоторые старые модули с использованием событий. Например, раньше модуль «Персонализованные шаблоны» с помощью ocmod внедрялся в код основных разделов каталога и подменял их шаблоны, а теперь его брат «Custom templates Pro» подменять любой шаблон в движке. Так что события очень хорошо решают некоторые типы задач.
Нюансы при использовании событий
- События могут быть добавлены только из контроллеров админки. Удобнее всего делать это при установке модуля, в функции install.
- Пути всех триггеров начинаются с названия нужного раздела, admin, catalog или library. Разделы admin и catalog содержат controller, view, language и config.
- В путях триггеров можно использовать знак «*», чтобы назначать триггеры по маске. Например, catalog/view/*/template/common/header/after.
- Для изменения данных в обработчиках событий config и language используйте $this->config->set(), $this->language->set(), соответственно.
- Данные полученные из обработчиков событий можно сохранять внутри класса и использовать их в других обработчиках, которые запускаются позднее.
Для редактирования событий из админки используйте «Event Manager» или adminer ([сtrl+click], для быстрого редактирования записи).
Нюансы для разных версий движка
- Код события для версии 2.3 должен иметь длину не более 32 символов.
- Для версий 3.x и 4.x не более 64. В версии 2.3 событиями (регистрация, удаление и т.д.) занимается модель extension/event, у 3.x и 4.x setting/event.
- В версии 2.3 в разделе catalog пути триггеров представлений (view) before/after будут отличаться. Например, catalog/view/common/header/before, catalog/view/default/template/common/header/after. Это связано использованием шаблонов оформления в разделе catalog.
- С версии 3.x добавлен порядок сортировки событий.
- С версии 4.x у каждого события должен быть description.
- Триггеры для библиотек (library) доступны только с версии 4.x.
Передаваемые аргументы
В обработчики событий аргументы передаются по ссылке. Т.е. можно менять значения аргументов не заботясь о передаче результата куда-либо еще.
2.3 | controller | model | view | language | config | |
before | $route, $data | $route, $args | $route, $data, $output | $route | $route | |
after | $route, $data, $output | $route, $args, $output | $route, $data, $output | $route, $output | $route | |
3.x | controller | model | view | language | config | |
before | $route, $args | $route, $args | $route, $data, $code | $route, $key | $route | |
after | $route, $data, $output | $route, $args, $output | $route, $data, $output | $route, $key, $output | $route | |
4.x | controller | model | view | language | config | library |
before | $route, $args | $route, $args | $route, $data, $code | $route, $prefix, $code | $route | $route, $args |
after | $route, $data, $output | $route, $args, $output | $route, $data, $output | $route, $prefix, $code, $data | $route, $data | $route, $args |
Возвращаемые значения
Помимо изменения данных через аргументы, обработчики событий также могут возвращать значения, используя return. Например, если обработчик события controller/common/home/before вернет через return сгенерированный html код, то весь вывод контроллера common/header будет заменен им, а сам контроллер common/header не будет выполнен, но запустится событие after. Т.е. можно подменять данные выполнения функций без их выполнения.
2.3 | controller | model | view |
before | mixed | mixed | string |
after | mixed | mixed | string |
3.x | controller | model | view |
before | mixed | mixed | string |
after | mixed | mixed | string |
4.x | controller | model | view |
before | mixed | ||
after | mixed |
Простая и понятная реализация
<?php class ControllerExtensionModuleSample extends Controller { public function install() { $this->checkEvent(); } public function uninstall() { $this->removeEvent(); } public function index() { # code } private $_events = [ [ 'code' => 'sample_394beb748918d3ce260756703', 'trigger' => 'admin/controller/design/layout/before', 'action' => '/eventControllerDesignLayoutBefore' ], [ 'code' => 'sample_7a2b613ccb07a2c0e9c8cb844', 'trigger' => 'admin/view/design/layout_list/after', 'action' => '/eventViewDesignLayoutListAfter' ], [ 'code' => 'sample_968b25d7939ec60e0008d670c', 'trigger' => 'admin/model/design/layout/getLayouts/after', 'action' => '/eventModelDesignLayoutGetLayoutsAfter' ], [ 'code' => 'sample_172e1deab50793d6c4bec3b42', 'trigger' => 'catalog/model/design/layout/getLayoutModules/after', 'action' => '/filter' ] ]; public function eventControllerDesignLayoutBefore(&$route, &$args) { # code } public function eventViewDesignLayoutListAfter(&$route, &$data, &$output) { # code } public function eventModelDesignLayoutgetLayoutsAfter(&$route, &$args, &$output) { # code } private function checkEvent() { $this->load->model('extension/event'); foreach($this->_events as $event) { if(!$result = $this->model_extension_event->getEvent($event['code'], $event['trigger'], 'extension/module/sample' . $event['action'])) { $this->model_extension_event->addEvent($event['code'], $event['trigger'], 'extension/module/sample' . $event['action']); } } } private function removeEvent() { $this->load->model('extension/event'); foreach($this->_events as $event) { $this->model_extension_event->deleteEvent($event['code']); } } }
Примеры
2.3
3.x
4.x
0 Комментариев
Рекомендуемые комментарии
Комментариев нет