Диагностика задачи: зачем менять стоимость товара в корзине
В WooCommerce стандартно цена товара фиксирована и отображается в корзине, исходя из цены в карточке товара. Однако часто возникает задача изменить стоимость товара динамически в корзине по определённым условиям: скидка при покупке нескольких товаров, изменение цены по ролям пользователей, индивидуальные скидки или специальные цены для определённых категорий. Без изменения цены именно в корзине эти сценарии не реализовать корректно.
Пошаговое решение: изменение цены товара в корзине через хук woocommerce_before_calculate_totals
Основной способ — использовать хук woocommerce_before_calculate_totals, который срабатывает до подсчёта итоговой стоимости корзины. Внутри этого хука можно изменить цену каждого товара в объекте корзины.
Пример: скидка 10% на все товары категории "Футболки" при покупке больше 3 штук
add_action('woocommerce_before_calculate_totals', 'custom_dynamic_price_cart', 10, 1); function custom_dynamic_price_cart( $cart ) { if ( is_admin() && ! defined('DOING_AJAX') ) return; // Проверяем, что это не админка и не ajax foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) { $product = $cart_item['data']; $quantity = $cart_item['quantity']; if ( has_term( 'futbolki', 'product_cat', $product->get_id() ) && $quantity >= 3 ) { $regular_price = $product->get_regular_price(); $new_price = $regular_price * 0.9; // Скидка 10% $product->set_price( $new_price ); } else { $product->set_price( $product->get_regular_price() ); // Возвращаем стандартную цену } } }Как это работает
- Проверяем категорию товара через
has_term. - Проверяем количество товара в корзине.
- Если условие выполнено — изменяем цену товара методом
set_price(). - Иначе сбрасываем цену на регулярную (чтобы при повторном подсчёте не осталось изменённой цены).
Проверка результата после внедрения
- Добавьте в корзину товары из нужной категории, например футболки, в количестве 3 и более.
- Перейдите в корзину и убедитесь, что цена изменилась на 10% меньше.
- Измените количество товара на меньше 3 — цена должна вернуться к обычной.
- Протестируйте на разных товарах и категориях, чтобы убедиться, что цены меняются корректно.
Частые ошибки и как исправить
1. Изменения не применяются, цена остаётся прежней
Убедитесь, что вы используете хук woocommerce_before_calculate_totals с приоритетом не выше 10, и что функция не вызывается в админке или ajax-запросах, которые могут сбрасывать цену.
2. Цена меняется, но итоговая сумма корзины неверна
Проверьте, чтобы каждый раз в цикле вы корректно сбрасывали цену к обычной, если условие не выполняется. Иначе WooCommerce может считать цену по старым значениям.
3. Изменение цены влияет на базу данных
Метод set_price() в корзине не меняет цену в базе, только в объекте корзины. Если вы видите, что цена изменилась в базе, возможно, где-то есть другой код, который сохраняет изменения. Исключите это.
Практические советы по безопасности и производительности
- Не храните изменённые цены в метаданных или базе без крайней необходимости — это может привести к рассинхронизации данных.
- Всегда проверяйте, что условия изменения цены не конфликтуют с кэшированием корзины и страниц.
- Избегайте сложных и тяжёлых вычислений в хук, особенно если корзина содержит много товаров — это замедлит страницу.
Сравнение вариантов реализации динамического изменения цены товара в корзине
| Метод | Описание | Плюсы | Минусы |
|---|---|---|---|
Хук woocommerce_before_calculate_totals | Изменение цены в объекте корзины перед подсчётом итоговой суммы | Легко реализовать, не меняет базу, гибко | Нужно сбрасывать цену при ненужных условиях, влияет на производительность при большом количестве товаров |
| Создание пользовательской скидки через купоны или скидочные классы | Использование стандартных механик WooCommerce для скидок | Простота и совместимость, кеширование работает корректно | Менее гибко для сложных условий, ограничено функционалом купонов |
| Изменение цены товара в базе данных | Переопределение цены продукта через метаданные или сохранение | Постоянное изменение, отображается во всех местах | Риск рассинхронизации, требует очистки кэша, сложнее реализовать |
Дополнительный пример: изменение цены по роли пользователя
add_action('woocommerce_before_calculate_totals', 'price_for_user_role', 10, 1); function price_for_user_role( $cart ) { if ( is_admin() && ! defined('DOING_AJAX') ) return; $user = wp_get_current_user(); foreach ( $cart->get_cart() as $cart_item ) { $product = $cart_item['data']; if ( in_array( 'wholesale_customer', (array) $user->roles ) ) { // Для оптовых покупателей $product->set_price( $product->get_regular_price() * 0.8 ); // Скидка 20% } else { $product->set_price( $product->get_regular_price() ); } } }Это простой способ задать разные цены в корзине для разных ролей пользователей без изменения данных продуктов.