В WooCommerce часто нужен не «умный» каталог, а простое правило: если в корзине уже лежит один товар, второй туда добавлять нельзя. Это встречается у расходников и оборудования, у товаров с разной логистикой, у лицензий с разными условиями или у позиций, которые нельзя отправлять одной посылкой. Если ограничение не поставить на уровне корзины, менеджер потом вручную правит заказы, а покупатель получает непредсказуемую ошибку уже после оплаты.
Ниже — рабочий вариант без плагинов: проверяем корзину, блокируем несовместимые товары и показываем понятное сообщение. Подход подходит, если правило простое и его можно описать через ID товаров или категорий.
Когда это действительно нужно
Сценарий обычно выглядит так: товар A можно купить только отдельно, а товар B — только без A. Или наоборот: в одной корзине нельзя смешивать товары из разных групп. Если это ограничение связано не с маркетингом, а с реальной обработкой заказа, лучше решать его сразу в корзине, а не после оформления.
Если у вас десятки исключений и сложная логика по складам, регионам или способам доставки, код ниже можно использовать как основу, но дальше уже стоит выносить правила в отдельную таблицу или метаполя. Для простого кейса этого обычно не требуется.
Диагностика проблемы: где именно ломается сценарий
Перед правкой кода проверьте, на каком этапе возникает конфликт:
- товар можно добавить в корзину, но ошибка появляется только на checkout;
- несовместимый товар заменяет предыдущий без предупреждения;
- корзина очищается полностью, хотя нужно удалить только новый товар;
- ограничение работает в обычной корзине, но не срабатывает при AJAX-добавлении.
Если у вас включены кэш-плагины или мини-корзина обновляется через AJAX, важно тестировать не только страницу товара, но и быстрый сценарий добавления из каталога. Именно там чаще всего всплывают ошибки с сообщениями и редиректами.
Какой подход выбрать: код, плагин или ручная настройка
| Подход | Когда подходит | Минус |
|---|---|---|
| Код в теме или мини-плагине | Нужно одно-две точных бизнес-правила | Надо сопровождать код |
| Плагин для ограничений корзины | Много правил без разработки | Лишняя нагрузка и зависимость от стороннего решения |
| Ручная проверка заказов | Редкие кейсы и маленький поток | Ошибки доходят до оформления и поддержки |
Если правило стабильно и не меняется каждый день, код обычно надежнее. Если же ограничения будут часто редактироваться менеджером, лучше делать отдельную админ-настройку или использовать специализированный плагин.
Пошаговое решение: блокируем несовместимые товары в корзине
Самый понятный вариант — сравнивать ID товаров. Это удобно, когда несовместимость касается конкретных позиций. Код можно добавить в functions.php дочерней темы или в небольшой mu-plugin.
<?php
add_filter( 'woocommerce_add_to_cart_validation', 'wpaction_block_incompatible_products', 10, 3 );
function wpaction_block_incompatible_products( $passed, $product_id, $quantity ) {
if ( ! WC()->cart ) {
return $passed;
}
// Список несовместимых товаров: ключ = товар, значение = массив товаров, с которыми его нельзя смешивать.
$rules = array(
123 => array( 456, 789 ),
456 => array( 123 ),
789 => array( 123 ),
);
$cart_product_ids = array();
foreach ( WC()->cart->get_cart() as $cart_item ) {
$cart_product_ids[] = (int) $cart_item['product_id'];
}
if ( empty( $cart_product_ids ) ) {
return $passed;
}
if ( isset( $rules[ $product_id ] ) ) {
$conflicts = array_intersect( $cart_product_ids, $rules[ $product_id ] );
if ( ! empty( $conflicts ) ) {
wc_add_notice( 'Этот товар нельзя покупать вместе с выбранными позициями в корзине. Оформите отдельный заказ.', 'error' );
return false;
}
}
foreach ( $rules as $locked_product_id => $blocked_ids ) {
if ( in_array( $product_id, $blocked_ids, true ) && in_array( (int) $locked_product_id, $cart_product_ids, true ) ) {
wc_add_notice( 'Этот товар нельзя покупать вместе с выбранными позициями в корзине. Оформите отдельный заказ.', 'error' );
return false;
}
}
return $passed;
}Что делает этот код:
- срабатывает до добавления товара в корзину;
- собирает список уже лежащих товаров;
- сравнивает его с таблицей несовместимости;
- показывает сообщение и отменяет добавление.
Если вам нужно ограничение не по конкретным товарам, а по категориям, логику можно упростить: проверять has_term() для товара и текущих позиций корзины. Это удобнее, когда ассортимент меняется часто.
Вариант по категориям
<?php
add_filter( 'woocommerce_add_to_cart_validation', 'wpaction_block_by_category', 10, 3 );
function wpaction_block_by_category( $passed, $product_id, $quantity ) {
if ( ! WC()->cart ) {
return $passed;
}
$restricted_category = 'hazmat';
$product_terms = wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'slugs' ) );
if ( is_wp_error( $product_terms ) || ! in_array( $restricted_category, $product_terms, true ) ) {
return $passed;
}
foreach ( WC()->cart->get_cart() as $cart_item ) {
$cart_product_id = (int) $cart_item['product_id'];
$cart_terms = wp_get_post_terms( $cart_product_id, 'product_cat', array( 'fields' => 'slugs' ) );
if ( ! is_wp_error( $cart_terms ) && ! empty( array_intersect( array( $restricted_category ), $cart_terms ) ) ) {
continue;
}
wc_add_notice( 'Товары из этой категории нельзя смешивать с текущей корзиной.', 'error' );
return false;
}
return $passed;
}Этот вариант полезен, если правило завязано на тип товара, а не на отдельные карточки. Но у него есть компромисс: если категория используется для разных сценариев, ограничение станет слишком грубым.
Как сделать сообщение понятным покупателю
Фраза «ошибка добавления товара» не помогает никому. Лучше сразу писать, что именно делать дальше: удалить текущие позиции, оформить отдельный заказ или выбрать другой товар. Так вы снижаете количество обращений в поддержку.
Если хотите, можно заменить сообщение на более конкретное для разных правил. Например, для одного товара — «Этот товар оформляется отдельно», для другой группы — «Сначала завершите заказ на текущие позиции». Главное — не перегружать текст внутренними терминами склада.
Проверка результата после внедрения
После добавления кода проверьте сценарий не только вручную, но и в нескольких точках:
- добавление товара со страницы товара;
- добавление из каталога через AJAX;
- попытка добавить несовместимый товар в уже заполненную корзину;
- переход на checkout после очистки кэша;
- поведение на мобильной версии, если мини-корзина открывается отдельно.
Если правило работает, WooCommerce должен показать notice и не добавить товар. Корзина при этом остается без изменений. Для проверки удобно открыть корзину в режиме инкогнито и повторить сценарий с нуля, чтобы исключить влияние старых сессий.
Дополнительно можно посмотреть, не ломается ли оформление заказа при наличии купонов, доставки и налога. Само ограничение на добавление товара обычно не конфликтует с этими функциями, но в реальном магазине лучше пройти весь путь до оплаты.
Частые ошибки и как их исправить
Код добавили в родительскую тему
После обновления темы правило исчезает. Для таких правок используйте дочернюю тему или отдельный мини-плагин. Это особенно важно, если магазин живет долго и обновляется регулярно.
Проверка идет только по product_id, а в корзине вариации
Если у товара есть вариации, иногда нужно учитывать не только родительский товар, но и variation_id. Иначе ограничение сработает не на ту позицию или не сработает вообще. В сложных кейсах проверяйте оба значения в массиве корзины.
Сообщение не показывается при AJAX-добавлении
Обычно причина в том, что тема или плагин перехватывает вывод notices. Проверьте, выводятся ли стандартные уведомления WooCommerce на странице корзины. Если нет, сначала исправьте шаблон, а потом уже тестируйте ограничение.
Корзина очищается целиком
Так делать не стоит, если правило касается только несовместимости. Пользователь теряет уже выбранные товары и чаще всего уходит. Лучше отменять добавление нового товара и оставлять текущую корзину как есть.
Безопасность и производительность
Сам по себе такой код легкий, но есть несколько практических моментов. Не храните правила в случайных опциях без валидации, если потом будете редактировать их из админки. Не используйте тяжелые запросы к базе на каждом добавлении товара, если можно обойтись ID и категориями. И не вставляйте код в сторонний плагин, который может отключиться после обновления.
Если ограничений много, лучше вынести правила в отдельный массив или в настройку, которую читает только админ. Для магазинов с большим каталогом имеет смысл заранее продумать структуру: товарные ID, категории, атрибуты или метки. Это упростит поддержку и снизит риск ошибки при ручном редактировании.
Когда нужно не только блокировать корзину, но и управлять другими сценариями магазина, иногда удобнее собрать набор точечных правок в одном месте. Для этого подходит, например, Clearfy Pro — не как замена коду, а как инструмент для части типовых задач по чистке и настройкам сайта: https://wpshop.ru/plugins/clearfy.
Если после внедрения ограничения покупатели все равно пытаются оформить несовместимые товары, проверьте не только код, но и путь добавления в корзину: быстрые кнопки, сторонние фильтры, мини-корзину и кэш на уровне страницы. В WooCommerce такие правила обычно ломаются не из-за самого хука, а из-за того, что тестируют не тот сценарий, который реально используют клиенты.