Диагностика задачи: зачем менять цену в зависимости от роли
В интернет-магазинах на WooCommerce часто требуется показывать разные цены для разных групп пользователей — например, оптовым покупателям, дистрибьюторам или зарегистрированным клиентам с особым статусом. Стандартный функционал WooCommerce не предусматривает такой кастомизации цен, поэтому приходится внедрять собственные решения с помощью кода или плагинов.
Пошаговое решение: как реализовать динамическую цену по ролям
1. Определяем роли пользователей
Убедитесь, что у вас есть роли, для которых нужно менять цены. Например, wholesale_customer — оптовый покупатель, vip_customer — клиент с VIP статусом и так далее. Роли можно создавать вручную или через плагины типа User Role Editor.
2. Добавляем фильтр для изменения цены товара
WooCommerce позволяет менять цену с помощью фильтра woocommerce_get_price. Ниже пример кода, который меняет цену товара в зависимости от роли пользователя:
add_filter('woocommerce_get_price', 'change_price_by_user_role', 10, 2);
function change_price_by_user_role($price, $product) {
if (is_admin() && !defined('DOING_AJAX')) {
return $price; // не меняем в админке
}
if (!is_user_logged_in()) {
return $price; // для гостей возвращаем стандартную цену
}
$user = wp_get_current_user();
if (in_array('wholesale_customer', $user->roles)) {
// Скидка 20% для оптовиков
$price = $price * 0.8;
} elseif (in_array('vip_customer', $user->roles)) {
// Фиксированная цена для VIP
$vip_prices = array(
123 => 50, // ID товара => цена
456 => 80,
);
$product_id = $product->get_id();
if (isset($vip_prices[$product_id])) {
$price = $vip_prices[$product_id];
}
}
return $price;
}3. Кеширование и производительность
Обратите внимание, что подобные фильтры влияют на скорость загрузки страниц, особенно если цены зависят от сложной логики. Можно кешировать результаты для авторизованных пользователей с помощью transient API или плагинов кеширования, поддерживающих динамические фрагменты.
Проверка результата после внедрения
- Залогиньтесь под пользователем с ролью
wholesale_customerи проверьте цену товара — она должна быть на 20% ниже. - Залогиньтесь под пользователем с ролью
vip_customerи убедитесь, что для товаров с ID 123 и 456 цена фиксированная. - Проверьте, что для гостей и других ролей цена не изменилась.
- Проверьте работу на странице корзины и оформления заказа, чтобы убедиться, что цена корректно применяется во всех местах.
Частые ошибки и как их исправить
- Изменение цены только на фронтенде, а не в корзине/чекауте. Фильтр
woocommerce_get_priceвлияет только на вывод, для корректного изменения цены в корзине используйте фильтрwoocommerce_before_calculate_totalsи изменяйте цену товара в объектеWC_Cart_Item. - Цена не меняется для вариативных товаров. Для вариативных товаров надо обрабатывать объекты типа
WC_Product_Variationотдельно, проверяя ID вариации. - Проблемы с кешем и кеширующими плагинами. Отключите кеширование для страниц с ценами, меняющимися по ролям, или используйте динамические фрагменты.
Практические советы по безопасности и производительности
- Не используйте глобальные переменные и не делайте запросы к базе в фильтрах цен без кеширования — это замедлит сайт.
- Для сложных правил ценообразования рассмотрите использование готовых плагинов с поддержкой ролей, например, Clearfy Pro может помочь с оптимизацией и SEO, но для цен лучше кастомные решения.
- Проверяйте сайт на предмет утечек информации: не показывайте оптовые цены гостям и пользователям без нужной роли.
Пример расширения: корректная замена цены в корзине
add_action('woocommerce_before_calculate_totals', 'change_cart_item_price_by_role', 10, 1);
function change_cart_item_price_by_role($cart) {
if (is_admin() && !defined('DOING_AJAX')) {
return;
}
if (!is_user_logged_in()) {
return;
}
$user = wp_get_current_user();
foreach ($cart->get_cart() as $cart_item) {
$product = $cart_item['data'];
$original_price = $product->get_regular_price();
if (in_array('wholesale_customer', $user->roles)) {
$new_price = $original_price * 0.8;
$product->set_price($new_price);
} elseif (in_array('vip_customer', $user->roles)) {
$vip_prices = array(
123 => 50,
456 => 80,
);
$product_id = $product->get_id();
if (isset($vip_prices[$product_id])) {
$product->set_price($vip_prices[$product_id]);
}
} else {
$product->set_price($original_price);
}
}
}