Перейти до вмісту
Пошук в
  • Детальніше...
Шукати результати, які ...
Шукати результати в ...

Diff між OpenCart 3 та OpenCart 4 з точки зору розробки модуля


SergeTkach

675 переглядів

Цей огляд заснований на diff найпростіших вбудованих модулів. Але все перевірено на простому власному модулі, в якому немає ані подій, ані vQmod.

 

Основні зміни у коді стандартного модуля

 

* Примітка

Слово Opencart, виділене жовтим кольором — це код модуля (!). Але, мабуть, ви й самі здогадалися :)

 

У контролері

 

OpenCart 3 OpenCart 4
class ControllerExtensionModuleAccount extends Controller { namespace Opencart\Admin\Controller\Extension\Opencart\Module;
class Account extends \Opencart\System\Engine\Controller {
protected function validate() { public function save(): void {
  // ...
  $this->response->addHeader('Content-Type: application/json');
  $this->response->setOutput(json_encode($json));
}
private $error = array(); Більше не потрібно. Всі помилки обробляються відразу в методі save()
if (isset($this->error['warning'])) {
  $data['error_warning'] = $this->error['warning'];
} else {
  $data['error_warning'] = '';
}

Не потрібно. Обробка помилок відбувається у методі save(). також Обробка помилок

if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {

Більше не використовується. Збереження форми відбувається шляхом надсилання AJAX-запиту до методу save()

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

// 4.0.0.0
$data['save'] = $this->url->link('extension/opencart/module/account|save', 'user_token=' . $this->session->data['user_token']);

 

// 4.0.2.0
$data['save'] = $this->url->link('extension/opencart/module/account.save', 'user_token=' . $this->session->data['user_token']);

 

*Зміни у побудові шляхів впливають на будь-які посилання всередині модуля. Ну, це і так зрозуміло.

$data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true); $data['back'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module');
   
$this->load->model('extension/dashboard/map'); $this->load->model('extension/opencart/dashboard/map');
$results = $this->model_extension_dashboard_map->getTotalOrdersByCountry(); $results = $this->model_extension_opencart_dashboard_map->getTotalOrdersByCountry();
if (isset($this->request->post['module_account_status'])) {
  $data['module_account_status'] = $this->request->post['module_account_status'];
} else {
  $data['module_account_status'] = $this->config->get('module_account_status');
}
$data['module_account_status'] = $this->config->get('module_account_status');

 

 

 

 

У моделі
 

OpenCart 3 OpenCart 4
class ModelExtensionDashboardMap extends Model { namespace Opencart\Admin\Model\Extension\Opencart\Dashboard;
class Map extends \Opencart\System\Engine\Model {

 

 

 

У в'юшці

 

В OpenCart 4.0.0.0 був FontAwesome 5.15.4, а в OpenCart 4.0.2.0 FontAwesome 6.1.1. Це впливає на класи іконок.

 

 

OpenCart 3 OpenCart 4
pull-right float-end
data-toggle="tooltip" data-bs-toggle="tooltip"
<i class="fa fa-save"></i>

<!-- 4.0.0.0 -->
<i class="fas fa-save"></i>

 

<!-- 4.0.2.0 -->
<i class="fa-solid fa-save"></i>

{{ cancel }}
{{ button_cancel }}
{{ back }}
{{ button_back }}
<ul class="breadcrumb"> <ol class="breadcrumb">
<li><a href="{{ breadcrumb.href }}">{{ breadcrumb.text }}</a></li> <li class="breadcrumb-item"><a href="{{ breadcrumb.href }}">{{ breadcrumb.text }}</a></li>
{% if error_warning %}
<div class="alert alert-danger alert-dismissible"><i class="fa fa-exclamation-circle"></i> {{ error_warning }}
<button type="button" class="close" data-dismiss="alert">&times;</button>
</div>
{% endif %}{% if error_warning %}
-- (AJAX)
<div class="panel panel-default"> <div class="card">
<div class="panel-heading">
<h3 class="panel-title"><i class="fa fa-pencil"></i> {{ text_edit }}</h3>
</div>

<!-- 4.0.0.0 -->
<div class="card-header"><i class="fas fa-pencil-alt"></i> {{ text_edit }}</div>

 

<!-- 4.0.2.0 -->
<div class="card-header"><i class="fa-solid fa-pencil"></i> {{ text_edit }}</div>

<div class="panel-body"> <div class="card-body">
<form action="{{ action }}" method="post" enctype="multipart/form-data" id="form-module" class="form-horizontal">

<form id="form-module" action="{{ save }}" method="post" data-oc-toggle="ajax">

 

* Якщо пропустити data-oc-toggle="ajax", то форма обробиться по-старому з повним завантаженням сторінки (принаймні у версії 4.0.0.0). Хоча сам .alert у bootstrap 5 трохи змінився.

<div class="form-group"> <div class="row mb-3">
control-label col-form-label
<select name="module_account_status" ... <!-- 4.0.2.0 -->
<input type="checkbox" name="module_account_status" ...

Шляхи в AJAX-запитах

url: 'index.php?route=extension/module/imagescanner/getNotUsedImagesList

<!-- 4.0.0.0 -->
url: 'index.php?route=extension/imagescanner/module/imagescanner|getNotUsedImagesList

 

<!-- 4.0.2.0 -->
url: 'index.php?route=extension/imagescanner/module/imagescanner.getNotUsedImagesList

Шляхи до зображень

var img_loader = new Image().src='view/image/imagescanner-loader.gif';

var img_loader = new Image().src='extension/imagescanner/admin/view/image/imagescanner-loader.gif';

А воно запитує: http://opencart-4000.loc/admin/extension/imagescanner/admin/view/image/imagescanner-loader.gif

 

Значить треба вказати повний шлях до файлу


var img_loader = new Image().src='{{ constant('HTTP_CATALOG') }}extension/imagescanner/admin/view/image/imagescanner-loader.gif';

 

 

 

Обробка помилок

 

Щоб поля з помилками підсвічувалися і до них були пояснювальні підписи, першою дією у в'юшці необхідно вписати порожні контейнери для текстів помилок.

 

<div id="error-field" class="invalid-feedback"></div>

 

Далі, при обробці форми по AJAX можна отримати наступний формат відповіді:


{
  "error": {
    "name_1": "Product Name must be greater than 1 and less than 255 characters!",
    "keyword_0_1": "SEO URL keyword required!",
   "warning": "Warning: Please check the form carefully for errors!"
  }
}

 

Потім воно буде універсально оброблено в admin/view/javascript/common.js з урахуванням того, чи це рядок, чи це об'єкт, та чи є там редірект. Також воно автоматично переведе under_score індекси массиву з помилками в kebab-case html-ідентифікаторів, в яких потрібно показати відповідні тексти червоним кольором.

 

Прихований текст

$(document).on('submit', 'form[data-oc-toggle=\'ajax\']', function (e) {
...
success: function (json) {
  $('.alert-dismissible').remove();
  $(element).find('.is-invalid').removeClass('is-invalid');
  $(element).find('.invalid-feedback').removeClass('d-block');
  console.log(json);
  if (json['redirect']) {
    location = json['redirect'];
  }
  if (typeof json['error'] == 'string') {
    $('#alert').prepend('<div class="alert alert-danger alert-dismissible"><i class="fas fa-exclamation-circle"></i> ' + json['error'] + ' <button type="button" class="btn-close" data-bs-dismiss="alert"></button></div>');
  }
  if (typeof json['error'] == 'object') {
    if (json['error']['warning']) {
      $('#alert').prepend('<div class="alert alert-danger alert-dismissible"><i class="fas fa-exclamation-circle"></i> ' + json['error']['warning'] + ' <button type="button" class="btn-close" data-bs-dismiss="alert"></button></div>');
    }
    for (key in json['error']) {
      $('#input-' + key.replaceAll('_', '-')).addClass('is-invalid').find('.form-control, .form-select, .form-check-input, .form-check-label').addClass('is-invalid');
      $('#error-' + key.replaceAll('_', '-')).html(json['error'][key]).addClass('d-block');
    }
  }
  if (json['success']) {
    $('#alert').prepend('<div class="alert alert-success alert-dismissible"><i class="fas fa-check-circle"></i> ' + json['success'] + ' <button type="button" class="btn-close" data-bs-dismiss="alert"></button></div>');
    // Refresh
    var url = $(form).attr('data-oc-load');
    var target = $(form).attr('data-oc-target');
     if (url !== undefined && target !== undefined) {
      $(target).load(url);
    }
  }
  // Replace any form values that correspond to form names.
  for (key in json) {
    $(element).find('[name=\'' + key + '\']').val(json[key]);
  }
},
...

 

 

TypeError: Cannot access offset of type string on string in при обробці помилок

 

У js при обробці відповіді AJAX-запиту робиться розмежування між отриманим рядком та об'єктом. Все тому, що в найпростішому "модулі" (наприклад, account) в помилки потрапляє лише рядок з текстом без зайвих проблем. І ось я скопіпастив звідти, а потім побачив, що в товарах йде інакше, і скопіпастив шматок коду ще й звідти. У результаті напоровся на помилку: TypeError: Cannot access offset of type string on string in ...

 

 

$json['error'] = $this->language->get('error_text'); // В найпростішому модулі присвоюється рядок
...
if (isset($json['error']) && !isset($json['error']['warning'])) {
  $json['error']['warning'] = $this->language->get('error_warning'); // Намагається рядку присвоїти індекс масиву, але це ж PHP 8...
}

 

 

Власні бібліотеки у складі модуля

 

Якщо ваш модуль використовує бібліотеку, яка зазвичай завантажувалася в system/library, то зараз при розпакуванні архіву вона потрапить до extnension/modulecode/system/library/ .

 

OpenCart автоматично створює простори імен, як для контролерів з моделями, так і для бібілотек:

 

// 4.0.0.0
[Opencart\Admin\Controller\Extension\Imagescanner] => Array
    (
        [directory] => .../opencart-4000.loc/extension/imagescanner/admin/controller/
        [psr4] => 
    )

[Opencart\Admin\Model\Extension\Imagescanner] => Array
    (
        [directory] => .../opencart-4000.loc/extension/imagescanner/admin/model/
        [psr4] => 
    )

[Opencart\System\Extension\Imagescanner] => Array
    (
        [directory] => .../opencart-4000.loc/extension/imagescanner/system/
        [psr4] => 
    )

 

// 4.0.2.0

[Opencart\System\Library\Extension\Imagescanner] => Array
    (
        [directory] => .../opencart-4021.loc/extension/imagescanner/system/library/
        [psr4] => 
    )

 

Зверніть увагу, що варіант іменування класу бібліотеки ImageScanner при підключенні перетвориться на image_scanner.php (system/engine/autoloader.php). Тоді як Imagescanner відповідатиме imagescanner.php. Це при тому, що в контролері назва класу в стилі CamelCase працює ок. А чому так — це вже окрема історія.

 

 

У файлі бібліотеки

 

Задамо простір імен:

 

// 4.0.0.0
namespace Opencart\System\Extension\Modulecode\Library;


// 4.0.2.0
namespace Opencart\System\Library\Extension\Imagescanner;

 

 

Називаємо клас:

 

class Yourclassname {

 

 

У файлі контролера

 

Створюємо екземпляр класу бібліотеки у нашому контролері:

 

// 4.0.0.0
$this->instance = new \Opencart\System\Extension\Modulecode\Library\Yourclassname();


// 4.0.2.0
$this->stdelog = new \Opencart\System\Library\Extension\Imagescanner\Stdelog('imagescanner');

 

 

З іншого боку чудово відпрацює і по-старому, і ще й ніяких приколів зі змінами в системі:

 

require_once DIR_EXTENSION . 'modulecode/system/library/modulecode.php';
$this->instance = new Yourclassname(); // и не паритися :)

 

 

Папка модуля

 

На прикладі присутньої в системі папки extension/opencart (де складені всі дефолтні модулі) здавалося, що OpenCart 4 з'явилося поняття "папка постачальника". Але потім з'ясувалося, що при спробі встановити в ту ж папку інший свій модуль, воно не працює

 

До речі, якщо в інсталяційному архіві будуть нестандартні шляхи до файлів (я, наприклад, пробував modulecode/library/file.php на 4.0.0.0), то при видаленні модуля з адмінки папка модуля не видаляється, хоча всі стандартні файли та папки звідти видалені. Тобто це може створити проблеми при оновленні модуля, адже в існуючу папку модуля установник не хоче записувати.

 

І ще з цією папкою є один приємний момент: щоб упакувати модуль, досить просто скопіювати папку та заархівувати. Більше не потрібно ритися в папках і копіювати кожен файл окремо.

 

 

P.S.

 

На преший погляд, адаптація модуль під OpenCart 4 має бути досить простою. Я цю статтю довше писав, ніж адаптував модуль. Але огляд поки що не повний. Дописуватиму згодом. Також буду додавати в текст статті зауваження з коментарів.

--

  • +1 11

2 коментаря


Recommended Comments

Зауважу.
Забудьте слово модулі

Є other
тому свої рішення, які не беруть участь у контенті через лейаут потрібно виносити в other

  • +1 3
Надіслати

Створіть аккаунт або увійдіть для коментування

Ви повинні бути користувачем, щоб залишити коментар

Створити обліковий запис

Зареєструйтеся для отримання облікового запису. Це просто!

Зареєструвати аккаунт

Вхід

Уже зареєстровані? Увійдіть тут.

Вхід зараз
  • Зараз на сторінці   0 користувачів

    • Ні користувачів, які переглядиють цю сторінку
×
×
  • Створити...

Important Information

На нашому сайті використовуються файли cookie і відбувається обробка деяких персональних даних користувачів, щоб поліпшити користувальницький інтерфейс. Щоб дізнатися для чого і які персональні дані ми обробляємо перейдіть за посиланням . Якщо Ви натиснете «Я даю згоду», це означає, що Ви розумієте і приймаєте всі умови, зазначені в цьому Повідомленні про конфіденційність.