В этой статье будет описан способ добавления своего модуля в OpenCart 2.0. Подразумевается, что читатель уже хотя бы редактировал стандартный шаблон под требования заказчика и ориентируется в php. Модуль будет отображать в виде слайдера отзывы о магазине в целом, которые добавил покупатель. Хотя кого мы обманываем – все отзывы будут заполняться из админки подготовленным человеком (ну может на основе отзывов которые скинули по почте админу магазина) =)

Для начала нужно разобраться как работает система модулей. А разбираться мы будем на основе модуля “Баннер” который идет в стандартной поставке.

Скопируем все файлы, назвав их предварительно вместо баннер – comments и определимся со стуктурой каталога:

- admin (здесь лежит часть админки)
 - controller (сюда обращаются скрипты со страницы админки в поисках чего же модуль делает)
  - module
   -comments.php
 - language (не обязательно, но MVC требует отдельно языковых файлов, да так и понятней)
  - russian (будет только русская версия)
   - module
    - comments.php
 - model (здесь скрипты для работы с БД)
  - comments
   - comments.php
 - viev (внешний вид модуля в админке)
  - template
   - module
    - comments.tpl
- catalog (то, что видит пользователь)
 - controller (сюда обращается за функционалом пользовательской части модуля движок сайта)
  - module
   - comments.php
 - model (здесь скрипты для работы с БД)
  - comments
   - comments.php
 - language 
  - russian 
   - module
    -comments.php
 - viev (внешний вид модуля на странице)
  - javascript
   - own-carousel и сопутствующие файлы. Этот слайдер так же в стандартной поставке OpenCart
   - theme
    - default
     - stylesheet
      - comments.css
     - template
      - module
       - comments.tpl
- image (картинки - будем складывать сюда аватарки)

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

Часть первая – Контроллер админки.

Определяемся с задачей модуля:

И напишен план действий модуля:

Казалось бы просто, но разберем все по порядку:

Начало контроллера ничем не отличается от всех остальных контроллеров (все название не от балды – так положено в OpenCart):

<!--?php class ControllerModuleComments extends Controller { private $error = array(); // объявляем переменную - массив с возможными ошибками public function index() {&lt;/pre&gt; &lt;p&gt;Далее нам нужно подключить языковой файл, согласно нашей структуре это делается таким образом:&lt;/p&gt; &lt;pre lang="php"&gt;$this-&gt;load-&gt;language('module/comments');&lt;/pre&gt; &lt;p&gt;Разберем фразу: обратиться к классу Controller (наш наследуется от него), в котором найти объект load, который подгружает разные штуки, просим его подгрузить языковой файл - этим мы ориентируем его на папку languages, в ней ищем папку module и файл comments. Расширение у файла подразумевается .php.&lt;/p&gt; &lt;p&gt;Зададим title для нашего модуля:&lt;/p&gt; &lt;pre lang="php"&gt;$this-&gt;document-&gt;setTitle($this-&gt;language-&gt;get('heading_title'));&lt;/pre&gt; &lt;p&gt;Далее нас интересует появление нашего модуля в списке модулей - из админки. Это делается такой функцией: &lt;/p&gt; &lt;pre lang="php"&gt;// регистрируем модуль $this-&gt;load-&gt;model('setting/setting'); if (($this-&gt;request-&gt;server['REQUEST_METHOD'] == 'POST') &amp;&amp; $this-&gt;validate()) { $this-&gt;model_setting_setting-&gt;editSetting('comments', $this-&gt;request-&gt;post); $this-&gt;session-&gt;data['success'] = $this-&gt;language-&gt;get('text_success'); $data['success'] = $this-&gt;session-&gt;data['success'] ; }&lt;/pre&gt; &lt;p&gt;Чтобы понять почему такое условие - надо знать, что кнопка "сохранить" в модуле работает методом GET, а кнопка "добавить модуль" получается методом POST, таким образом фукнция вызовется всего лишь один раз при добавлении модуля и больше не нужна. Функция validate нужна для проверки прав пользователя добавлять модули (не сказать, чтоб сильно нужна, но так положено)&lt;/p&gt; &lt;pre lang="php"&gt;protected function validate() { if (!$this-&gt;user-&gt;hasPermission('modify', 'module/comments')) { $this-&gt;error['warning'] = $this-&gt;language-&gt;get('error_permission'); } return !$this-&gt;error; }&lt;/pre&gt; &lt;p&gt;Итак, модуль зарегистрирован.&lt;/p&gt; &lt;p&gt;Далее начинаем заполнять массив данных, который передается на отрисовку, начнем с констант текстовых:&lt;/p&gt; &lt;pre lang="php"&gt;$data['heading_title'] = $this-&gt;language-&gt;get('heading_title'); $data['text_edit'] = $this-&gt;language-&gt;get('text_edit'); $data['text_enabled'] = $this-&gt;language-&gt;get('text_enabled'); $data['text_disabled'] = $this-&gt;language-&gt;get('text_disabled'); $data['table_image'] = $this-&gt;language-&gt;get('table_image'); $data['table_name'] = $this-&gt;language-&gt;get('table_name'); $data['table_text'] = $this-&gt;language-&gt;get('table_text'); $data['button_save'] = $this-&gt;language-&gt;get('button_save'); $data['button_cancel'] = $this-&gt;language-&gt;get('button_cancel'); $data['table_header'] = $this-&gt;language-&gt;get('table_header'); $data['table_edit'] = $this-&gt;language-&gt;get('table_edit'); $data['table_delete'] = $this-&gt;language-&gt;get('table_delete'); $data['table_id'] = $this-&gt;language-&gt;get('table_id'); $data['title_add'] = $this-&gt;language-&gt;get('title_add');&lt;/pre&gt; &lt;p&gt;Так же нам нужен токен для переадресации на следущую страницу, чтоб не выкидывало на страницу ввода пароля (в url он присутствует в админке все время, в шаблоне он приписывается к ссылкам дабы не разрушать сессию): &lt;/p&gt; &lt;pre lang="php"&gt;$data['token'] = $this-&gt;session-&gt;data['token'];&lt;/pre&gt; &lt;p&gt;И сообщения об ошибках:&lt;/p&gt; &lt;pre lang="php"&gt;if (isset($this-&gt;error['warning'])) { $data['error_warning'] = $this-&gt;error['warning']; } else { $data['error_warning'] = ''; }&lt;/pre&gt; &lt;p&gt;Нам понадобятся хлебные крошки. Они есть почти в каждом контроллере в OpenCart, можно смело их копировать, если не нужны какие то особые.&lt;/p&gt; &lt;pre lang="php"&gt;$data['breadcrumbs'] = array(); // Добавляем по одной крошки, сначала ссылка на главную страницу $data['breadcrumbs'][] = array( 'text' =&gt; $this-&gt;language-&gt;get('text_home'), // text_home по всей видимости доступен отовсюду 'href' =&gt; $this-&gt;url-&gt;link('common/dashboard', 'token=' . $this-&gt;session-&gt;data['token'], 'SSL') ); // добавляем ссылку на список с модулями, прописано в своем языковом файле $data['breadcrumbs'][] = array( 'text' =&gt; $this-&gt;language-&gt;get('text_module'), 'href' =&gt; $this-&gt;url-&gt;link('extension/module', 'token=' . $this-&gt;session-&gt;data['token'], 'SSL') );&lt;/pre&gt; &lt;p&gt;Здесь УЖЕ видно как вставляется токен в ссылки - так и нужно делать со всеми ссылками по админке.&lt;/p&gt; &lt;p&gt;Понадобятся кнопки сохранить и отменить:&lt;/p&gt; &lt;pre lang="php"&gt;// кнопки $data['action'] = $this-&gt;url-&gt;link('module/comments', 'token=' . $this-&gt;session-&gt;data['token'], 'SSL'); $data['cancel'] = $this-&gt;url-&gt;link('extension/module', 'token=' . $this-&gt;session-&gt;data['token'], 'SSL');&lt;/pre&gt; &lt;p&gt;Так же каждый раз при заходе в модуль хотелось бы видеть его реальный статус - включен или нет. Проверим:&lt;/p&gt; &lt;pre lang="php"&gt;if (isset($this-&gt;request-&gt;post['comments_status'])) { $data['comments_status'] = $this-&gt;request-&gt;post['comments_status']; } else { $data['comments_status'] = $this-&gt;config-&gt;get('comments_status'); }&lt;/pre&gt; &lt;p&gt;Получим текущие коменты (получаем ВСЕ коменты, в рамках текущей задачи этого достаточно, а так нужно предусматривать сколько отображать)&lt;/p&gt; &lt;pre lang="php"&gt;$data['comments'] = array(); $this-&gt;load-&gt;model('comments/comments'); $data['comments'] = $this-&gt;model_comments_comments-&gt;getAll();&lt;/pre&gt; &lt;p&gt;Как видим внешне все просто - создаем архив под коменты, подгружаем модель, используем из модели функцию "getAll". Как она работает будет рассмотрено позже, сейчас важно знать что она берет из базы данные.&lt;/p&gt; &lt;p&gt;Если мы зайдем в любой из модулей то увидим, что присутствуют: шапка, футер и левая колонка, их тоже надо подключить:&lt;/p&gt; &lt;pre lang="php"&gt;// подключаем с админской части шапки колонки слева и футера $data['header'] = $this-&gt;load-&gt;controller('common/header'); $data['column_left'] = $this-&gt;load-&gt;controller('common/column_left'); $data['footer'] = $this-&gt;load-&gt;controller('common/footer');&lt;/pre&gt; &lt;p&gt;Так как все данные собраны и переданы в массив data мы его шлем на отрисовку - т.е. вызываем comments.tpl&lt;/p&gt; &lt;pre lang="php"&gt;// передаем данные на отрисовку $this-&gt;response-&gt;setOutput($this-&gt;load-&gt;view('module/comments.tpl', $data));&lt;/pre&gt; &lt;p&gt;На даном этапе мы имеет контроллер, который собирает данные, достаточные для отображения админской части. Но забегая наперед - нам будут нужны функции добавления комента, удаления. Напишем их как будто мы знаем что должно выйти (на самом деле файл контроллера правится все время во время создания модуля, так как появляются новые идеи решения и функции):&lt;/p&gt; &lt;pre lang="php"&gt;public function addComment(){ // получаем значения $name = ($this-&gt;request-&gt;post['name']) ? $this-&gt;request-&gt;post['name']:'default'; $image = ($this-&gt;request-&gt;post['image']) ? $this-&gt;request-&gt;post['image']:1; $text = ($this-&gt;request-&gt;post['text']) ? $this-&gt;request-&gt;post['text']:'no comment'; $id = (isset($this-&gt;request-&gt;post['id'])) ? $this-&gt;request-&gt;post['id']:0; // массив вывода $json = array(); $json['name']=$name; $json['image']=$image; $json['text']=$text; $this-&gt;load-&gt;model('comments/comments'); $json['com_id'] = ($id) ? $this-&gt;model_comments_comments-&gt;updateRow($id,$name,$image,$text) : $this-&gt;model_comments_comments-&gt;addNew($name,$image,$text); $this-&gt;response-&gt;addHeader('Content-Type: application/json'); $this-&gt;response-&gt;setOutput(json_encode($json)); }&lt;/pre&gt; &lt;p&gt;Решим для себя, что будет использовать метод POST для передачи данных из формы. &lt;/p&gt; &lt;pre lang="php"&gt;$name = ($this-&gt;request-&gt;post['name']) ? $this-&gt;request-&gt;post['name']:'default';&lt;/pre&gt; &lt;p&gt;В этой и подобных строчках мы получаем имя, если оно не задано - присваиваем default, так как комент без имени смотрится странно. &lt;/p&gt; &lt;pre lang="php"&gt;Лирическое отступление. Именно на таком принципе построены все валидации в OpenCart - если значения полученного методом post нет или оно не соответствует шаблону - записываем в массив error нужную ошибку. А затем ЕСЛИ error ПУСТОЙ - делаем нужные действия, нет - отсылаем error обратно пользователю. Все ест-сно работает на ajax.&lt;/pre&gt; &lt;p&gt;Далее записываем данные полученные в массив, который будет передан обратно - это нужно для отладки, чтобы видеть что добавилось и смотреть реальную картину.&lt;/p&gt; &lt;p&gt;Подгружаем нашу модель для использовании функций работы с БД&lt;/p&gt; &lt;p&gt;И наконец проверяем id - если оно не ноль - т.е. если передали в форме id записи, значит мы редактируем комент, если 0 - добавляем новую. &lt;/p&gt; &lt;p&gt;И как обычно отправляем на отрисовку - только теперь не в файл, а json строкой.&lt;/p&gt; &lt;p&gt;Если разобрались с этой, функция удалить будет более простой: &lt;/p&gt; &lt;pre lang="php"&gt;public function deleteComment(){ // получаем значения $text = ($this-&gt;request-&gt;post['id']) ? $this-&gt;request-&gt;post['id'] : 0; // массив вывода $json = array(); if (!$text) { $json['error'] = 'no id'; } else { $this-&gt;load-&gt;model('comments/comments'); $json['com_id'] = $this-&gt;model_comments_comments-&gt;deleteRow($text); } $this-&gt;response-&gt;addHeader('Content-Type: application/json'); $this-&gt;response-&gt;setOutput(json_encode($json)); }&lt;/pre&gt; &lt;p&gt;Вот и все, файл полностью готов и, надеюсь, понятен. Полную версию файла можно скачать ниже.&lt;br ?-->
<a href="https://web-porosya.com/wp-content/uploads/2016/02/comments.txt" rel="">comments.txt</a>

Другие части:
Как написать модуль OpenCart (Часть 2)
Как написать модуль OpenCart (Часть 3)
Как написать модуль OpenCart (Часть 4)