Свой модуль для OpenCart: приёмы из практики
Разбираем разработку модуля для OpenCart 3.0.3.8: структуру файлов, настройки в админке, вывод на витрине, права доступа и ошибки, из-за которых расширение ломается после обновления.

Модуль для OpenCart 3.0.3.8 можно собрать без правок ядра: отдельные контроллер, языковые файлы, шаблон и сохранение настроек через стандартную модель. На практике проблемы возникают не в самом выводе блока, а в маршрутах, правах администратора, именах ключей конфигурации и смешивании кода для OpenCart 3 и 4.
Сначала выберите границу модуля
Модуль подходит, когда функциональность нужно включать и настраивать из админки, а результат выводится в одной или нескольких позициях темы. Например, это блок с гарантией на странице товара, баннер в колонке или уведомление о бесплатной доставке.
Не стоит превращать модуль в место для всей бизнес-логики магазина. Расчёт цены, изменение заказа и интеграцию с внешним сервисом лучше отделить в модель или отдельное расширение. Контроллер должен принять запрос, проверить права, передать данные и выбрать шаблон.
Главный практический приём: сначала зафиксируйте один маршрут, один ключ настроек и один шаблон. Рабочий маленький модуль проще расширить, чем отлаживать универсальный файл на тысячу строк.
Термин. Маршрут — строка, по которой OpenCart находит контроллер, например extension/module/stock_label. В OpenCart 3 маршрут не совпадает с URL на витрине: он используется внутри вызова контроллера.
Какой подход выбрать
| Подход | Когда использовать | Цена решения |
|---|---|---|
| Модуль | Нужен настраиваемый блок в макете | Нужно создать админскую форму, права и шаблон |
| Событие | Нужно реагировать на действие OpenCart без отдельного блока | Сложнее отлаживать порядок вызовов и совместимость |
| OCMod | Нужно изменить поведение существующего контроллера или шаблона | Обновления и другие модификаторы могут изменить результат |
Для первого собственного расширения выбирайте модуль. Событие имеет смысл, когда результат должен появляться после конкретного действия системы, а не в позиции макета. OCMod оставляйте для случаев, когда штатной точки расширения нет. Править файлы ядра не стоит: обновление OpenCart затрёт изменения.
Минимальная структура модуля
Ниже пример для OpenCart 3.0.3.8. Имена stock_label и module_stock_label должны быть одинаковыми во всех местах. В архиве расширения обычно лежит каталог upload, внутри которого сохраняется структура OpenCart.
upload/
├── admin/
│ ├── controller/extension/module/stock_label.php
│ ├── language/en-gb/extension/module/stock_label.php
│ └── view/template/extension/module/stock_label.twig
└── catalog/
├── controller/extension/module/stock_label.php
├── language/en-gb/extension/module/stock_label.php
└── view/theme/default/template/extension/module/stock_label.twig
Если магазин работает на другом языке, добавьте языковой файл в соответствующий каталог. Путь темы на витрине может отличаться от default. Проверяйте активную тему в настройках магазина, иначе контроллер найдёт данные, но шаблон не откроется.
Админка: настройки без лишней логики
Административный контроллер отвечает за форму и сохранение значений. Для этого используется стандартная модель настроек. Пример ниже сохраняет статус модуля и короткий текст.
<?php
class ControllerExtensionModuleStockLabel extends Controller {
private $error = array();
public function index() {
$this->load->language('extension/module/stock_label');
$this->document->setTitle($this->language->get('heading_title'));
$this->load->model('setting/setting');
if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
$this->model_setting_setting->editSetting('module_stock_label', $this->request->post);
$this->session->data['success'] = $this->language->get('text_success');
$this->response->redirect($this->url->link('extension/extension', 'user_token=' . $this->session->data['user_token'], true));
}
$data['action'] = $this->url->link('extension/module/stock_label', 'user_token=' . $this->session->data['user_token'], true);
$data['cancel'] = $this->url->link('extension/extension', 'user_token=' . $this->session->data['user_token'], true);
$data['module_stock_label_status'] = $this->request->post['module_stock_label_status'] ?? $this->config->get('module_stock_label_status');
$data['module_stock_label_text'] = $this->request->post['module_stock_label_text'] ?? $this->config->get('module_stock_label_text');
$data['header'] = $this->load->controller('common/header');
$data['column_left'] = $this->load->controller('common/column_left');
$data['footer'] = $this->load->controller('common/footer');
$this->response->setOutput($this->load->view('extension/module/stock_label', $data));
}
protected function validate() {
if (!$this->user->hasPermission('modify', 'extension/module/stock_label')) {
$this->error['warning'] = $this->language->get('error_permission');
}
return !$this->error;
}
}
Оператор ?? требует PHP 7.0 или новее. Если проект использует более старый PHP, замените его на проверку через isset. Сам OpenCart 3.0.3.8 рассчитан на окружение, совместимость которого нужно сверять с версией PHP и установленными расширениями.
Язык и шаблон формы
Языковой файл не должен содержать текст, разбросанный по контроллеру. Минимальный вариант:
<?php
$_['heading_title'] = 'Stock label';
$_['text_success'] = 'Settings saved';
$_['error_permission'] = 'Warning: You do not have permission to modify this module';
В реальном магазине добавьте русскую локализацию в каталог языка магазина. Шаблон административной формы получает переменные из контроллера. Для переключателя статуса важно отправлять значение 1 или 0, а не произвольный текст.
<form action="{{ action }}" method="post">
<label>Текст блока</label>
<input type="text" name="module_stock_label_text" value="{{ module_stock_label_text }}" />
<label>
<input type="checkbox" name="module_stock_label_status" value="1" {% if module_stock_label_status %}checked{% endif %} />
Включить модуль
</label>
<button type="submit">Сохранить</button>
</form>
После сохранения проверьте не только сообщение об успехе, но и запись в таблице настроек с префиксом магазина. Не меняйте ключ на stock_label_status только в одном файле: контроллер, шаблон и вызов конфигурации должны использовать одну схему имён.
Витрина: контроллер должен быть коротким
Контроллер витрины получает настройки через аргумент $setting, когда модуль добавлен в макет. Он возвращает HTML, а при отключённом статусе ничего не выводит.
<?php
class ControllerExtensionModuleStockLabel extends Controller {
public function index($setting) {
if (!$this->config->get('module_stock_label_status')) {
return;
}
$data['text'] = $this->config->get('module_stock_label_text');
return $this->load->view('extension/module/stock_label', $data);
}
}
Шаблон витрины:
<div class="stock-label">
{{ text }}
</div>
Не вставляйте в Twig необработанные значения из запроса. Данные, которые проходят через стандартный механизм Twig OpenCart, экранируются при обычном выводе. Для HTML из настройки нужна отдельная, осознанная политика: иначе поле администратора может стать источником XSS.
Настройки, права и размещение в макете
Одна из частых ошибок — сохранить модуль, но не выдать группе администратора разрешение на маршрут. В OpenCart 3 разрешения бывают на просмотр и изменение. Для административного контроллера нужны обе операции:
accessдля открытия страницы настроек;modifyдля сохранения формы;- маршрут должен точно совпадать с
extension/module/stock_label.
После установки расширения добавьте его в разделе макетов: выберите нужный макет, позицию и статус. Если контроллер вызывается напрямую, но в макете нет экземпляра модуля, переменная $setting может оказаться пустой. Не смешивайте два сценария: глобальная настройка модуля и настройки конкретного экземпляра в макете.
Что обычно ломается
Белая страница или ошибка 500
Чаще всего это синтаксическая ошибка PHP, несовместимый оператор или неверное имя класса. Смотрите журнал ошибок PHP и storage/logs/error.log, а не пытайтесь угадывать по пустому экрану.
Сохранение возвращает в админку, но значение не меняется
Проверьте имя поля в Twig, ключ в editSetting и чтение через $this->config->get. Если форма отправляет module_stock_label_text, именно этот ключ должен сохраняться и читаться.
В админке нет пункта или появляется ошибка permission denied
Проверьте разрешения группы администратора. У пользователя должны быть доступ и изменение для того же маршрута. После установки расширения иногда требуется выйти из админки и войти снова.
Шаблон не найден
Сверьте имя в $this->load->view и физический путь в view/template. Для витрины проверьте активную тему. Очистите кеш модификаторов и кеш темы после установки файлов.
Модуль работает в OpenCart 3, но не переносится в OpenCart 4
Это ожидаемо: в OpenCart 4 изменились структура расширений, маршруты и способ упаковки. Не переносите каталог копированием. Сверьте текущую документацию и каркас расширения для конкретной версии.
Когда вместо модуля нужен другой механизм
Если задача — добавить текстовый блок, модуль проще и безопаснее. Если нужно менять данные заказа перед сохранением, смотрите события. Если требуется встроиться в существующую страницу, а готового события нет, применяйте OCMod и документируйте точку замены.
Инвалидация кеша, события и перенос модуля на OpenCart 4 — отдельные темы. Здесь важнее не смешивать их с базовой структурой расширения.
Что сделать после первого запуска
Создайте тестовый товар, включите модуль только в одном макете и проверьте четыре сценария: включение, отключение, пустое значение и отсутствие прав у администратора. Затем установите расширение на копию магазина, очистите кеш и проверьте журнал после обновления темы.
Следующий полезный шаг — добавить удаление собственных настроек при деинсталляции и автоматическую проверку версии PHP. Перед публикацией архива сравните структуру с документацией OpenCart 3.0.3.8 и отдельно подготовьте ветку для OpenCart 4, а не обещайте универсальную совместимость.
Создатель презентаций: Структура и текст каждого слайда: заголовки-утверждения, одна мысль на слайд, заметки докладчику.
Спросить за 150 ₽Источники: OpenCart Documentation: Developer — Modules, OpenCart 3.0.x source code
Частые вопросы
Можно ли использовать этот код в OpenCart 4?
Почему модуль установлен, но его нет в макете?
Что делать, если я не вижу страницу настроек?
Почему после сохранения настройка снова выключена?
Можно ли менять файлы ядра вместо создания модуля?
Что проверять, если на витрине появляется белая страница?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.