Сценарий типичный: в магазине есть товары, которые нельзя отправлять обычной доставкой — например, крупногабаритные позиции, цифровые товары с отдельной логикой, хрупкие наборы или товары с самовывозом только из конкретного склада. Если в корзине появляется такой товар, часть способов доставки нужно скрыть, а иногда и оставить только один вариант.
В WooCommerce это лучше решать на уровне фильтра расчёта доставки, а не через ручную правку каждого метода в админке. Так логика остаётся прозрачной и не ломается при обновлении плагинов.
Когда это действительно нужно
Отключение способов доставки по составу корзины полезно, если:
- есть товары, которые нельзя отправлять почтой или курьером;
- отдельные категории должны идти только самовывозом;
- для части ассортимента доставка доступна только по определённым зонам;
- нужно убрать конфликтующие методы, когда в корзине смешаны обычные и нестандартные товары.
Если задача сводится к одному-двум товарам, код обычно надёжнее, чем набор условных правил в нескольких плагинах. Но если логика зависит от менеджеров, складов и регионов, сначала стоит проверить, не проще ли решить это настройками зон доставки.
Диагностика: почему метод доставки не скрывается
Перед правкой кода полезно понять, где именно возникает проблема. В WooCommerce доставка рассчитывается по зонам, а затем фильтруется по доступным методам. Если метод всё равно показывается, обычно причина одна из этих:
- условие проверяет не те товары — например, смотрит только на текущую страницу товара, а не на корзину;
- используется неправильный хук;
- в корзине есть вариативный товар, а код проверяет только родительский ID;
- кэш доставки или кэш страницы мешает увидеть изменения;
- метод доставки отключается слишком поздно, уже после формирования списка.
Для отладки удобно временно вывести состав корзины в лог. Это помогает убедиться, что нужный товар действительно попадает в проверку.
add_action('woocommerce_before_calculate_totals', function () {
if (!function_exists('WC') || !WC()->cart) {
return;
}
foreach (WC()->cart->get_cart() as $cart_item) {
error_log('Cart product ID: ' . $cart_item['product_id']);
error_log('Variation ID: ' . ($cart_item['variation_id'] ?? 0));
}
});Этот код не нужен в продакшене постоянно. Он только помогает проверить, какие ID реально видит WooCommerce.
Как отключить способы доставки для конкретных товаров
Самый практичный вариант — использовать фильтр woocommerce_package_rates. Он позволяет изменить список доступных методов доставки уже после расчёта, но до вывода на странице оформления заказа.
Ниже пример: если в корзине есть товар с определённым ID, скрываем все методы, кроме самовывоза. При необходимости список можно расширить под свои правила.
add_filter('woocommerce_package_rates', function ($rates, $package) {
if (is_admin() && !defined('DOING_AJAX')) {
return $rates;
}
$restricted_product_ids = [123, 456]; // ID товаров, для которых нужна особая доставка
$has_restricted_product = false;
foreach ($package['contents'] as $item) {
$product_id = (int) $item['product_id'];
$variation_id = !empty($item['variation_id']) ? (int) $item['variation_id'] : 0;
if (in_array($product_id, $restricted_product_ids, true) || in_array($variation_id, $restricted_product_ids, true)) {
$has_restricted_product = true;
break;
}
}
if (!$has_restricted_product) {
return $rates;
}
foreach ($rates as $rate_id => $rate) {
// Оставляем только самовывоз
if (strpos($rate_id, 'local_pickup') === false) {
unset($rates[$rate_id]);
}
}
return $rates;
}, 10, 2);Если вам нужно не оставить один метод, а наоборот скрыть только конкретные способы, меняется только условие внутри цикла. Например, можно убрать flat_rate:1 и оставить всё остальное.
add_filter('woocommerce_package_rates', function ($rates, $package) {
$blocked_rate_ids = ['flat_rate:1', 'free_shipping:3'];
foreach ($rates as $rate_id => $rate) {
if (in_array($rate_id, $blocked_rate_ids, true)) {
unset($rates[$rate_id]);
}
}
return $rates;
}, 10, 2);Если нужно ограничить доставку по категории товара
Часто удобнее проверять не отдельные ID, а категорию. Это особенно полезно, когда ассортимент меняется, а код должен оставаться стабильным.
add_filter('woocommerce_package_rates', function ($rates, $package) {
$restricted_category_slug = 'khrupkie-tovary';
$has_restricted_category = false;
foreach ($package['contents'] as $item) {
$product_id = (int) $item['product_id'];
if (has_term($restricted_category_slug, 'product_cat', $product_id)) {
$has_restricted_category = true;
break;
}
}
if (!$has_restricted_category) {
return $rates;
}
foreach ($rates as $rate_id => $rate) {
if (strpos($rate_id, 'local_pickup') === false) {
unset($rates[$rate_id]);
}
}
return $rates;
}, 10, 2);Сравнение подходов: код, настройки, плагин
| Подход | Когда подходит | Минус |
|---|---|---|
| Настройки зон доставки WooCommerce | Если правило зависит только от региона | Не учитывает состав корзины |
Код через woocommerce_package_rates | Если логика зависит от товаров, категорий, вариаций | Нужно поддерживать код |
| Плагин для условной доставки | Если правил много и ими управляет менеджер | Дополнительная нагрузка и зависимость от расширения |
Если у вас один понятный сценарий, код обычно проще в сопровождении. Если правил много и они часто меняются, лучше смотреть в сторону специализированного решения, чтобы не держать бизнес-логику в теме оформления.
Пошагово: как внедрить без поломки checkout
- Определите, по чему именно строится правило: ID товара, категория, вариация или комбинация признаков.
- Добавьте код в дочернюю тему или в собственный мини-плагин, а не в файлы WooCommerce.
- Проверьте, что условие срабатывает на товаре в корзине, а не только на странице товара.
- Очистите кэш страницы и, если используется кэш доставки, обновите расчёт в корзине.
- Протестируйте несколько сценариев: только обычный товар, только ограниченный товар, смешанная корзина.
Если вы работаете через мини-плагин, структура может быть такой:
<?php
/**
* Plugin Name: WC Shipping Restrictions
*/
add_filter('woocommerce_package_rates', function ($rates, $package) {
$restricted_product_ids = [123, 456];
$has_restricted_product = false;
foreach ($package['contents'] as $item) {
if (in_array((int) $item['product_id'], $restricted_product_ids, true)) {
$has_restricted_product = true;
break;
}
}
if (!$has_restricted_product) {
return $rates;
}
foreach ($rates as $rate_id => $rate) {
if (strpos($rate_id, 'local_pickup') === false) {
unset($rates[$rate_id]);
}
}
return $rates;
}, 10, 2);Как проверить, что решение сработало
Проверка должна быть не визуальной «на глаз», а по конкретным сценариям. Иначе легко пропустить ситуацию, когда метод скрывается только для части корзины.
- Добавьте в корзину обычный товар — должны показываться все ожидаемые методы.
- Добавьте ограниченный товар — должны остаться только разрешённые способы доставки.
- Смешайте обычный и ограниченный товар — проверьте, что правило работает именно для всей корзины, а не только для одной позиции.
- Откройте оформление заказа в режиме инкогнито, чтобы исключить влияние сессии и кэша браузера.
- Если включён кэш на сервере, убедитесь, что страница checkout и cart не кэшируются как статические.
Для дополнительной проверки можно временно включить логирование и посмотреть, какие rate ID приходят в фильтр. Это особенно полезно, если вы не уверены, как именно называется метод в конкретной зоне доставки.
Частые ошибки и как их исправить
Проверяют только product_id, но забывают про вариации
У вариативного товара в корзине может быть отдельный variation_id. Если ограничение завязано на конкретную вариацию, проверяйте оба значения.
Скрывают методы по неправильному rate_id
В WooCommerce идентификатор метода доставки выглядит не как просто flat_rate, а как flat_rate:1 или похожая строка. Сначала посмотрите фактический rate_id, потом уже пишите условие.
Добавляют код в файл темы, который потом меняется
Если правило критично для бизнеса, лучше вынести его в мини-плагин. Иначе при смене темы логика доставки исчезнет.
Не учитывают кэш
Если на сайте есть кэширование страниц или агрессивный оптимизатор, checkout и cart должны быть исключены из кэша. Иначе вы будете тестировать не WooCommerce, а сохранённую копию страницы.
Слишком рано отключают все методы
Иногда разработчики фильтруют методы до того, как WooCommerce собрал пакет доставки. В результате список пустой даже там, где правило не должно срабатывать. Для таких задач безопаснее работать через woocommerce_package_rates.
Безопасность и поддержка
Если правило доставки влияет на деньги и логистику, не держите его в произвольном сниппете без контроля версий. Для таких изменений полезно:
- хранить код в Git;
- добавить комментарий, какие товары или категории подпадают под ограничение;
- проверять изменения на staging-сайте перед публикацией;
- не использовать сомнительные плагины с неясной поддержкой, если задача решается штатным фильтром WooCommerce.
Когда логика начинает разрастаться, имеет смысл вынести правила в отдельный класс или хотя бы в конфигурационный массив. Это проще сопровождать, чем набор разрозненных if по всему проекту.
Если вам нужно не только скрывать доставку, но и управлять другими ограничениями WooCommerce, иногда удобнее собрать несколько точечных правил в одном техническом плагине. Для задач SEO, очистки дублей и обслуживания сайта можно отдельно посмотреть Clearfy Pro: https://wpshop.ru/plugins/clearfy.