Разработка страницы настроек модуля OpenCart

Категория: Программирование | Скилл: opencart , php | Дата: 22.01.2021
При разработке модуля, для новой неизвестной мне CMS, всегда хотелось сначала сделать страницу настроек разрабатываемого модуля, чтобы провести быстрый экскурс в ядро движка и на основе примеров из соседних модулей ознакомится с кодом.

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

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

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

Перевод

Создадим файл перевода admin/language/ru-ru/extension/module/mymodulesettings.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 - деинсталяция/удаление (из системы движка) модуля
Примерно так выглядит шаблон контроллера модуля без реализации:
class ControllerExtensionModuleMyModuleSettings extends Controller
{
	//страница настроек модуля
	public function index() {}
	
	//установка модуля
	public function install() {}
	
	//деинсталяция модуля
	public function uninstall() {}
	
	//валидация настроек модуля
	protected function validate() {}
	
	//линейный массив с ошибками
	private $m_aErrors = [];
};

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

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

public function index()
{
...
}

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

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

//загрузка файла перевода модуля
$this->load->language('extension/module/mymodulesettings');

//установка title страницы
$this->document->setTitle($this->language->get('doc_title'));

//загрузка модели настроек
$this->load->model('setting/setting');

//создаем пустой массив, позже заполним его данными для шаблона
$data = [];

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

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 запрос). Самый простой вариант, который пришел на ум это сохранять статус в сессию (вообще-то не статус, а текст статуса), а при показе страницы показывать статус и удалять его из сессии.

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

//если было успешное изменение настроек - показываем сообщение и удаляем из сессии чтобы больше не показывать
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;

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

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

Далее ...

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

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

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

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

//загрузка представления головной части страницы
$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');

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

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

$data['mymodulesettings_setting1'] = $aModuleInfo["mymodulesettings_setting1"];
$data['mymodulesettings_setting2'] = $aModuleInfo["mymodulesettings_setting2"];
//...

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

$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)
];

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

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);
}

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

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

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

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

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

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

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

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

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

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

$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 могут устанавливаться обработчики событий.

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

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

$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. Не то чтобы это уберполезная инфа, но как минимум она поможет сократить время входа в понимание самых основ движка.

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