Для чего нужен хук woocommerce_before_calculate_totals и когда он применяется
Хук woocommerce_before_calculate_totals — один из ключевых для динамического изменения цен в корзине WooCommerce. Он вызывается перед пересчётом общей стоимости, что позволяет программно изменять стоимость товаров, добавленных в корзину, на лету. Это полезно для реализации скидок, наценок, динамического ценообразования и прочих сценариев, где цена зависит от условий, не заложенных в базовые данные товара.
Диагностика задачи: когда и почему не меняется цена в корзине
Если вы пытаетесь изменить цену товара в корзине через пользовательский код, но итоговая сумма не меняется, возможные причины:
- Хук используется неправильно или с неверным приоритетом.
- Изменения цены не применяются к объектам корзины, а только к локальным переменным.
- Кеширование корзины или сторонние плагины перезаписывают итоговые цены после вашего кода.
- Вы изменяете цену товара, а не цену позиции в корзине (cart item).
Для диагностики:
- Подключите вывод
error_logилиvar_dumpвнутри функции на хук, чтобы убедиться, что функция вызывается. - Проверьте, что изменяете цену именно объекта типа
WC_Cart_Itemвнутри корзины. - Отключите временно кеширующие плагины и плагины, которые влияют на цены, чтобы исключить конфликт.
Пошаговое решение: как правильно изменить цену товара в корзине через хук
1. Используйте хук woocommerce_before_calculate_totals с достаточно низким приоритетом (например, 10).
2. В функции переберите все товары в корзине через WC()->cart->get_cart().
3. Для каждого товара измените цену через метод set_price() у объекта $cart_item['data'].
4. Добавьте проверку, чтобы не менять цену повторно (например, с помощью пользовательского флага в $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 ) {
if ( isset($cart_item['custom_price_applied']) ) continue;
$original_price = $cart_item['data']->get_price();
// Пример: уменьшаем цену на 15% для всех товаров
$new_price = $original_price * 0.85;
$cart_item['data']->set_price( $new_price );
// Флаг, чтобы не менять цену повторно
$cart_item['custom_price_applied'] = true;
}
}Как проверить, что решение работает
- Добавьте товар в корзину и перейдите в корзину или оформление заказа — цена должна измениться согласно формуле.
- В консоли или логе отладьте вывод цены до и после изменения.
- Убедитесь, что сумма в итогах корзины пересчиталась и соответствует новым ценам.
Частые ошибки при изменении цены в корзине и способы их исправления
- Изменение цены вне хука woocommerce_before_calculate_totals: цена не обновится — всегда используйте этот хук.
- Изменение свойства
$cart_item['data']не через методset_price(): напрямую менять свойства объекта WC_Product не рекомендуется, используйте официальные методы. - Отсутствие проверки флага изменения: приводит к множественным перерасчетам и ошибкам.
- Вызов функции в админке без проверки: может вызвать ошибки — добавьте проверку
is_admin()иDOING_AJAX.
Практические советы по безопасности и производительности
- Не меняйте цену в админке и при AJAX-запросах, которые не связаны с корзиной, чтобы избежать конфликтов.
- Для сложных условий используйте кэширование расчетов, чтобы не перегружать сервер.
- Тестируйте код на совместимость с другими плагинами, особенно с кеширующими и оптимизирующими.
- В продакшене логирование отключайте, чтобы не засорять логи.
Таблица сравнения способов изменения цены в WooCommerce
| Метод | Применение | Плюсы | Минусы |
|---|---|---|---|
| Хук woocommerce_before_calculate_totals | Динамическое изменение цен в корзине | Гибко, работает на лету, не требует плагинов | Требует правильного кода, возможны конфликты |
| Плагины скидок и акций | Простое создание правил скидок | Удобный интерфейс, поддержка большинства сценариев | Может влиять на производительность, ограничены логикой |
| Изменение цены товара через фильтры на уровне продукта | Изменение цены на страницах товаров | Влияет на отображение, но не на корзину | Не подходит для динамических скидок в корзине |