Как создать пользовательский плагин в OpenCart 2.1.x.x: часть 1

Как создать пользовательский плагин в OpenCart

От автора: разработчику всегда интересно создать что-то свое в каком-нибудь фреймворке, то же самое можно сказать и про плагины для 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 должен найти его в вашем плагине.

<?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 с кодом ниже.

<?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 формате. Если все правильно, по условию сохраняются настройки модуля.

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

$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'] ) && ( $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. Давайте создадим его!

<!-- 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">&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-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

Источник: http://code.tutsplus.com/

Редакция: Команда webformyself.

Фреймворк YII2: теория и первая практика

Овладейте азами фреймворка Yii2 за 5 дней!

Получить

Метки:

Комментарии Вконтакте:

Комментарии Facebook:

Комментарии (2)

  1. Сергей

    Если вы тестируете данный код на opencart версии 2.3.0 — то у вас данный код не пройдет, дело в том, что там добавились папочки новые extension

  2. Modules

    Модули можно качать отсюда opencart2x.ru/moduli/

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree