Розробка кастомної теми OpenCart

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

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

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

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

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка кастомної теми OpenCart
Середня
~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 — компроміс між швидкістю запуску і відповідністю бренду. Кастомна тема дає повний контроль над HTML-структурою, CSS, продуктивністю і доступністю. При грамотній реалізації кастомна тема швидша за готові теми завдяки відсутності невикористаного коду.

Архітектура тем в OpenCart 4.x

OpenCart 4.x використовує Twig як шаблонізатор замість PHP-шаблонів у версіях 3.x. Це змінило підхід до розробки тем.

Структура кастомної теми:

catalog/view/theme/{theme_name}/
├── template/
│   ├── common/
│   │   ├── header.twig
│   │   ├── footer.twig
│   │   ├── cart.twig
│   │   └── search.twig
│   ├── product/
│   │   ├── category.twig       ← каталог
│   │   ├── product.twig        ← карточка товара
│   │   ├── search.twig
│   │   └── special.twig
│   ├── checkout/
│   │   ├── cart.twig
│   │   └── checkout.twig
│   └── account/
│       ├── login.twig
│       ├── register.twig
│       └── order.twig
└── stylesheet/
    └── (для мінімальних переопределень)

CSS і JS підключаються не через папку теми, а через події і конфігурацію контролера.

Наслідування від default-теми

Кастомна тема може бути повністю незалежною або розширювати тему default. Для другого варіанту — в налаштуваннях указується батьківська тема:

Admin → System → Settings → Store → Theme → Parent Theme: default

Тоді якщо в папці кастомної теми нема потрібного файлу — OpenCart бере його з default. Це прискорює розробку: переопределяємо тільки змінені шаблони.

Реєстрація теми

// Створюємо файл extension/myshop/catalog/controller/startup/theme.php
// (або через event system)

// У таблиці oc_extension реєструємо тему:
INSERT INTO `oc_extension` (`extension_id`, `extension`, `type`, `code`)
VALUES (NULL, 'opencart', 'theme', 'myshop');

// У oc_setting прописуємо шлях:
INSERT INTO `oc_setting` (`store_id`, `code`, `key`, `value`)
VALUES (0, 'config', 'config_theme', 'myshop');

Або через Extension Installer, якщо тема упакована як розширення.

Базовий шаблон header.twig

<!DOCTYPE html>
<html lang="{{ lang }}" dir="{{ direction }}">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{ title }}</title>
    <meta name="description" content="{{ description }}">

    {% if canonical %}
    <link rel="canonical" href="{{ canonical }}">
    {% endif %}

    {# Підключаємо Bootstrap або власний CSS #}
    <link rel="stylesheet" href="{{ stylesheet }}">

    {% for style in styles %}
    <link rel="stylesheet" type="text/css" href="{{ style.href }}" media="{{ style.media }}">
    {% endfor %}
</head>
<body class="{{ class }}">

<header class="site-header">
    <div class="container">
        <a class="site-logo" href="{{ home }}">
            {% if logo %}
            <img src="{{ logo }}" alt="{{ name }}" loading="eager">
            {% else %}
            <span>{{ name }}</span>
            {% endif %}
        </a>

        <nav class="site-nav">
            {% for category in categories %}
            <a href="{{ category.href }}" {% if category.children %}class="has-dropdown"{% endif %}>
                {{ category.name }}
                {% if category.children %}
                <ul class="dropdown">
                    {% for child in category.children %}
                    <li><a href="{{ child.href }}">{{ child.name }}</a></li>
                    {% endfor %}
                </ul>
                {% endif %}
            </a>
            {% endfor %}
        </nav>

        <div class="header-actions">
            <a href="{{ cart }}" class="cart-icon" data-count="{{ cart_count }}">
                Кошик ({{ cart_quantity }})
            </a>
            {% if logged %}
            <a href="{{ account }}">Кабінет</a>
            {% else %}
            <a href="{{ login }}">Увійти</a>
            {% endif %}
        </div>
    </div>
</header>

Карточка товара — product.twig

Ключові змінні, доступні в шаблоні карточки товара:

{# Основні дані #}
{{ product_id }}, {{ name }}, {{ description }}, {{ model }}
{{ price }}, {{ special }}, {{ tax }}
{{ rating }}, {{ reviews }}
{{ manufacturer }}, {{ manufacturer_href }}

{# Зображення #}
{{ thumb }}       {# основне зображення #}
{{ images }}      {# масив додаткових зображень #}

{# Опції #}
{% for option in options %}
    {{ option.name }}, {{ option.type }}
    {% for value in option.product_option_value %}
        {{ value.name }}, {{ value.price }}
    {% endfor %}
{% endfor %}

{# SEO #}
{{ meta_title }}, {{ meta_description }}, {{ meta_keyword }}
{{ canonical }}

Шаблон з галереєю та вибором опцій:

<section class="product-page">
    <div class="product-gallery">
        <img id="product-image-main"
             src="{{ thumb }}"
             alt="{{ name }}"
             loading="eager"
             fetchpriority="high">

        <div class="thumbnails">
            <img src="{{ thumb }}" data-src="{{ image }}" class="thumb active">
            {% for image in images %}
            <img src="{{ image.thumb }}" data-src="{{ image.popup }}" class="thumb">
            {% endfor %}
        </div>
    </div>

    <div class="product-info">
        <h1>{{ name }}</h1>

        <div class="product-price">
            {% if special %}
            <span class="price-old">{{ price }}</span>
            <span class="price-new">{{ special }}</span>
            {% else %}
            <span class="price-current">{{ price }}</span>
            {% endif %}
        </div>

        {% for option in options %}
        <div class="product-option">
            <label>{{ option.name }}{% if option.required %} *{% endif %}</label>

            {% if option.type == 'select' %}
            <select name="option[{{ option.product_option_id }}]">
                <option value="">— Виберіть —</option>
                {% for value in option.product_option_value %}
                <option value="{{ value.product_option_value_id }}"
                        {% if value.price %}data-price="{{ value.price }}"{% endif %}>
                    {{ value.name }}{% if value.price %} (+ {{ value.price }}){% endif %}
                </option>
                {% endfor %}
            </select>

            {% elseif option.type == 'radio' %}
            <div class="option-radios">
                {% for value in option.product_option_value %}
                <label class="option-radio">
                    <input type="radio"
                           name="option[{{ option.product_option_id }}]"
                           value="{{ value.product_option_value_id }}">
                    {{ value.name }}
                </label>
                {% endfor %}
            </div>
            {% endif %}
        </div>
        {% endfor %}

        <div class="quantity-row">
            <input type="number" name="quantity" value="1" min="1">
            <button id="btn-cart" data-id="{{ product_id }}">У кошик</button>
        </div>
    </div>
</section>

JavaScript у теміс

OpenCart 4.x використовує власний AJAX для кошика. Розширення через події:

// catalog/view/javascript/myshop/theme.js

// Додавання в кошик
document.querySelectorAll('[data-id]').forEach(btn => {
    btn.addEventListener('click', async function() {
        const productId = this.dataset.id
        const quantity = document.querySelector('[name="quantity"]')?.value || 1
        const options = {}

        document.querySelectorAll('[name^="option"]').forEach(el => {
            const match = el.name.match(/option\[(\d+)\]/)
            if (match && el.value) {
                options[match[1]] = el.value
            }
        })

        const response = await fetch('index.php?route=checkout/cart.add', {
            method: 'POST',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            body: new URLSearchParams({
                product_id: productId,
                quantity,
                ...Object.fromEntries(
                    Object.entries(options).map(([k, v]) => [`option[${k}]`, v])
                )
            })
        })

        const data = await response.json()

        if (data.success) {
            updateCartWidget(data)
            showNotification(data.success)
        } else {
            showErrors(data.error)
        }
    })
})

Підключення ресурсів теми

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

// У event-обробнику або startup-контролері теми:
$this->document->addStyle(
    'catalog/view/javascript/myshop/css/theme.css',
    'screen',
    100  // sort_order
);

$this->document->addScript(
    'catalog/view/javascript/myshop/js/theme.js',
    'footer',
    100
);

Для production — збірка через Vite або Webpack: мініфікація, хеш-суффікс для cache busting:

# package.json у корені теми
npm run build
# Генерує: theme.abc123.css, theme.abc123.js

Адаптивна верстка

OpenCart-тема повинна коректно працювати на мобільних. Breakpoints:

/* Mobile-first approach */
.product-grid { grid-template-columns: repeat(2, 1fr); gap: 16px; }

@media (min-width: 768px) {
    .product-grid { grid-template-columns: repeat(3, 1fr); }
}

@media (min-width: 1200px) {
    .product-grid { grid-template-columns: repeat(4, 1fr); }
}

Зображення — з loading="lazy" для всього, крім першого екрану, srcset для різних щільностей екрану.

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

  • Верстка header + footer + навігація: 1–2 дні
  • Головна сторінка (баннер, категорії, хіти): 1–2 дні
  • Каталог з фільтрами: 1–2 дні
  • Карточка товара з галереєю + опціями: 1–2 дні
  • Кошик + оформлення замовлення: 2–3 дні
  • Особистий кабінет + сторінка замовлення: 1–2 дні
  • Адаптивність + кросбраузерність: 1–2 дні

Разом: 1,5–2 тижні при наявності готового дизайну в Figma. Без дизайну — добавити 1–2 тижні на дизайн.