Содержание
Разрабатывая модуль для OpenCart, возникла необходимость сделать дополнительное поле в карточке товара, которое должно быть видно только в админке. Это должно быть поле с логическим значением "маркирован товар или нет". Вот так в итоге:
Недолго поковыряв админку выяснил, что из коробки OpenCart не поддерживает произвольные поля, однако есть платные модули, например вот. Значит нужно сделать кастомное поле в OpenCart самому.
Варианты решения задачи:
В коде этого модуля нет настроек, но в другой статье можно почитать о том, как создать страницу настроек модуля.
В общем схема выглядит так:
Основная логика модуля умещается в один файл контроллера admin/controller/extension/module/productmarkedfield.php
(закроем глазки на верстку и sql внутри контроллера).
Для того чтобы модуль отобразился в разделе "Расширений" (чтобы его можно было инсталлировать/деинсталлировать) надо создать языковой файл admin/language/ru-ru/extension/module/productmarkedfield.php
с таким содержимым:
php<?php $_['heading_title'] = 'Кастомное поле в карточке товара "Маркировка"';
В методе install
нужно модифицировать таблицу product
:
php$this->db->query("ALTER TABLE `".DB_PREFIX."product` ADD `marked` TINYINT UNSIGNED NOT NULL DEFAULT '0';");
В ocStore 2.3.x все нормально, но в ocStore 3.0.2.0 при использовании MySQL 8, запрос добавления нового столбца в таблицу заканчивался ошибкой:
Для решения этой проблемы изменим значение по умолчанию для столбца date_available
:
php$this->db->query("ALTER TABLE `".DB_PREFIX."product` CHANGE `date_available` `date_available` DATE NOT NULL;");
Теперь надо добавить обработчики событий, чтобы с их помощью мы могли изменять верстку карточки товара и сохранять значение нашего дополнительного поля (для ocStore 2.3.x):
php$this->load->model('extension/event'); //событие "после загрузки формы товара" - для показа дополнительного поля товара (обязательна маркировка или нет) $this->model_extension_event->addEvent( 'productmarkedfield', //код, в данном случае название модуля 'admin/view/catalog/product_form/after', //событие 'extension/module/productmarkedfield/eventProductFormAfter' //обработчик ); //событие "после редактирования товара" - для сохранения статуса маркировки $this->model_extension_event->addEvent( 'productmarkedfield', 'admin/model/catalog/product/editProduct/after', 'extension/module/productmarkedfield/eventProductEditAfter' );
Для ocStore 3.0.x модель событий загружается таким образом:
php$this->load->model('setting/event');
И вместо объекта model_extension_event
нужно использовать model_setting_event
соответственно.
Выводить наше поле в карточку товара мы будем после загрузки admin/view/template/catalog/product_form.twig
. наш метод будет принимать 3 аргумента:
phppublic function eventProductFormAfter( &$route, &$args, //переданные аргументы в этот шаблон &$output//html верстка страницы )
Нас интересует третий аргумент &$output
, именно его мы и будем модифицировать вставляя туда верстку со значением нашего нового поля.
Для модификации формы нам понадобится Simple HTML DOM, и краткий мануал. Скачиваем и ложим его в system/library
, а в коде подгружаем расположенный внутри класс таим образом (@ чтобы не выводить ошибки, так как оригинальная версия этой библиотеки загружается с некритичными ошибками):
php@$this->load->library('simple_html_dom');
Затем используем регулярные выражения для того чтобы получить id
товара. (Искал другие варианты, но id в нормальном виде в $args
массиве не нашел):
phppreg_match("/product_id=(\d+)/", $args["action"], $aMatch); $idProdict = $aMatch[1];
Загружаем модель описывающую товар и получаем необходимую информацию о товаре (данные из таблиц product
и product_description
):
php$this->load->model('catalog/product'); $aProduct = $this->model_catalog_product->getProduct($idProdict);
Теперь мы имеем доступ к данным товара, среди которых наше дополнительное поле - значение маркировки.
Однако, этот код будет работать только для редактирования уже существующего товара, но при создании нового товара будут ошибки, так как не будет найден id
еще несуществующего товара. Изменим вышеприведенный код:
php$isMarked = false; if(preg_match("/product_id=(\d+)/", $args["action"], $aMatch)) { $idProduct = $aMatch[1]; $this->load->model('catalog/product'); $aProduct = $this->model_catalog_product->getProduct($idProduct); $isMarked = $aProduct["marked"]; }
То есть мы создали новую переменную isMarked
, которая будет хранить в себе значение по умолчанию false
и если удастся найти id
товара, тогда в isMarked
будет записано значение из нашего произвольного поля в карточке товара OpenCart.
Теперь при помощи Simple HTML DOM
найдем вкладку "Данные" и вставим в самое начало наше поле маркировки, предварительно подсмотрев на верстку необходимого gui элемента
в dmin/view/template/catalog/product_form.twig
(в ocStore 2.3.x tpl расширение, и внутри нет Twig):
Код рабочего метода целиком:php$html = str_get_html($output); $html->find('div#tab-data', 0)->innertext = '<div class="form-group"> <label class="col-sm-2 control-label">Маркирован</label> <div class="col-sm-10"> <label class="radio-inline"> <input type="radio" name="marked" value="1" '.($aProduct["marked"] ? 'checked="checked"' : "").'>Да </label> <label class="radio-inline"> <input type="radio" name="marked" value="0" '.(!$aProduct["marked"] ? 'checked="checked"' : "").'>Нет </label> </div> </div>' . $html->find('div#tab-data', 0)->innertext;
phppublic function eventProductFormAfter(&$route, &$args, &$output) { @$this->load->library('simple_html_dom'); $isMarked = false; if(preg_match("/product_id=(\d+)/", $args["action"], $aMatch)) { $idProduct = $aMatch[1]; $this->load->model('catalog/product'); $aProduct = $this->model_catalog_product->getProduct($idProduct); $isMarked = $aProduct["marked"]; } $html = str_get_html($output); $html->find('div#tab-data', 0)->innertext = '<div class="form-group"> <label class="col-sm-2 control-label">Маркирован</label> <div class="col-sm-10"> <label class="radio-inline"> <input type="radio" name="marked" value="1" '.($isMarked ? 'checked="checked"' : "").'>Да </label> <label class="radio-inline"> <input type="radio" name="marked" value="0" '.(!$isMarked ? 'checked="checked"' : "").'>Нет </label> </div> </div>' . $html->find('div#tab-data', 0)->innertext; $output = $html->outertext; }
Для сохранения результатов в момент когда администратор нажимает кнопку "Сохранить", необходимо вручную (с помощью обработчика события) внести изменения в базу данных, так как модель catalog/product
при редактировании товара (ModelCatalogProduct::editProduct
) сохраняет только определенный набор данных, и наше кастомное поле не входит в этот набор.
Для этого мы уже зарегистрировали ранее обработчик события "после редактирования товара":
phppublic function eventProductEditAfter(&$route, &$args) { //в $args[0] лежит id товара $sSql = "UPDATE " . DB_PREFIX . "product SET marked = " . $this->db->escape($args[1]['marked']) . " WHERE product_id = '" . (int)$args[0] . "'"; $this->db->query($sSql); }
При деинсталяции модуля надо удалить столбец marked
из таблицы product
и удалить обработчики событий установленные нашим модулем. Все это делается в методе uninstall
.
Удалить столбец из таблицы товаров:
php$this->db->query("ALTER TABLE `".DB_PREFIX."product` DROP `marked`");
Удалить все сообщения (для ocStore 2.3.x):
php$this->load->model('extension/event'); $this->model_extension_event->deleteEvent('productmarkedfield');
Удалить все сообщения (ocStore 3.0.x):
php$this->load->model('setting/event'); $this->model_setting_event->deleteEventByCode('productmarkedfield');