Диагностика задачи: когда нужно менять цену товара в корзине
Иногда в WooCommerce возникает необходимость динамически менять цену товара уже в корзине — например, для применения индивидуальных скидок, наценок, или корректировки цены в зависимости от пользовательских условий (роль пользователя, количество товаров, дата, промокод и др.). Изменять цену товара напрямую в базе не рекомендуется, поэтому используется хук woocommerce_before_calculate_totals, который позволяет корректировать цену на лету перед пересчётом итогов корзины.
Как работает хук woocommerce_before_calculate_totals
Хук вызывается до того, как WooCommerce пересчитывает итоговую стоимость корзины и каждого её элемента. В него передаётся объект корзины (WC_Cart), где можно получить все элементы корзины и изменить их цену с помощью метода set_price() у объекта WC_Cart_Item.
Пример базового использования хука:
add_action('woocommerce_before_calculate_totals', 'custom_change_price_in_cart', 10, 1);
function custom_change_price_in_cart($cart) {
if (is_admin() && !defined('DOING_AJAX')) return; // Защита от админки
foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
// Новая цена
$new_price = 1000; // Например фиксированная цена
$cart_item['data']->set_price($new_price);
}
}Пошаговое решение: динамическая корректировка цены с проверкой
Рассмотрим более практичный пример: скидка 10% для пользователей с ролью "оптовик" на все товары в корзине.
- Добавляем проверку роли пользователя.
- Проходим по каждому товару в корзине.
- Вычисляем новую цену с учётом скидки.
- Устанавливаем новую цену через
set_price().
add_action('woocommerce_before_calculate_totals', 'apply_wholesale_discount', 10, 1);
function apply_wholesale_discount($cart) {
if (is_admin() && !defined('DOING_AJAX')) return;
// Проверяем роль пользователя
if (!is_user_logged_in()) return;
$user = wp_get_current_user();
if (!in_array('wholesaler', (array) $user->roles)) return;
foreach ($cart->get_cart() as $cart_item) {
$original_price = $cart_item['data']->get_regular_price();
$discounted_price = $original_price * 0.9; // 10% скидка
$cart_item['data']->set_price($discounted_price);
}
}Как проверить, что код работает
- Залогиньтесь под пользователем с ролью
wholesaler(создайте её, если нужно). - Добавьте товар в корзину.
- Перейдите в корзину и проверьте, что цена товара уменьшилась на 10% от регулярной.
- Если сменить пользователя на другого с другой ролью — цена не меняется.
Частые ошибки при использовании woocommerce_before_calculate_totals
- Изменение цены без проверки контекста. Например, изменение цены в админке или при AJAX-запросах может привести к ошибкам. Используйте проверку
is_admin()иDOING_AJAX. - Использование неправильных методов для установки цены. Нужно менять цену через
$cart_item['data']->set_price(), а не напрямую менять значения в массиве. - Отсутствие проверки роли или условий. Это может привести к изменению цены для всех пользователей.
- Кэширование. Если используются плагины кеширования, изменения в корзине могут не отображаться сразу.
Дополнительные практические советы
Обработка множества условий и скидок
Для сложных скидок и наценок лучше собрать всю логику в одном месте, чтобы избежать конфликтов и дублирования. Например, использовать приоритеты хуков и четкие проверки по ролям, количеству товаров и др.
Производительность
Избегайте тяжелых запросов в функции, которая вызывается на каждый пересчет корзины. Кэшируйте результаты и минимизируйте вызовы API.
Безопасность
Не доверяйте пользовательским данным без проверки. Все вычисления делайте на стороне сервера.
Таблица сравнения способов изменения цены в WooCommerce
| Метод | Пример | Преимущества | Недостатки |
|---|---|---|---|
Хук woocommerce_before_calculate_totals | set_price() в корзине | Динамическая корректировка без изменения базы | Применяется только в корзине/чекауте |
| Изменение цены товара в базе | update_post_meta с _price | Постоянное изменение цены | Риски сломать магазин, влияет на все заказы |
| Создание купонов | Автоматические скидки WooCommerce | Простота использования для конечного пользователя | Меньшая гибкость для сложной логики |