От автора: разработчику всегда интересно создать что-то свое в каком-нибудь фреймворке, то же самое можно сказать и про плагины для OpenCart. В этой серии из двух уроков я объясню, как создавать пользовательские плагины в OpenCart. Мы пройдемся по всем деталям продвинутой разработки в OpenCart для новичков, а также создадим небольшой плагин, чтобы показать все аспекты структуры разработки в OpenCart.
В первом уроке мы создадим пользовательский плагин, который будет показывать недавно просмотренные товары на стороне back-end’а. Изменить количество товаров можно на стороне back-end’а. Цель сегодняшнего урока – разработать back-end плагин с формой конфигураций.
Предполагается, что у вас уже установлена последняя версия OpenCart. На момент написания статьи это была версия 2.1.0.2. Перед самой разработкой плагина я хочу рассказать вам про базовую архитектуру плагинов в OpenCart.
Коротко о шаблоне MVCL
OpenCart спроектирован по одному из самых популярных шаблонов веб-разработки MVC с небольшими изменениями или, можно сказать, дополнениями. Это дополнение превращает шаблон в MVCL. Возможно, вы уже слышали о данном шаблоне, но я все равно быстро пробегусь по нему для новичков.
Буква M в MVC расшифровывается как модель, именно там расположена почти вся логика. В контексте OpenCart модель взаимодействует с абстрактным слоем базы данных и выполняет всю тяжелую работу, необходимую для работы магазина. Большую часть времени вы будете проводить в модели.
V расшифровывается как представление – слой представления приложения. Из имени понятно, что данный слой работает только с логикой представления страниц, получает данные от других слоев и большую часть времени генерирует XHTML выходные данные. Программный код в этом слое лучше не писать. Представление должно заботиться лишь о том, что делать, а не как это делать.
C – контроллер в MVC, обрабатывающий все запросы. Данный слой охватывает большую часть логики приложения: от обработки и валидации вводимых данных до загрузки подходящей модели и вида для подготовки страницы на вывод.
Наконец, есть дополнительный компонент L, который расшифровывается как язык. С его помощью намного легче создавать многоязычные сайты.
Это был быстрый обзор архитектуры OpenCart. Как только мы начнем более подробно разбирать все компоненты, вам станет все гораздо понятнее.
Скелет любого плагина в OpenCart
Давайте взглянем на то, какие файлы нам понадобятся для создания пользовательского back-end плагина.
admin/language/english/module/recent_products.php: файл, в котором хранятся статичные названия, которые используются в админке.
admin/controller/module/recent_products.php: файл контроллера, в котором хранится логика приложения нашего модуля.
admin/view/template/module/recent_products.tpl: файл-шаблон представления с XHTML кодом.
В следующей секции мы создадим все эти файлы и подробно их разберем. Согласно объявлению выше, файлы пользовательского плагина должны быть в папке module. Так как это back-end плагин, то эту папку нужно искать в папке admin. В соответствии с архитектурой, описанной выше, все файлы раскиданы по разным каталогам или компонентам.
Создаем файлы для back-end плагина
В этом разделе мы будем создавать файлы модуля. Первым мы создадим файл языка admin/language/english/module/recent_products.php со следующим кодом. Крайне важный файл, так как OpenCart должен найти его в вашем плагине.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php // admin/language/english/module/recent_products.php // Заголовок $_['heading_title'] = 'Recent Products'; // Текст $_['text_module'] = 'Modules'; $_['text_success'] = 'Success: You have modified Recent Products module!'; $_['text_edit'] = 'Edit Recent Products Module'; // Вход $_['entry_name'] = 'Module Name'; $_['entry_limit'] = 'Limit'; $_['entry_status'] = 'Status'; // Ошибка $_['error_permission'] = 'Warning: You do not have permission to modify Recent Products module!'; $_['error_name'] = 'Module Name must be between 3 and 64 characters!'; |
Из кода видно, что статические лейблы занесены в PHP массив. Позже, когда массив будет переконвертирован в PHP переменные, они станут доступны в шаблоне представления.
Также вы могли обратить внимание, что файл был создан в папке english – это папка с языком по умолчанию для магазина. Если у вас многоязычный сайт, необходимо будет создать дополнительные папки. К примеру, для французского необходимо будет создать файл в папке admin/language/french/module/recent_products.php.
Далее мы создадим один из важнейших файлов плагина – файл контроллера. Создайте файл admin/controller/module/recent_products.php с кодом ниже.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
<?php // admin/controller/module/recent_products.php class ControllerModuleRecentProducts extends Controller { private $error = array(); public function index() { $this->load->language('module/recent_products'); $this->document->setTitle($this->language->get('heading_title')); $this->load->model('extension/module'); if (( $this->request->server['REQUEST_METHOD'] == 'POST' ) && $this->validate()) { if (!isset( $this->request->get['module_id'] )) { $this->model_extension_module->addModule( 'recent_products', $this->request->post ); } else { $this->model_extension_module->editModule( $this->request->get['module_id'], $this->request->post ); } $this->session->data['success'] = $this->language->get('text_success'); $this->response->redirect( $this->url->link( 'extension/module', 'token=' . $this->session->data['token'], 'SSL' ) ); } $data['heading_title'] = $this->language->get('heading_title'); $data['text_edit'] = $this->language->get('text_edit'); $data['text_enabled'] = $this->language->get('text_enabled'); $data['text_disabled'] = $this->language->get('text_disabled'); $data['entry_name'] = $this->language->get('entry_name'); $data['entry_limit'] = $this->language->get('entry_limit'); $data['entry_status'] = $this->language->get('entry_status'); $data['button_save'] = $this->language->get('button_save'); $data['button_cancel'] = $this->language->get('button_cancel'); if (isset($this->error['warning'])) { $data['error_warning'] = $this->error['warning']; } else { $data['error_warning'] = ''; } if (isset($this->error['name'])) { $data['error_name'] = $this->error['name']; } else { $data['error_name'] = ''; } $data['breadcrumbs'] = array(); $data['breadcrumbs'][] = array( 'text' => $this->language->get('text_home'), 'href' => $this->url->link( 'common/dashboard', 'token=' . $this->session->data['token'], 'SSL' ) ); $data['breadcrumbs'][] = array( 'text' => $this->language->get('text_module'), 'href' => $this->url->link( 'extension/module', 'token=' . $this->session->data['token'], 'SSL' ) ); if (!isset($this->request->get['module_id'])) { $data['breadcrumbs'][] = array( 'text' => $this->language->get('heading_title'), 'href' => $this->url->link( 'module/recent_products', 'token=' . $this->session->data['token'], 'SSL' ) ); } else { $data['breadcrumbs'][] = array( 'text' => $this->language->get('heading_title'), 'href' => $this->url->link( 'module/recent_products', 'token=' . $this->session->data['token'] . '&module_id=' . $this->request->get['module_id'], 'SSL' ) ); } if (!isset($this->request->get['module_id'])) { $data['action'] = $this->url->link( 'module/recent_products', 'token=' . $this->session->data['token'], 'SSL' ); } else { $data['action'] = $this->url->link( 'module/recent_products', 'token=' . $this->session->data['token'] . '&module_id=' . $this->request->get['module_id'], 'SSL' ); } $data['cancel'] = $this->url->link( 'extension/module', 'token=' . $this->session->data['token'], 'SSL' ); if (isset( $this->request->get['module_id']) && ( $this->request->server['REQUEST_METHOD'] != 'POST' ) ) { $module_info = $this->model_extension_module->getModule( $this->request->get['module_id'] ); } if (isset($this->request->post['name'])) { $data['name'] = $this->request->post['name']; } elseif (!empty($module_info)) { $data['name'] = $module_info['name']; } else { $data['name'] = ''; } if (isset($this->request->post['limit'])) { $data['limit'] = $this->request->post['limit']; } elseif (!empty($module_info)) { $data['limit'] = $module_info['limit']; } else { $data['limit'] = 5; } if (isset($this->request->post['status'])) { $data['status'] = $this->request->post['status']; } elseif (!empty($module_info)) { $data['status'] = $module_info['status']; } else { $data['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( 'module/recent_products.tpl', $data ) ); } protected function validate() { if ( !$this->user->hasPermission( 'modify', 'module/recent_products' ) ) { $this->error['warning'] = $this->language->get('error_permission'); } if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) { $this->error['name'] = $this->language->get('error_name'); } return !$this->error; } } |
В коде создается новый класс для пользовательского плагина, который является дочерним для базового класса Controller. Судя по коду, название класса должно дублировать структуру папок к этому файлу. Путь controller/module/recent_products.php превращается в ControllerModuleRecentProducts (опускаем слэши, а первые буквы записываем в верхнем регистре).
Далее идет метод по умолчанию index, который вызывается сразу после загрузки плагина на стороне front-end’а. В этом методе прописана основная логика плагина.
В контексте текущего приложения сокращение $this->load->language загружает соответствующий файл языка. В нашем случае загружается файл, заданный в предыдущем разделе. Синтаксис довольно прост – нужно передать название плагина с префиксом module/. Переменные языка доступны через метод $this->language->get. Далее задается заголовок страницы при помощи метода setTitle объекта document.
Идем дальше, сокращение $this->load->model используется для загрузки модели модуля. Модель представляет собой класс с методами, с помощью которых можно хранить параметры модуля.
Далее идет важный кусок кода, который проверяет, чтобы данные были поданы в POST формате. Если все правильно, по условию сохраняются настройки модуля.
1 2 3 4 5 6 7 8 9 10 |
if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) { if (!isset($this->request->get['module_id'])) { $this->model_extension_module->addModule( 'recent_products', $this->request->post ); } else { $this->model_extension_module->editModule( $this->request->get['module_id'], $this->request->post ); } $this->session->data['success'] = $this->language->get('text_success'); $this->response->redirect( $this->url->link( 'extension/module', 'token=' . $this->session->data['token'], 'SSL' ) ); } |
Далее мы записываем языковые лейблы типа heading_title и text_edit в массив $data, чтобы потом использовать их в файле шаблона. Следом идет кусок кода, отвечающий за создание хлебных крошек на странице настроек.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
$data['breadcrumbs'] = array(); $data['breadcrumbs'][] = array( 'text' => $this->language->get('text_home'), 'href' => $this->url->link( 'common/dashboard', 'token=' . $this->session->data['token'], 'SSL' ) ); $data['breadcrumbs'][] = array( 'text' => $this->language->get('text_module'), 'href' => $this->url->link( 'extension/module', 'token=' . $this->session->data['token'], 'SSL' ) ); if (!isset($this->request->get['module_id'])) { $data['breadcrumbs'][] = array( 'text' => $this->language->get('heading_title'), 'href' => $this->url->link( 'module/recent_products', 'token=' . $this->session->data['token'], 'SSL' ) ); } else { $data['breadcrumbs'][] = array( 'text' => $this->language->get('heading_title'), 'href' => $this->url->link( 'module/recent_products', 'token=' . $this->session->data['token'] . '&module_id=' . $this->request->get['module_id'], 'SSL' ) ); } |
Если бы модуль уже был настроен и находился в режиме редактирования, следующий кусок кода выставил бы настройки по умолчанию.
1 2 3 |
if ( isset( $this->request->get['module_id'] ) && ( $this->request->server['REQUEST_METHOD'] != 'POST' ) ) { $module_info = $this->model_extension_module->getModule( $this->request->get['module_id'] ); } |
И наконец, мы загружаем такие элементы, как хедер, футер и левый сайдбар. За это отвечает сокращение $this->load->view, которое загружает сам файл представления recent_products.tpl и показывает форму настроек.
В файле контроллера нужно запомнить пару важных моментов. В файле можно часто встретить запись типа $this->load->ELEMENT, где ELEMENT может быть представление, модель или язык. Этот вызов загружает соответствующий вид, модель и язык компонентов.
Следующий и последний файл на сегодня – шаблон вида admin/view/template/module/recent_products.tpl. Давайте создадим его!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
<!-- admin/view/template/module/recent_products.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-recent-products" 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">×</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-recent-products" class="form-horizontal"> <div class="form-group"> <label class="col-sm-2 control-label" for="input-name"><?php echo $entry_name; ?></label> <div class="col-sm-10"> <input type="text" name="name" value="<?php echo $name; ?>" placeholder="<?php echo $entry_name; ?>" id="input-name" class="form-control" /> <?php if ($error_name) { ?> <div class="text-danger"><?php echo $error_name; ?></div> <?php } ?> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label" for="input-limit"><?php echo $entry_limit; ?></label> <div class="col-sm-10"> <input type="text" name="limit" value="<?php echo $limit; ?>" placeholder="<?php echo $entry_limit; ?>" id="input-limit" class="form-control" /> </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="status" id="input-status" class="form-control"> <?php if ($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; ?> |
Пользователи с хорошим зрением уже заметили, что в файле просто отображаются переменные из контроллера. В остальном это простой XHTML код с формой настроек. Самая главная фишка в том, что код полностью адаптивный прямо из коробки. С созданием файлов для нашего back-end плагина закончили.
Включаем плагин
Перейдите в панель администратора OpenCart в Extensions > Modules. В списке должен быть плагин Recent Products. Для установки модуля кликните на +, как показано на скриншоте ниже.
После установки появится иконка редактирования. Кликните по ней для открытия формы настроек модуля.
В форме конфигурации можно задать недавние продукты, которые должны показываться в блоке на стороне front-end’а. Не забудьте установить поле status в Enabled! Сохраните модуль, он должен выглядеть вот так.
Появилась новая строка Recent Products > My Recent Block Plugin. То есть модуль можно дублировать под разные страницы! Почти закончили! Мы создали полноценный back-end плагин в OpenCart. В следующей части мы разберем front-end сторону вопроса, которая отвечает за показ блока с товарами!
Заключение
Сегодня мы начали создавать пользовательский плагин в OpenCart. В первой части мы уделили внимание разработке на стороне back-end’а, а также создали рабочий пользовательский плагин с формой настроек.
Если вам нужны дополнительные инструменты для OpenCart, утилиты, расширения и т.д., и вы хотите использовать их в своих новых проектах или просто для обучения, перейдите по ссылке.
В следующей части мы завершим разработку плагина и создадим front-end составляющую, которая будет показывать список товаров. Свои вопросы и предложения пишите в комментариях.
Автор: Sajal Soni
Источник: //code.tutsplus.com/
Редакция: Команда webformyself.
Комментарии (2)