В штатном OpenCart «сопутствующие» — это не «к удочке леска», а взаимная дружба навек. Повесил на товар A товар B — и B сразу орёт «смотрите, я с A». Для SEO и для честного апсейла это часто вредно: чехол к телефону должен светиться на телефоне, а не наоборот.
Модуль за 500 рублей покупать не будем. Вечер, чай, один файл админки. Делаем связь в одну сторону: A знает про B, B про A молчит, пока сами не скажете.
Файл: admin/model/catalog/product.php. Ищем методы добавления и правки товара — там пачка запросов в product_related.
Было (двусторонняя любовь, OpenCart 2.x / ocStore, кусок editProduct):
if (isset($data['product_related'])) {
foreach ($data['product_related'] as $related_id) {
$this->db->query("DELETE FROM " . DB_PREFIX . "product_related WHERE product_id = '" . (int)$product_id . "' AND related_id = '" . (int)$related_id . "'");
$this->db->query("INSERT INTO " . DB_PREFIX . "product_related SET product_id = '" . (int)$product_id . "', related_id = '" . (int)$related_id . "'");
$this->db->query("DELETE FROM " . DB_PREFIX . "product_related WHERE product_id = '" . (int)$related_id . "' AND related_id = '" . (int)$product_id . "'");
$this->db->query("INSERT INTO " . DB_PREFIX . "product_related SET product_id = '" . (int)$related_id . "', related_id = '" . (int)$product_id . "'");
}
}
Стало — оставляем только прямую запись. Две нижние строки комментируем, не вырезаем: через полгода вспомните, зачем они были.
if (isset($data['product_related'])) {
foreach ($data['product_related'] as $related_id) {
$this->db->query("DELETE FROM " . DB_PREFIX . "product_related WHERE product_id = '" . (int)$product_id . "' AND related_id = '" . (int)$related_id . "'");
$this->db->query("INSERT INTO " . DB_PREFIX . "product_related SET product_id = '" . (int)$product_id . "', related_id = '" . (int)$related_id . "'");
// односторонняя связь: обратную не пишем
// $this->db->query("DELETE FROM " . DB_PREFIX . "product_related WHERE product_id = '" . (int)$related_id . "' AND related_id = '" . (int)$product_id . "'");
// $this->db->query("INSERT INTO " . DB_PREFIX . "product_related SET product_id = '" . (int)$related_id . "', related_id = '" . (int)$product_id . "'");
}
}
То же самое повторите в addProduct — иначе новые товары опять начнут дружить взаимно, а старые будут «как повезёт».
Удаление товара. Если снести карточку A, OpenCart подчищает и чужие связи «на меня». Это ок. Блок обычно такой:
$this->db->query("DELETE FROM " . DB_PREFIX . "product_related WHERE product_id = '" . (int)$product_id . "'");
$this->db->query("DELETE FROM " . DB_PREFIX . "product_related WHERE related_id = '" . (int)$product_id . "'");
Второй запрос лучше оставить: иначе мёртвый id будет торчать в чужих карточках. Речь только про создание обратной связи, не про уборку мусора.
Где править в OpenCart 3: тот же файл, те же четыре запроса, только код чуть разъехался по строкам. Ищите product_related по файлу — попаданий немного.
Фронт трогать не надо. Шаблон catalog/view/theme/.../template/product/product.twig (или .tpl в двойке) как выводил $products из $product_related, так и выводит. Меняется только то, что лежит в таблице.
Проверить за 30 секунд:
- Товар A → вкладка «Связи» → прикрутили B → сохранили.
- В карточке A на витрине B есть.
- В карточке B товара A нет. Если есть — значит правили не тот метод или кэш модификаций (OCMOD/vQmod) перехватывает модель. Тогда правьте не ядро, а модификатор, либо отключите чужой «related».
SQL для параноиков, вдруг руками в базе наковыряли двусторонние хвосты:
SELECT product_id, related_id FROM oc_product_related
WHERE product_id = 15 OR related_id = 15;
Префикс oc_ подставьте свой. Нужна одна строка 15 → 88, а не пара 15 → 88 и 88 → 15.
На этом всё. Чехол продаём к телефону, а не телефон к чехлу. Бесплатно, без модуля и без «рекомендуем ещё вот это случайное».
Алексей:
16 Jul 2017г. в 11:38
Спасибо огромное!!! Очень напрягали эти ответные связи. А теперь все ОК!