Установка модуля, страница настройки. OpenCart

22.01.2021

При разработке модуля, для новой неизвестной мне CMS, всегда хотелось сначала сделать страницу настроек разрабатываемого модуля, чтобы провести быстрый экскурс в ядро движка и на основе примеров из соседних модулей ознакомится с кодом.

Однако, порой при разборе встроенных или сторонних модулей (почему-то не нашелся внятный ман для OpenCart по этой теме), не удается изъть код, который решает некоторые, казалось бы, элементарные вещи, на самой ранней стадии разработки модуля.

Именно этим мы и займемся в данной статье, ну хотя бы попытаемся :)

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

Перевод

Создадим файл перевода admin/language/ru-ru/extension/module/mymodulesettings.php в который помещаем минимально необходимый набор переводов:

•••
php
$_['doc_title'] = 'Мой модуль с настройками'; $_['heading_title'] = 'Мой модуль с настройками'; $_['settings_success'] = 'Настройки успешно изменены!'; $_['settings_edit'] = 'Настройки модуля'; $_['entry_setting1'] = 'Введите настройку 1'; $_['entry_setting2'] = 'Введите настройку 2';

Без ключа heading_title модуль появится в списке модулей с неправильным именем

Контроллер

Создать контроллер модуля страницы настроек admin/controller/extension/module/mymodulesettings.php в котором должны быть методы:

  • index - вывод страницы настроек, обработка их сохранения
  • validate - для валидации настроек из метода index
  • install - установка модуля
  • uninstall - деинсталяция/удаление (из системы движка) модуля
Примерно так выглядит шаблон контроллера модуля без реализации:
•••
php
class ControllerExtensionModuleMyModuleSettings extends Controller { //страница настроек модуля public function index() {} //установка модуля public function install() {} //деинсталяция модуля public function uninstall() {} //валидация настроек модуля protected function validate() {} //линейный массив с ошибками private $m_aErrors = []; };

Страница настроек модуля

Метод контроллера позволяющий вывести интерфейс страницы настроек (метод по умолчанию):

•••
php
public function index() { ... }

Метод сам по себе не выведет интерфейс страницы, это нужно прописать!

Помещаем в этот метод код. Осуществляем первичные действия:

•••
php
//загрузка файла перевода модуля $this->load->language('extension/module/mymodulesettings'); //установка title страницы $this->document->setTitle($this->language->get('doc_title')); //загрузка модели настроек $this->load->model('setting/setting'); //создаем пустой массив, позже заполним его данными для шаблона $data = [];

Теперь, если текущий метод POST, тогда нужно обработать входящие данные и сохранить настройки модуля если проверка корректности данных прошла успешно (если нет, то останутся старые данные):

•••
php
if($this->request->server['REQUEST_METHOD'] == 'POST') { //если валидация прошла успешно if($this->validate()) { //сохранение настроек модуля $this->model_setting_setting->editSetting('mymodulesettings', $this->request->post); //записываем в сессию статус успеха сохранения настроек $this->session->data['settings_success'] = $this->language->get('settings_success'); } //валидация закончилась ошибкой, запишем информацию об этом в сессию else $this->session->data['settings_error'] = $this->m_aErrors; //перенаправляем на страницу настроек модуля $this->response->redirect($this->url->link('extension/module/mymodulesettings', 'token=' . $this->session->data['token'] . '&type=module', true)); }

Внимательный читатель заметит что мы что-то там записываем в сессию.

Нам как-то надо иметь информацию о статусе сохранения (удалось или нет) и показывать ее в интерфейсе. А у нас тут перенаправление при сохранении (оно и понятно, ведь нам нужно сбросить текущий метод запроса страницы, а он сбрасывается редиректом, если не сбросить то при обновлении страницы на сервер будет отправляться POST запрос). Самый простой вариант, который пришел на ум это сохранять статус в сессию (вообще-то не статус, а текст статуса), а при показе страницы показывать статус и удалять его из сессии.

Статус мы уже сохранили в сессию ранее, теперь обработаем сохраненные данные и вставим в массив для шаблона то, что нам нужно:

•••
php
//если было успешное изменение настроек - показываем сообщение и удаляем из сессии чтобы больше не показывать if(array_key_exists("settings_success", $this->session->data)) { $data['settings_success'] = $this->language->get('settings_success'); unset($this->session->data["settings_success"]); } else $data['settings_success'] = false; //если есть ошибки - показываем и удаляем из сессии чтобы больше не показывать if(array_key_exists("settings_error", $this->session->data)) { $data['error_warning'] = implode("<br/>", $this->session->data["settings_error"]); unset($this->session->data["settings_error"]); } else $data['error_warning'] = false;

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

Один из вариантов обработки статуса сохранения настроек - это сохранение его в сессии и удаление при первом обращении

Далее ...

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

•••
php
$this->request->post["mymodulesettings_readonly"] = $this->config->get('mymodulesettings_readonly');

Теперь нужно показать страницу с формой редактирования настроек модуля. Продолжаем заполнять массив данными для шаблона представления.

Установка основных данных для страницы редактирования модуля в админке:

•••
php
//загрузка представления головной части страницы $data['header'] = $this->load->controller('common/header'); //загрузка сайдбара $data['column_left'] = $this->load->controller('common/column_left'); //загрузка подвала админки $data['footer'] = $this->load->controller('common/footer'); //заголовок h1 (но не title) $data['heading_title'] = $this->language->get('heading_title'); $data['button_save'] = $this->language->get('button_save'); $data['settings_edit'] = $this->language->get('settings_edit'); //плейхолдеры для настроек $data['entry_setting1'] = $this->language->get('entry_setting1'); $data['entry_setting2'] = $this->language->get('entry_setting2');

Теперь настройки модуля:

•••
php
//получаем массив настроек модуля (ранее мы загружали модель setting/setting) $aModuleInfo = $this->model_setting_setting->getSetting("mymodulesettings"); $data['mymodulesettings_setting1'] = $aModuleInfo["mymodulesettings_setting1"]; $data['mymodulesettings_setting2'] = $aModuleInfo["mymodulesettings_setting2"]; //...

Еще нужны хлебные крошки:

•••
php
$data['breadcrumbs'] = []; $data['breadcrumbs'][] = [ 'text' => $this->language->get('text_home'), 'href' => $this->url->link('common/dashboard', 'token=' . $this->session->data['token'], true) ]; $data['breadcrumbs'][] = [ 'text' => $this->language->get('text_extension'), 'href' => $this->url->link('marketplace/extension', 'token=' . $this->session->data['token'] . '&type=module', true) ]; $data['breadcrumbs'][] = [ 'text' => $this->language->get('heading_title'), 'href' => $this->url->link('extension/module/mymodulesettings', 'token=' . $this->session->data['token'], true) ];

В заключение нужно еще добавить ссылку на отправку формы редактирования настроек нашего модуля:

•••
php
if (!array_key_exists('module_id', $this->request->get)) { $data['action'] = $this->url->link('extension/module/mymodulesettings', 'token=' . $this->session->data['token'], true); } else { $data['action'] = $this->url->link('extension/module/mymodulesettings', 'token=' . $this->session->data['token'] . '&module_id=' . $this->request->get['module_id'], true); }

И наконец - все это отправляем в шаблон:

•••
php
$this->response->setOutput($this->load->view('extension/module/mymodulesettings', $data));

Проверка настроек

Метод валидации просто проверяет все входящие данные:

•••
php
protected function validate() { //... return true; }

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

В случае возникновения ошибок метод validate записывает сообщения об ошибках в массив $this->m_aErrors.

Установка модуля

•••
php
public function install() { //... }

В данном методе нам нужно создать настройки по умолчанию:

•••
php
$this->load->model('setting/setting'); $this->model_setting_setting->editSetting('mymodulesettings', [ 'mymodulesettings_setting1' => '', 'mymodulesettings_setting2' => '', 'mymodulesettings_readonly' => 'dsgf5' ]);

Имена параметров настроек должны иметь префикс имени модуля, например modulename_settingname

Если нужно поработать с базой данных, то внутри каждого контроллера есть объект класса DB расположенного по пути system/library/db.php (а по пути system/library/db/ лежат классы-адапторы для конкретной низкой реализации), который доступен как $this->db.

У этого объекта нас интересуют следующие методы:

  • query($sql, $params = array()) - выполнить SQL запрос в переменной sql, подставив параметры из массива params
  • escape($value) - экранирует строку запроса value
  • countAffected() - возвращает количество строк затронутых последним запросом
  • getLastId() - возвращает последний вставленный ID
  • connected() - установлено ли соединение с БД

Для детального разбора, знакомым с pdo можно посмотреть файл system/library/db/mpdo.php.

Пример работы с БД можно взять из статьи где мы разбирали создание дополнительного поля в карточке товара.

В методе install могут устанавливаться обработчики событий.

Удаление модуля

Здесь можно удалить настройки модуля:

•••
php
$this->load->model('setting/setting'); $this->model_setting_setting->deleteSetting('mymodulesettings');

А также произвести нужные манипуляция с базой данных через тот же объект $this->db.

В методе uninstall должны удаляться все обработчики событий установленные этим модулем.

Представление

А теперь посмотрим как сделаны другие файлы представления из директории admin/view/template/extension/module/ и частично возьмем оттуда код шаблона.

В OpenCart 2.3 используется шаблонизация на основе php кода в файле шаблона (.tpl), а в OpenCart 3.0 появился Twig

Создаем файл admin/view/template/extension/module/mymodulesettings.tpl (не забываем что .tpl для OpenCart 2.3, а для 3.0 .twig), в который помещаем адаптированный код для нашего модуля подсмотренный в других файлах шаблонов :)

Пропустим здесь этот момент (много html), лучше посмотреть файл в репозитории.

Итог

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

Теперь можно двигаться дальше ...