Работа с базой OpenCart: структура таблиц и связей
В OpenCart карточка товара, заказ и категория хранятся не в одной таблице. Разбираем схему OpenCart 3.x, связи между сущностями, мультиязычность, префиксы и безопасные SQL-запросы для диагностики.

В OpenCart 3.x один товар обычно занимает несколько таблиц: основные свойства лежат в product, название и описание — в product_description, категории — в product_to_category, магазины — в product_to_store. Если искать всё в одной таблице, можно получить неполные данные, задублировать строки или случайно изменить каталог. Ниже — практическая карта базы OpenCart, которой достаточно для чтения данных, диагностики и подготовки собственных отчётов.
Как устроена база OpenCart 3.x
OpenCart хранит данные в MySQL или MariaDB. При установке движок добавляет к именам таблиц префикс из конфигурации. В стандартной установке это часто oc_, но рассчитывать на него нельзя: администратор мог выбрать другой вариант.
Ищите префикс в файлах config.php и admin/config.php — там задана константа DB_PREFIX. В SQL-примерах ниже используется oc_. Перед запуском замените его на свой префикс.
Главное правило схемы OpenCart: сначала найдите идентификатор сущности, затем проверьте таблицы связей, языка, магазина и статуса. Самая важная информация часто находится не рядом с основной записью.
Сноска. Нормализация — способ хранить связанные данные в отдельных таблицах, чтобы не повторять один и тот же текст или идентификатор в каждой строке. В OpenCart это особенно заметно на товарах, категориях и заказах.
Главные сущности и их таблицы
Названия приведены для OpenCart 3.0.x. Расширения добавляют собственные таблицы, а модификаторы могут менять запросы и логику работы, но базовые сущности обычно сохраняются.
| Сущность | Основная таблица | Связанные таблицы | Что хранится |
|---|---|---|---|
| Товар | oc_product | product_description, product_image, product_to_category, product_to_store | Цена, остаток, артикулы, статус, изображение, даты |
| Категория | oc_category | category_description, category_path, category_to_store | Статус, родительская категория, сортировка |
| Заказ | oc_order | order_product, order_option, order_total, order_history | Покупатель, адреса, суммы, доставка, оплата, статус |
| Клиент | oc_customer | customer_login, address, customer_activity | Учётная запись, группа, контакты, адреса и активность |
| Магазин | oc_store | product_to_store, category_to_store | Домены и параметры дополнительных магазинов |
| Настройки | oc_setting | — | Параметры модулей, темы, магазина и служб |
Товары: почему одной таблицы недостаточно
Основные свойства
oc_product — центральная таблица товара. В ней находятся product_id, model, sku, quantity, price, image, manufacturer_id, tax_class_id, status, date_added и date_modified.
Поле image содержит путь к главному изображению, а не само изображение. Дополнительные картинки лежат в oc_product_image. Остаток в quantity — числовое значение, но его смысл зависит от настроек вычитания товара и логики конкретного модуля.
Название, описание и языки
oc_product_description связывает товар с языком через пару product_id и language_id. Здесь же хранятся name, description, meta_title, meta_description, meta_keyword и tag.
Поэтому запрос к oc_product без соединения с product_description вернёт товар, но не его название. Если языков несколько, соединение только по product_id создаст по одной строке на каждый язык.
SELECT
p.product_id,
pd.name,
p.model,
p.sku,
p.price,
p.quantity,
p.status
FROM oc_product AS p
JOIN oc_product_description AS pd
ON pd.product_id = p.product_id
AND pd.language_id = 1
WHERE p.status = 1
ORDER BY p.product_id DESC
LIMIT 100;
Число 1 — это идентификатор языка, а не универсальное значение. Проверьте его в oc_language или в настройках конкретного магазина. Если нужен определённый магазин, добавьте соединение с oc_product_to_store.
Категории и производители
Прямая связь товара с категорией хранится в oc_product_to_category. Один товар может иметь несколько строк и, соответственно, несколько категорий. Название категории находится не в oc_category, а в oc_category_description.
Поле parent_id в oc_category показывает непосредственного родителя. Для построения полного дерева используется oc_category_path. Это важно для хлебных крошек, фильтров и отчётов по всему разделу: простого соединения по parent_id недостаточно.
SELECT
p.product_id,
pd.name AS product_name,
cd.name AS category_name
FROM oc_product AS p
JOIN oc_product_description AS pd
ON pd.product_id = p.product_id
AND pd.language_id = 1
JOIN oc_product_to_category AS ptc
ON ptc.product_id = p.product_id
JOIN oc_category_description AS cd
ON cd.category_id = ptc.category_id
AND cd.language_id = 1
WHERE p.status = 1
ORDER BY cd.name, pd.name;
Такой отчёт может содержать несколько строк для одного товара, если он назначен в несколько категорий. Убирать дубли через DISTINCT стоит только после того, как вы поняли, какую связь хотите сохранить.
Заказы: заголовок, позиции и итоги
Заказ в OpenCart разделён на несколько уровней. oc_order хранит общие данные: номер, имя покупателя, адреса, способы оплаты и доставки, суммы и текущий order_status_id. Позиции находятся в oc_order_product, опции товара — в oc_order_option, а строки итогов — в oc_order_total.
Не рассчитывайте итог заказа, просто суммируя цены из order_product. В заказе могут быть скидки, доставка, налоги, сертификаты и другие строки из order_total. Для бухгалтерского или управленческого отчёта сначала определите, нужна ли вам сумма товара, доставка, налог или финальная сумма.
SELECT
o.order_id,
o.order_status_id,
o.date_added,
op.name AS product_name,
op.model,
op.quantity,
op.price,
op.total
FROM oc_order AS o
JOIN oc_order_product AS op
ON op.order_id = o.order_id
WHERE o.date_added >= '2025-01-01'
ORDER BY o.date_added DESC, o.order_id DESC;
В заказе сохраняется снимок названия, модели и цены позиции. Это отличается от связи с текущей карточкой товара: после переименования товара старый заказ не должен автоматически менять историческое название.
Настройки, пользователи и префиксы
oc_setting устроена как набор параметров: store_id, code, key, value, serialized. Одна настройка модуля может занимать несколько строк с общим значением code. В key обычно записывается имя параметра или его составная форма.
Значение в value может быть обычным текстом, числом или сериализованной структурой. Не редактируйте его вручную, если не знаете формат: изменение одного символа может сломать загрузку настроек модуля.
Администраторы находятся в oc_user, группы и разрешения — в oc_user_group. Пароли нельзя восстанавливать чтением поля из базы: там хранится хеш, а формат зависит от версии и механизма авторизации.
Как читать базу безопасно
Для разовых отчётов прямой SQL удобен, но для изменений каталога лучше использовать модель OpenCart или собственный модуль. Модель учитывает события, кеширование, форматирование и соглашения движка. Прямой UPDATE этого не делает.
| Подход | Когда использовать | Цена и риск |
|---|---|---|
| SQL только на чтение | Диагностика, выгрузка, поиск дублей | Быстро, но нужно самому контролировать связи и префикс |
| Модель OpenCart | Изменение товаров, категорий, заказов из расширения | Больше кода, зато сохраняется логика движка |
| Прямое изменение таблиц | Только после резервной копии и проверки на копии сайта | Высокий риск повредить связи, кеш и исторические данные |
Что ломается чаще всего
- Неверный префикс. Запрос к
oc_productзавершается ошибкой «Table ... doesn't exist». Возьмите фактическое значениеDB_PREFIXиз конфигурации. - Дубли из-за языка. После соединения с
product_descriptionтовар появляется несколько раз. Добавьте условие по нужномуlanguage_id. - Дубли из-за категорий. Один товар связан с несколькими категориями. Это нормальное поведение, а не обязательно ошибка данных.
- Пустой отчёт по товарам. Проверьте
status,product_to_store, идентификатор языка и фильтр по датам. - Сломанные настройки после ручного UPDATE. Верните резервную копию и проверьте, не было ли значение сериализованным. Инвалидация кеша — отдельная тема, здесь важен сам формат данных.
Где схема OpenCart перестаёт быть универсальной
Модули доставки, оплаты, фильтров, складского учёта и интеграций часто создают собственные таблицы. Их имена можно найти по префиксу расширения, в файлах установки модуля или через список таблиц базы. Нельзя переносить SQL от одного магазина к другому без проверки: набор модулей, версия OpenCart и структура данных могут отличаться.
Особенно осторожно работайте с заказами. Удаление товара из каталога не должно стирать его из order_product, потому что заказ хранит историческую информацию. Перед массовым изменением проверьте запрос на копии базы и сохраните резервную копию.
Что делать дальше
Сначала определите версию OpenCart и фактический префикс таблиц. Затем составьте карту нужного отчёта: основная сущность, таблица языка, таблица магазина, связи и статус. Для нового SQL-запроса проверьте количество строк до и после каждого JOIN — так быстрее всего обнаруживаются неожиданные дубли.
После этого сравните результат с тем, что показывает административная панель. Если цифры расходятся, ищите не «неправильную базу», а фильтры магазина, языка, статуса, модификаторы и таблицы установленного расширения.
Инфографика для маркетплейса: Три главных преимущества товара короткими фразами, выноски в безопасной зоне превью, точный размер площадки.
Спросить за 60 ₽Источники: OpenCart 3.x: исходная SQL-схема установки
Частые вопросы
Где в OpenCart хранится название товара?
Почему один товар появляется в SQL-отчёте несколько раз?
Можно ли менять цену товара напрямую в базе?
Что делать, если таблицы oc_product нет?
Почему SQL-запрос не показывает товары из административной панели?
Можно ли удалить товар из product, не затронув заказы?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.