Публикации
Публикации  »  PHP

Написание модуля для OpenCart 3.x

OpenCart - одна из популярных CMS для магазина, но в стандартном виде в нем не очень-то много возможностей, что компенсируется большим количеством платных и бесплатных модулей. В этой статье на простом примере я постараюсь рассказать как создать свой модуль для OpenCart 3.x.

Модуль OpenCart может быть исполнен в нескольких вариантах:

  1. Файл-модификатор OCMOD, который изменяет уже существующие в системе файлы. Подробнее о том, как написать модификатор читайте в следующей статье.
  2. Набор php, twig и других файлов, которые добавляют новые возможности в системе как отдельная программа и не затрагивающие стандартный функционал.
  3. Комбинированный - сочетание модификатора и файлов php, twig и т.д.

В этой статье рассказывается как написать свой модуль для OpenCart добавляющий новые возможности в систему.

Начну с того, что OpenCart построен на схеме MVC (Model-View-Controller). Это значит, что модуль может состоять из следующих файлов:

  • Контроллер (Controller) - php-файл, который будет вызван первым и отвечает за логику работы конкретного модуля, определяет, что именно будет делать модуль, обрабатывает взаимодействие с пользователем и т.д. Контроллер может использовать (хотя и не обязательно) Модель и Представление.
  • Модель (Model) - php-файл, отвечающий за чтение и запись данных в базу данных, т.е. содержит функции для получения или сохранения данных. Файл модели не обязателен.
  • Представление (View) - файл какого-то html-шаблона, который определяет каким образом будет выведена информация на экран браузера. В OpenCart 3.x для создания шаблонов применяется обработчик шаблонов Twig и поэтому файл имеют расширение .twig.

Для поддержки мультиязычности в OpenCart так же есть языковые файлы, о которых будет чуть ниже. Файлы OpenCart-а организованы по папкам, контроллеры в своих, представления в своих и т.д. Итак, для создания модуля и правильного расположения файлов, нам понядобятся следующие папки и файлы:

Контроллер административной (back) части - admin / controller / extension / module / имя_файла_модуля.php
Контроллер пользовательской (front) части - catalog / controller / extension / module / имя_файла_модуля.php
Модель административной (back) части - admin / model / extension / module / имя_файла_модуля.php
Модель пользовательской (front) части - catalog / model / extension / module / имя_файла_модуля.php
Представление административной (back) части - admin / view / template / extension / module / имя_файла_модуля.twig
Представление пользовательской (front) части - catalog / view / theme / тема_дизайна / template / extension / module / имя_файла_модуля.twig

Кроме контроллера, модели и представления, у модуля так же обычно есть так называемые языковые файлы, которые находятся в папках:

Языковые файлы административной (front) части:
admin / language / en-gb / extension / module / имя_файла_модуля.php
admin / language / ru-ru / extension / module / имя_файла_модуля.php
Языковые файлы пользовательской (back) части:
catalog / language / en-gb / extension / module / имя_файла_модуля.php
catalog / language / ru-ru / extension / module / имя_файла_модуля.php

Впринципе, если планируется только 1 язык, то можно сделать языковые файлы только для него.

Минимально, у модуля должен быть хотя бы 1 php-файл - контроллер. Остальные составляющие, впринципе, могут и отсутствовать, если модуль не работает с базой данных и ничего не выводит пользователю. Но думаю, не имеет смысла рассматривать такой модуль, т.к. таким образом, мы не сможем понять всю цепочку создания полноценного модуля.

Итак, представим, что мы разрабатываем пример модуля для OpenCart 3.x, который будет иметь административную часть (back) и пользовательскую (front). В административной части, он будет иметь лишь одну настройку "Статус", которую можно менять на Включено/Выключено. А в пользовательской части пусть просто выведет страницу с текстом "Пример модуля на OpenCart 3.x", если он включен или сообщение об ошибке, если выключен. Файлы примера модуля пусть будут иметь имя example, т.е. example.php, example.twig и т.д.

Административная часть модуля

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

Контроллер модуля admin/controller/extension/module/example.php

Создание контроллера предполагает чтение из базы единственной настройки - статуса (включено/выключено), выведение на экран представления (шаблона) и сохранение настройки, когда пользователь нажал соответствующую кнопку.

class ControllerExtensionModuleExample extends Controller {

  public function index() {

    // Загружаем "модель" модуля
    $this->load->model('extension/module/example');

    // Сохранение настроек модуля, когда пользователь нажал "Записать"
    if (($this->request->server['REQUEST_METHOD'] == 'POST')) {
      // Вызываем метод "модели" для сохранения настроек
      $this->model_extension_module_example->SaveSettings();
      // Выходим из настроек с выводом сообщения
      $this->session->data['success'] = 'Настройки сохранены';
      $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
    }

    // Загружаем настройки через метод "модели"
    $data = array();
    $data['module_example_status'] = $this->model_extension_module_example->LoadSettings();
    // Загружаем языковой файл
    $data += $this->load->language('extension/module/example');
    // Загружаем "хлебные крошки"
    $data += $this->GetBreadCrumbs();

    // Кнопки действий
    $data['action'] = $this->url->link('extension/module/example', '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);
    // Загрузка шаблонов для шапки, колонки слева и футера
    $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/example', $data));

  }

  // Хлебные крошки
  private function GetBreadCrumbs() {
    $data = array(); $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' => 'Пример модуля',
      'href' => $this->url->link('extension/module/example', 'token=' . $this->session->data['user_token'], true)
    );
    return $data;
  }

}

Модель модуля admin/model/extension/module/example.php

Создание модели подразумевает работу с базой данных, а именно сохранение и чтение данных. В данном случае, можно использовать стандартные opencart-овские методы, но при необходимости, вы конечно можете, создавать свои таблицы и сохранять и читать из них.

class ModelExtensionModuleExample extends Model {

  // Запись настроек в базу данных
  public function SaveSettings() {
    $this->load->model('setting/setting');
    $this->model_setting_setting->editSetting('module_example', $this->request->post);
  }

  // Загрузка настроек из базы данных
  public function LoadSettings() {
    return $this->config->get('module_example_status');
  }

}

Языковой файл модуля admin/language/ru-ru/extension/module/example.php

Здесь все просто: нужно написать все используемые фразы и предложения и их переводы на русский, которые будут использоваться в представлении (шаблоне).

$_['heading_title']  = 'Пример модуля';
$_['text_extension'] = 'Расширения';
$_['text_success']   = 'Настройки успешно изменены!';
$_['text_edit']      = 'Настройки модуля';
$_['entry_status']   = 'Статус';
$_['text_enabled']   = 'Включено';
$_['text_disabled']  = 'Выключено';

Представление (шаблон) модуля admin/view/template/extension/module/example.twig

Как упоминалось выше, для создания представлений-шаблонов, нужно использовать twig. Русскоязычную документацию можете посмотреть, например, на x-twig.ru

{{ header }}{{ column_left }}
<div id="content">
  <div class="page-header">
    <div class="container-fluid">

      <!-- Кнопки управления -->
      <div class="pull-right">
        <button type="submit" form="form-module" data-toggle="tooltip" title="{{ button_save }}" class="btn btn-primary"><i class="fa fa-save"></i></button>
        <a href="{{ cancel }}" data-toggle="tooltip" title="{{ button_cancel }}" class="btn btn-default"><i class="fa fa-reply"></i></a>
      </div>

      <!-- Название модуля -->
      <h1>{{ heading_title }}</h1>

      <!-- Хлебные крошки -->
      <ul class="breadcrumb">
        {% for breadcrumb in breadcrumbs %}
        <li><a href="{{ breadcrumb.href }}">{{ breadcrumb.text }}</a></li>
        {% endfor %}
      </ul>

    </div>
  </div>

  <div class="container-fluid">
    <div class="panel panel-default">
      <div class="panel-heading">
        <h3 class="panel-title"><i class="fa fa-pencil"></i> {{ text_edit }}</h3>
      </div>
      <div class="panel-body">
        <form action="{{ action }}" method="post" enctype="multipart/form-data" id="form-module" class="form-horizontal">

          <!-- Настройка: "Статус" -->
          <div class="form-group">
            <label class="col-sm-2 control-label" for="input-status">{{ entry_status }}</label>
            <div class="col-sm-10">

              <select name="module_example_status" id="input-status" class="form-control">
                {% if module_example_status %}
                <option value="1" selected="selected">{{ text_enabled }}</option>
                <option value="0">{{ text_disabled }}</option>
                {% else %}
                <option value="1">{{ text_enabled }}</option>
                <option value="0" selected="selected">{{ text_disabled }}</option>
                {% endif %}
              </select>

            </div>
          </div>

        </form>
      </div>
    </div>
  </div>
</div>
{{ footer }}

Теперь еще несколько слов о том, как всё примерно работает и взаимодействует.

Когда мы нажимаем на кнопку перехода в настройки модуля, в адресной строке получается адрес заканчивающийся на index.php?route=extension/module/example&user_token=123. Токен в конце соответственно будет другой. Нас интересует часть extension/module/example - именно она и говорит opencart-у, где находится файл нашего контроллера.

Получивший управление файл контроллера загружает файл-модель для обращения к базе данных, загружает языковой файл и выводит шаблон twig на экран браузера.

Обратите внимание: в первых строках контроллера и модели идут названия классов: class ControllerExtensionModuleExample extends Controller и class ModelExtensionModuleExample extends Model. Как видим, в их названиях присутствует путь к модулю и название модуля. Если назвать классы как-то иначе, ничего работать не будет.

Пользовательская часть модуля

Контроллер модуля catalog/controller/extension/module/example.php

Создание контроллера пользовательской части похоже на административную, только не нужно сохранять настройки, нужно только написать код, который вывводит информацию в зависимости от того включен модуль или нет.

class ControllerExtensionModuleExample extends Controller {
  public function index() {
    // Загружаем "модель"
    $this->load->model('extension/module/example');
    $data = array();
    // Загружаем настройки (для проверки включен модуль или нет)
    $data['module_example_status'] = $this->model_extension_module_example->LoadSettings();
    // Загружаем языковой файл
    $data += $this->load->language('extension/module/example');
    // Хлебные крошки
    $data['breadcrumbs'][] = array(
      'text' => $this->language->get('text_home'),
      'href' => $this->url->link('common/home')
    );
    $data['breadcrumbs'][] = array(
      'text' => $data['heading_title'],
      'href' => $this->url->link('extension/module/example')
    );
    // Загружаем остальное
    $data['column_left'] = $this->load->controller('common/column_left');
    $data['column_right'] = $this->load->controller('common/column_right');
    $data['content_top'] = $this->load->controller('common/content_top');
    $data['content_bottom'] = $this->load->controller('common/content_bottom');
    $data['footer'] = $this->load->controller('common/footer');
    $data['header'] = $this->load->controller('common/header');
    // Выводим на экран
    $this->response->setOutput($this->load->view('extension/module/example', $data));
  }
}

Модель модуля catalog/model/extension/module/example.php

В нашем примере модель пользовательской части маленькая, т.к. нужно лищь прочитать одну настройку.

class ModelExtensionModuleExample extends Model {
  // Загрузка настроек из базы данных
  public function LoadSettings() {
    return $this->config->get('module_example_status');
  }
}

Языковой файл модуля catalog/langugage/ru-ru/extension/module/example.php

$_['heading_title'] = 'Пример модуля';
$_['text_example']   = 'Пример модуля на OpenCart 3.x';
$_['text_error']   = 'Модуль выключен';

Представление (шаблон) модуля catalog/view/theme/default/template/extension/module/example.twig

{{ header }}
<div id="information-information" class="container">
  <ul class="breadcrumb">
    {% for breadcrumb in breadcrumbs %}
    <li><a href="{{ breadcrumb.href }}">{{ breadcrumb.text }}</a></li>
    {% endfor %}
  </ul>
  <div class="row">{{ column_left }}
    {% if column_left and column_right %}
    {% set class = 'col-sm-6' %}
    {% elseif column_left or column_right %}
    {% set class = 'col-sm-9' %}
    {% else %}
    {% set class = 'col-sm-12' %}
    {% endif %}
    <div id="content" class="{{ class }}">
    {{ content_top }}

    <!-- Выводим соответствующий текст, если модуль включен или выключен -->
    {% if module_example_status %}
    <h1>{{ text_example }}</h1>
    {% else %}
    {{ text_error }}
    {% endif %}

    {{ content_bottom }}
    </div>
    {{ column_right }}
</div>
{{ footer }}

Принцип работы пользовательской части такой же, как и административной. В адресной строке будет соответственно index.php?route=extension/module/example, что и говорит opencart-у какой файл контроллера нужно использовать и из какой папки.

В административной части и в пользовательской я использовал по 4 файла (контроллер, модель, языковой файл и представление). Однако, как я писал выше, впринципе, вы можете создать более компактный модуль, сократив количество файлов только до одного контроллера, если он всё необходимое сделает сам. Так же, можно и наоборот, написать более сложный модуль, который будет состоять из нескольких контроллеров, нескольких моделей и нескольких шаблонов.

 

Категория: PHP

Комментарии к статье:

26.09.18   Гость Спасибо
28.10.18   Гость Добрый день. Хорошая статья!
Скажите, а переделать модуль с версии под 3.0 на 2.1 возможно? Я так понимаю, нужно пути верные в файлах прописать. А вот как быть с twig?
Спасибо
29.10.18   Admin Для версии 2.1 путь будет без "extension", а шаблоны в версиях младше 3-й не используют twig, а представляют собой файлы смесь html + php вставки. Например:
<?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-account" 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">

<!-- Здесь что-то, что выводит модуль на экран пользователю -->

  </div>
</div>
<?php echo $footer; ?>

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