Розробка кастомного модуля OpenCart

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

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

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка кастомного модуля OpenCart
Середня
~3-5 робочих днів
Часті питання

Наші компетенції:

Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка кастомного модуля OpenCart

Стандартний функціонал OpenCart покриває більшість базових e-commerce завдань, але рано чи пізно виникають вимоги, які виходять за рамки налаштувань: інтеграція з конкретною обліковою системою, нестандартна логіка ціноутворення, специфічна форма замовлення, кастомний блок на вітрині. Це завдання для розширень (extension) — основного механізму розширення OpenCart.

Типи розширень OpenCart

OpenCart 4.x структурує розширення за типами:

Тип Назначення
module Блоки на вітрині (баннери, хіти, виборки товарів)
payment Платіжні шлюзи
shipping Методи доставки з розрахунком вартості
total Позиції в сумарній вартості замовлення (знижки, збори, податки)
report Звіти в панелі адміністратора
event Обробники подій системи (гаки)
theme Теми оформлення
language Мовні пакети

Більшість кастомних завдань вирішується через module (вітрина) або event (інтеграції, модифікатори бізнес-логіки).

Структура кастомного модуля

Модуль розташовується в папці extension/{vendor_name}/:

extension/
└── myvendor/
    ├── admin/
    │   ├── controller/
    │   │   └── module/
    │   │       └── mymodule.php
    │   ├── language/
    │   │   ├── en-gb/module/mymodule.php
    │   │   └── ru-ru/module/mymodule.php
    │   ├── model/
    │   │   └── module/
    │   │       └── mymodule.php
    │   └── view/
    │       └── template/module/
    │           └── mymodule.twig
    └── catalog/
        ├── controller/
        │   └── module/
        │       └── mymodule.php
        ├── language/
        │   ├── en-gb/module/mymodule.php
        │   └── ru-ru/module/mymodule.php
        ├── model/
        │   └── module/
        │       └── mymodule.php
        └── view/
            └── template/module/
                └── mymodule.twig

Приклад: модуль «Товари з тієї ж категорії»

Практичний приклад — модуль, що показує товари з тієї ж категорії, що й переглядаємий товар.

Admin Controller (admin/controller/module/related_category.php):

<?php
namespace Opencart\Admin\Controller\Extension\Myvendor\Module;

class RelatedCategory extends \Opencart\System\Engine\Controller
{
    public function index(): void
    {
        $this->load->language('extension/myvendor/module/related_category');

        $this->document->setTitle($this->language->get('heading_title'));

        $data['heading_title'] = $this->language->get('heading_title');

        // Налаштування модуля з форми
        $data['limit'] = $this->config->get('module_related_category_limit') ?: 6;
        $data['status'] = $this->config->get('module_related_category_status');

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

        $this->response->setOutput($this->load->view(
            'extension/myvendor/module/related_category',
            $data
        ));
    }

    public function save(): void
    {
        $this->load->language('extension/myvendor/module/related_category');

        if ($this->request->server['REQUEST_METHOD'] == 'POST') {
            $this->load->model('setting/setting');
            $this->model_setting_setting->editSetting('module_related_category', [
                'module_related_category_status' => $this->request->post['status'],
                'module_related_category_limit'  => (int) $this->request->post['limit'],
            ]);

            $this->response->redirect($this->url->link(
                'marketplace/extension',
                'user_token=' . $this->session->data['user_token'] . '&type=module'
            ));
        }
    }
}

Catalog Controller (catalog/controller/module/related_category.php):

<?php
namespace Opencart\Catalog\Controller\Extension\Myvendor\Module;

class RelatedCategory extends \Opencart\System\Engine\Controller
{
    public function index(array $setting): string
    {
        // Працює тільки на сторінці товара
        if ($this->router->getPath() !== 'product/product') {
            return '';
        }

        $product_id = (int) ($this->request->get['product_id'] ?? 0);
        if (!$product_id) {
            return '';
        }

        $this->load->model('extension/myvendor/module/related_category');
        $this->load->model('tool/image');

        $limit = $setting['limit'] ?? 6;

        $products = $this->model_extension_myvendor_module_related_category
            ->getProductsFromSameCategory($product_id, $limit);

        if (!$products) {
            return '';
        }

        $data['products'] = [];

        foreach ($products as $product) {
            $data['products'][] = [
                'product_id' => $product['product_id'],
                'name'       => $product['name'],
                'href'       => $this->url->link('product/product', 'product_id=' . $product['product_id']),
                'thumb'      => $this->model_tool_image->resize(
                    $product['image'],
                    $this->config->get('config_image_related_width'),
                    $this->config->get('config_image_related_height')
                ),
                'price'      => $this->currency->format(
                    $this->tax->calculate($product['price'], $product['tax_class_id'], true),
                    $this->session->data['currency']
                ),
            ];
        }

        return $this->load->view(
            'extension/myvendor/module/related_category',
            $data
        );
    }
}

Catalog Model:

<?php
namespace Opencart\Catalog\Model\Extension\Myvendor\Module;

class RelatedCategory extends \Opencart\System\Engine\Model
{
    public function getProductsFromSameCategory(int $productId, int $limit): array
    {
        $query = $this->db->query("
            SELECT DISTINCT p.product_id, pd.name, p.image, p.price, p.tax_class_id
            FROM oc_product p
            JOIN oc_product_description pd
                ON p.product_id = pd.product_id AND pd.language_id = '" . (int) $this->config->get('config_language_id') . "'
            JOIN oc_product_to_category ptc
                ON p.product_id = ptc.product_id
            WHERE ptc.category_id IN (
                SELECT category_id FROM oc_product_to_category WHERE product_id = '" . $productId . "'
            )
            AND p.product_id != '" . $productId . "'
            AND p.status = 1
            AND p.date_available <= NOW()
            ORDER BY RAND()
            LIMIT " . $limit . "
        ");

        return $query->rows;
    }
}

Система подій (Events)

Для модифікації існуючої поведінки без правки ядра — події:

// Реєстрація події в методі встановлення модуля:
$this->load->model('setting/event');
$this->model_setting_event->addEvent([
    'code'        => 'myvendor_related_category',
    'description' => 'Add category data to product page',
    'trigger'     => 'catalog/controller/product/product/before',
    'action'      => 'extension/myvendor/event/product.before',
    'status'      => true,
    'sort_order'  => 0,
]);

Обробник події:

// catalog/controller/event/product.php
class Product extends \Opencart\System\Engine\Controller
{
    public function before(\Opencart\System\Engine\Action &$route, array &$args, mixed &$output): void
    {
        // Включається до завантаження контролера product/product
        // Можна додати дані в $this->registry або модифікувати $args
    }

    public function after(\Opencart\System\Engine\Action &$route, array &$args, mixed &$output): void
    {
        // Включається після генерації HTML сторінки товара
        // Тут можна інжектувати HTML-блоки в $output через str_replace
        $output = str_replace(
            '<!-- related_products_placeholder -->',
            $this->getRelatedCategoryHtml(),
            $output
        );
    }
}

Приклад: модуль інтеграції з 1С (webhook)

Приймання даних від 1С для оновлення остатків:

// catalog/controller/api/stock_update.php
class StockUpdate extends \Opencart\System\Engine\Controller
{
    public function index(): void
    {
        // Перевірка API-ключа
        $apiKey = $this->request->server['HTTP_X_API_KEY'] ?? '';
        if ($apiKey !== $this->config->get('module_1c_api_key')) {
            $this->response->addHeader('HTTP/1.0 403 Forbidden');
            $this->response->setOutput(json_encode(['error' => 'Unauthorized']));
            return;
        }

        $payload = json_decode(file_get_contents('php://input'), true);

        $this->load->model('catalog/product');

        $updated = 0;
        foreach ($payload['items'] ?? [] as $item) {
            $products = $this->model_catalog_product->getProductsByModel($item['sku']);
            foreach ($products as $product) {
                $this->model_catalog_product->editProduct($product['product_id'], [
                    'quantity' => max(0, (int) $item['quantity']),
                ]);
                $updated++;
            }
        }

        $this->response->addHeader('Content-Type: application/json');
        $this->response->setOutput(json_encode(['updated' => $updated]));
    }
}

Установник модуля (install/uninstall)

public function install(): void
{
    // Створюємо таблиці якщо потрібні
    $this->db->query("
        CREATE TABLE IF NOT EXISTS `oc_mymodule_log` (
            `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
            `message` TEXT NOT NULL,
            `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (`id`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    ");

    // Реєструємо події
    $this->load->model('setting/event');
    // ...

    // Налаштування за замовчуванням
    $this->load->model('setting/setting');
    $this->model_setting_setting->editSetting('module_mymodule', [
        'module_mymodule_status' => 1,
        'module_mymodule_limit'  => 6,
    ]);
}

public function uninstall(): void
{
    $this->db->query("DROP TABLE IF EXISTS `oc_mymodule_log`");

    $this->load->model('setting/event');
    $this->model_setting_event->deleteEventByCode('myvendor_mymodule');
}

Упаковка модуля для Extension Installer

Структура ZIP-архіву для завантаження через Extension Installer:

mymodule.ocmod.zip
├── extension/
│   └── myvendor/
│       ├── admin/...
│       └── catalog/...
└── install.json

install.json:

{
    "name": "My Custom Module",
    "version": "1.0.0",
    "author": "My Company",
    "link": "https://mycompany.ua"
}

Терміни розробки

  • Простий витринний модуль (блок на сторінку): 1–2 дні
  • Модуль з налаштуваннями в адмінці + вивід на вітрині: 2–3 дні
  • Платіжний шлюз (новий провайдер): 3–5 днів
  • Метод доставки з розрахунком через API: 2–4 дні
  • Інтеграція з зовнішною системою (1С, ERP) через webhook: 3–5 днів
  • Кастомна логіка ціноутворення (знижки, групи клієнтів): 3–5 днів