CommerCentr
EN
Sign in

Создание модуля для BroCart [OpenCart] 3 версии

#1
Доброго времени, други и подруги!
Сегодня будем рассматривать обращение комрада, который предложил реализовать поиск картинок, используя API от Google, отписав заранее задачи, в стиле ТЗ со скриншотами и описанием, а так же ссылкой на аналогичный модуль, но под версию 2.3. Так же, написанием статьи послужило общение с товарищем, который желает обрести знание в шоп-мейкерстве, частью которого и будет создание модуля по образу и подобию, вернее будет рассмотрено создание каркаса, сама же магия алхимии будет раскрыта в зависимости от задачи и отмашки главнокомандующего, по чьей просьбе я так же создаю эту тему, что бы и другие смогли почитать и обучится.

Цель: Создание модуля для bro 3 версии
Задача модуля: парсинг картинок, используя Google API
Инструменты: прямые руки, хорошее настроение, пол литра киндзмараули
Поехали... :cool:

Как создать модуль для BroCart [OpenCart] 3

  • создание каркаса MVC для админки
  • создание каркаса MVC для витрины (если необходимо)
  • создание установочного файла
  • реализация функционала

STEP 0: Название файлов модуля
Символьное обозначение файлов должно соответствовать базовой функциональности, в нижнем регистре, если есть 2 и более слова, разделяем нижним подчеркиванием
У меня получилось вот такое название: brogsi (BROcart Google Search Images), тоесть все файлы, я буду называть либо brogsi.php либо brogsi.twig либо brogsi.tpl
Согласно паттерну MVC + L:

mvcl.png
Все файлы нам нужно создать согласно их размещению в каталоге.
Начнем с раздела admin:
0.1 Первым делом я начинаю с контроллера (на картинке он обозначен в центре, тоесть по адресу: admin\controller\extension\module\ я добавляю новый файл, под названием brogsi.php
Настройки файла, я буду хранить в базе и файл модели (на рисунке синий круг - справа) я создавать не буду, а создам только файл с переменными (нижний желтый круг на рисунке) для разных языков:
0.2. В бро на данный момент 3 (языка), соответственно в каждом из каталогов:

  • admin\language\en-gb\extension\module\
  • admin\language\uk-ua\extension\module\
  • admin\language\ru-ru\extension\module\

и создаем по файлу brogsi.php с базовыми текстовыми переменными:
Для англ. языка:
<?php
// Heading
$_['heading_title'] = 'BROcart Google Search Images [brogsi]';

// Text
$_['text_extension'] = 'Extensions';
$_['text_success'] = 'Success: You have modified brogsi module!';
$_['text_edit'] = 'Edit Brogsi Module';

// Entry
$_['entry_api'] = 'API key';
$_['entry_type'] = 'ImgType';
$_['entry_size'] = 'ImgSize';
$_['entry_color'] = 'ImgColorType';
$_['entry_status'] = 'Status';

// Error
$_['error_permission'] = 'Warning: You do not have permission to modify brogsi module!';
$_['error_api'] = 'API key required!';

Для укр. файла:
<?php
// Heading
$_['heading_title'] = 'BROcart Google Search Images [brogsi]';

// Text
$_['text_extension'] = 'Доповнення';
$_['text_success'] = 'Вітання: Ви щойно змінили налаштування в модулі!';
$_['text_edit'] = 'Редагувати модуль';

// Entry
$_['entry_api'] = 'API ключ';
$_['entry_type'] = 'Тип малюнку';
$_['entry_size'] = 'Розмір малюнку';
$_['entry_color'] = 'Тип кольору';
$_['entry_status'] = 'Статус';

// Error
$_['error_permission'] = 'Увага: У Вас немає прав для налаштування модуля!';
$_['error_api'] = 'Увага: API ключ є обов\'язковим!';

Ну и для рос. файла:
<?php
// Heading
$_['heading_title'] = 'BROcart Google Search Images [brogsi]';

// Text
$_['text_extension'] = 'Расширения';
$_['text_success'] = 'Поздравляем: Настройки успешно изменены!';
$_['text_edit'] = 'Редактировать модуль';

// Entry
$_['entry_api'] = 'API ключ';
$_['entry_type'] = 'Тип рисунка';
$_['entry_size'] = 'Размер рисунка';
$_['entry_color'] = 'Тип цвета';
$_['entry_status'] = 'Статус';

// Error
$_['error_permission'] = 'Внимание: У Вас нет прав для настроек модуля!';
$_['error_api'] = 'Внимание: API ключ обязателен!';


Теперь осталось добавить файл для отображения (на рисунке MVC это верхний - зелёный круг) в каталоге:
admin\view\template\extension\module\ файл под названием brogsi.twig (так как в брокарте, есть поддержка TPL без конфликтов, то я создамbrogsi.tpl).
Готово, файловый каркас по паттерну MVC готов, осталось теперь реализовать функционал, языковые переменные мы заполнили с самого начала, осталось только контроллер и view файл заполнить кодом\разметкой, ну что ж... поехали ;)
STEP: 1 Файл-контроллер
Напомню, что находится он в разделе admin\controller\extension\module\brogsi.php
Открываем его любимым редактором кода, у каждого он свой, я же предпочту своим, и добавляем первые строки:
Код самого контроллера
<?php
class ControllerExtensionModuleBrogsi extends Controller {
private $error = array();

public function index() {

$this->load->language('extension/module/brogsi');

$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_brogsi', $this->request->post);
$this->session->data['success'] = $this->language->get('text_success');
$this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
}

if (isset($this->error['warning'])) {
$data['error_warning'] = $this->error['warning'];
} else {
$data['error_warning'] = '';
}

$data['breadcrumbs'] = array();

$data['breadcrumbs'][] = array(
'text' => $this->language->get('text_home'),
'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
);

$data['breadcrumbs'][] = array(
'text' => $this->language->get('text_extension'),
'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
);

$data['breadcrumbs'][] = array(
'text' => $this->language->get('heading_title'),
'href' => $this->url->link('extension/module/brogsi', 'user_token=' . $this->session->data['user_token'], true)
);

$data['action'] = $this->url->link('extension/module/brogsi', 'user_token=' . $this->session->data['user_token'], true);

$data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);

if (isset($this->request->post['module_brogsi_api'])) {
$data['module_brogsi_api'] = $this->request->post['module_brogsi_api'];
} else {
$data['module_brogsi_api'] = $this->config->get('module_brogsi_api');
}

if (isset($this->request->post['module_brogsi_type'])) {
$data['module_brogsi_type'] = $this->request->post['module_brogsi_type'];
} else {
$data['module_brogsi_type'] = $this->config->get('module_brogsi_type');
}

$data['file_types'] = array(
'clipart',
'face',
'lineart',
'news',
'photo'
);

if (isset($this->request->post['module_brogsi_size'])) {
$data['module_brogsi_size'] = $this->request->post['module_brogsi_size'];
} else {
$data['module_brogsi_size'] = $this->config->get('module_brogsi_size');
}

$data['file_sizes'] = array(
'icon',
'small',
'medium',
'large',
'xlarge',
'xxlarge',
'huge'
);

if (isset($this->request->post['module_brogsi_color'])) {
$data['module_brogsi_color'] = $this->request->post['module_brogsi_color'];
} else {
$data['module_brogsi_color'] = $this->config->get('module_brogsi_color');
}

$data['file_colors'] = array(
'mono',
'gray',
'color'
);

if (isset($this->request->post['module_brogsi_status'])) {
$data['module_brogsi_status'] = $this->request->post['module_brogsi_status'];
} else {
$data['module_brogsi_status'] = $this->config->get('module_brogsi_status');
}

$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/brogsi', $data));
}

protected function validate() {
if (!$this->user->hasPermission('modify', 'extension/module/brogsi')) {
$this->error['warning'] = $this->language->get('error_permission');
}
if (empty($this->request->post['module_brogsi_api'])) {
$this->error['warning'] = $this->language->get('error_api');
}
return !$this->error;
}
}
?>



Ну и код для самого view файла:
admin\view\template\extension\module\brogsi.tpl
<?php echo $header; ?><?php echo $column_left; ?>
<div id="content">
<div class="page-header">
<div class="container-fluid">
<div class="pull-right">
<button type="submit" form="form-category" data-toggle="tooltip" title="<?php echo $button_save; ?>" class="btn btn-primary"><i class="fa fa-save"></i></button>
<a href="<?php echo $cancel; ?>" data-toggle="tooltip" title="<?php echo $button_cancel; ?>" class="btn btn-default"><i class="fa fa-reply"></i></a></div>
<h1><?php echo $heading_title; ?></h1>
<ul class="breadcrumb">
<?php foreach ($breadcrumbs as $breadcrumb) { ?>
<li><a href="<?php echo $breadcrumb['href']; ?>"><?php echo $breadcrumb['text']; ?></a></li>
<?php } ?>
</ul>
</div>
</div>
<div class="container-fluid">
<?php if ($error_warning) { ?>
<div class="alert alert-danger"><i class="fa fa-exclamation-circle"></i> <?php echo $error_warning; ?>
<button type="button" class="close" data-dismiss="alert">&times;</button>
</div>
<?php } ?>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><i class="fa fa-pencil"></i> <?php echo $text_edit; ?></h3>
</div>
<div class="panel-body">
<form action="<?php echo $action; ?>" method="post" enctype="multipart/form-data" id="form-category" class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label" for="input-api"><?php echo $entry_api; ?></label>
<div class="col-sm-10">
<input type="text" name="module_brogsi_api" value="<?php echo $module_brogsi_api; ?>" placeholder="<?php echo $entry_api; ?>" id="input-api" class="form-control" />
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="input-type"><?php echo $entry_type; ?></label>
<div class="col-sm-10">
<select name="module_brogsi_type" id="input-type" class="form-control">
<option value="0"><?php echo $text_none; ?></option>
<?php foreach ($file_types as $file_type) { ?>
<?php if ($file_type == $module_brogsi_type) { ?>
<option value="<?php echo $file_type; ?>" selected="selected"><?php echo $file_type; ?></option>
<?php } else { ?>
<option value="<?php echo $file_type; ?>"><?php echo $file_type; ?></option>
<?php } ?>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="input-size"><?php echo $entry_size; ?></label>
<div class="col-sm-10">
<select name="module_brogsi_size" id="input-size" class="form-control">
<option value="0"><?php echo $text_none; ?></option>
<?php foreach ($file_sizes as $file_size) { ?>
<?php if ($file_size == $module_brogsi_size) { ?>
<option value="<?php echo $file_size; ?>" selected="selected"><?php echo $file_size; ?></option>
<?php } else { ?>
<option value="<?php echo $file_size; ?>"><?php echo $file_size; ?></option>
<?php } ?>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="input-color"><?php echo $entry_color; ?></label>
<div class="col-sm-10">
<select name="module_brogsi_color" id="input-color" class="form-control">
<option value="0"><?php echo $text_none; ?></option>
<?php foreach ($file_colors as $file_color) { ?>
<?php if ($file_color == $module_brogsi_color) { ?>
<option value="<?php echo $file_color; ?>" selected="selected"><?php echo $file_color; ?></option>
<?php } else { ?>
<option value="<?php echo $file_color; ?>"><?php echo $file_color; ?></option>
<?php } ?>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="input-status"><?php echo $entry_status; ?></label>
<div class="col-sm-10">
<select name="module_brogsi_status" id="input-status" class="form-control">
<?php if ($module_brogsi_status) { ?>
<option value="1" selected="selected"><?php echo $text_enabled; ?></option>
<option value="0"><?php echo $text_disabled; ?></option>
<?php } else { ?>
<option value="1"><?php echo $text_enabled; ?></option>
<option value="0" selected="selected"><?php echo $text_disabled; ?></option>
<?php } ?>
</select>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<?php echo $footer; ?>



В результате чего, получаем вот такой вот простенький модуль:
brogsi.jpg

где у нас под номером 1 - левая колонка, подключенная в контроллере:
$data['column_left'] = $this->load->controller('common/column_left');

номер 2 соответственно - шапка сайта и подвал (котороне нет на рисунке, но по факту он есть):
$data['header'] = $this->load->controller('common/header');

$data['footer'] = $this->load->controller('common/footer');

далее, под номером 3 выводит заголовок <h1>, которого нет в контроллере, но он автоматически взят из файла языка heading_title
номер 4 - хлебные крошки, массив которых в контроллере, мы указали вот так:
$data['breadcrumbs'] = array();

$data['breadcrumbs'][] = array(
'text' => $this->language->get('text_home'),
'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
);

$data['breadcrumbs'][] = array(
'text' => $this->language->get('text_extension'),
'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
);

$data['breadcrumbs'][] = array(
'text' => $this->language->get('heading_title'),
'href' => $this->url->link('extension/module/brogsi', 'user_token=' . $this->session->data['user_token'], true)
);

под номером 5 - выводится ошибка, которую нужно указать, если какое то из полей обязательно к заполнению и, что вполне логично, без которого наш модуль не сможет корректно парсить картинки. В контроллере, мы инициализировали переменную с ошибкой сразу в начале класса (перед функцией index):
private $error = array();
далее логика вывода в самом контроллере:
if (isset($this->error['warning'])) {
$data['error_warning'] = $this->error['warning'];
} else {
$data['error_warning'] = '';
}

а добавляется она в момент проверки данных, когда происходит нажатие на иконку "сохранить" в методе $this->validate():
if (empty($this->request->post['module_brogsi_api'])) {
$this->error['warning'] = $this->language->get('error_api');
}

ну и выводится она во view файле в переменной:
<?php if ($error_warning) { ?>
<div class="alert alert-danger"><i class="fa fa-exclamation-circle"></i> <?php echo $error_warning; ?>
<button type="button" class="close" data-dismiss="alert">&times;</button>
</div>
<?php } ?>

далее, под номером 6 у нас выводит само поле, для ввода ключа API, которое мы указали в контроллере:
if (isset($this->request->post['module_brogsi_api'])) {
$data['module_brogsi_api'] = $this->request->post['module_brogsi_api'];
} else {
$data['module_brogsi_api'] = $this->config->get('module_brogsi_api');
}

под номером 7 у нас указаный статичный массив параметров, который мы взяли отсюда и в контроллере указали его так:
$data['file_colors'] = array(
'mono',
'gray',
'color'
);

ну и наконец, под 8-м номером у нас сам статус модуля, который может быть показан так же и на странице со списком модулей:
if (isset($this->request->post['module_brogsi_status'])) {
$data['module_brogsi_status'] = $this->request->post['module_brogsi_status'];
} else {
$data['module_brogsi_status'] = $this->config->get('module_brogsi_status');
}

Вот так вот создан модуль (простенький и с минимумом функционала, сохраняет базовые параметры в настройки и может быть получен в любом месте сайта, архив с файлами приложу к скачиванию, надеюсь понравилось, остальную магию по парсеру раскрою, еслим получу фидбэк от пользователей, что материал понравился и есть желание продолжить вникать в азы кодинга под бро или опенкарт, всем приятного вечера booboo
Enjoy, bro!Вкладення: mvcl.png · brogsi.jpg · brogsi.zip
#2
Хороший мануал, спасибо, давно не встречал такого детального описания, хотелось бы отдельно узнать, как данные попадают из базы - в контроллер, а из него - во view файл, как передаются обратно, если можно на примере этого же простенького модуля, расписать от момента создания класса, зачем он имеет такое название, заканчивая:
$this->response->setOutput($this->load->view('extension/module/brogsi', $data));

С описанием, что это, как срабатывает, что выводит в итоге.
Так же приглашаю участников, кому интересно разобрать все моменты, которые были до сих пор непонятны, и вызывали отложить разработку на "когда-нибудь".
#3
прошу мне отдать локализацию 2-х языков
по настройкам прошу рассмотреть возможность указания нескольких апи у указанием лимита на каждый ключ, на тот случай если гугль изменит лимит на запросы или будут докуплены дополнительные запросы, ну и при достижении лимита переходим на следующий апи
ну и наверное я бы вынес содержимое масивов
file_sizes
file_types
file_color
вынес в языковые файлы
#4
по нескольким апи - понял, помудрую, касательно выноса параметров в языковые переменные, то разве дополнив, например вот так:
$data['file_types'] = array(
'clipart' => $this->language->get('text_clipart'),
'face' => $this->language->get('text_face'),
'lineart' => $this->language->get('text_lineart'),
'news' => $this->language->get('text_news'),
'photo' => $this->language->get('text_photo')
);

далее, во въюшке, я переберу как ключ\значение, главное подобавлять параметры - в языковые файлы.
#5
ALarikпо нескольким апи - понял, помудрую,

через запятую
Получил отлуп - пометил - юзай другой
#6
Спасибо за материал.:)
#7
Bro-127278Спасибо за материал.:)

рад что понравилось, задавайте вопросы, попробуемс ответить, если вдруг чего не так поедет
#8
ALarikрад что понравилось, задавайте вопросы, попробуемс ответить, если вдруг чего не так поедет

Артур, как с вами можно связаться? на сайте не получается пройти полностью регистрацию. Хотим узнать стоимость разработке модуля. Не можем найти специалиста.
#9
Abalabкак с вами можно связаться?

здравствуйте, можно по скайпу, можно в телеграме, или оставьте свои данные, я свяжусь с вами. но меня больше всего интересует момент с:
Abalabна сайте не получается пройти полностью регистрацию

напишите в 2-х словах, что не так, мы для пользователей стараемся, одновременно спамеров хотим отсеять.
#10
Добрый день. очень хорошая статья . подскажите есть ли статья о написании модуля для опенкарт 3. rest api или api синоризации. А именно выгрузка товаров с опенкарт на площадки или на другой магазин. очень буду благодарен.
#11
Доброго дня, саме по rest api потрібно деталізовано розуміти, під що саме писати, які методи потрібні, тощо. а щодо вивантажень, то оптимальніше за все, це зробити файл-фід, по типу як файл sitemap.xml, тільки розширений і видавати в такому файлі потрібну інформацію, а на іншому магазині - парсити це все.
#12
BacoДоброго дня, саме по rest api потрібно деталізовано розуміти, під що саме писати, які методи потрібні, тощо. а щодо вивантажень, то оптимальніше за все, це зробити файл-фід, по типу як файл sitemap.xml, тільки розширений і видавати в такому файлі потрібну інформацію, а на іншому магазині - парсити це все.

Нет не подходит фид. На том маркетплейсе своя документация api
#13
Доброго часу, наразі, оптимальним рішенням ось таке вважаю: UNIXml Pro вигрузки в маркетплейси
#14
nsklitalНа том маркетплейсе своя документация api

так, а в чому тоді питання?
#15
SlaSoftтак, а в чому тоді питання?

потрібно під їхні теги вивантажувати фіди, наскільки прочитав між рядків
#16
або по АРІ выддавати, типу cURL, але ключі там теж свої будуть, тому рівноцінне тегам
#17
nsklitalДобрый день. очень хорошая статья . подскажите есть ли статья о написании модуля для опенкарт 3. rest api или api синоризации. А именно выгрузка товаров с опенкарт на площадки или на другой магазин. очень буду благодарен.

для конструктивної відповіді, потрібно дати приклад АРІ, під котру писати рішення, бо в у відносному %, різними будуть key->value, як такого, немає стандартизовано єдиного реєстру методів (action) та параметрів, як ключів, REST – це стиль архітектури АРІ для розподілених систем, правила взаємодії, так би мовити. ось наприклад instagram API, а ось стаття по REST API для розуміння.

Anyone can read. To post and vote, sign in.