Представьте: вы запускаете интернет-магазин на WooCommerce в России, а через полгода выходите на рынок Казахстана или Европы. Или другой сценарий — поставщик сменил базовую валюту, и теперь вам нужно пересчитать 5 000 товаров. Вручную это ад, а стандартные настройки WooCommerce не покрывают все сценарии. Именно здесь на помощь приходят хуки WooCommerce — мощный механизм, который позволяет автоматизировать миграцию цен и валют, не переписывая ядро движка.

В этой статье разберём, как именно работают хуки для пересчёта цен, смены валюты и массовой миграции данных в WooCommerce. Будет много кода — но я постараюсь объяснить каждый фрагмент так, чтобы вы (или ваш разработчик) могли внедрить решение на своём проекте.

Зачем вообще нужна миграция цен через хуки

WooCommerce из коробки умеет отображать одну валюту. Да, можно поставить плагин вроде Currency Switcher, но что если вам нужно:

  • Перевести все цены из долларов в рубли при переносе сайта на другой домен
  • Добавить наценку на все товары на 15% при смене поставщика
  • Реализовать мультивалютность без сторонних плагинов
  • Пересчитать цены вручную по курсу ЦБ при импорте прайс-листа

Стандартные инструменты WooCommerce для этого не приспособлены. Можно, конечно, написать SQL-запрос и обновить цены напрямую в базе — но это рискованно: можно сломать кеширование, сломать связи вариаций, потерять историю заказов. Хуки — это безопасный и правильный путь.

Как устроена система хуков в WooCommerce

Если вы уже работали с WordPress, то знаете: хуки — это точки, в которые можно «вклиниться» своим кодом. WooCommerce расширяет эту систему, добавляя сотни собственных хуков — actions (действия) и filters (фильтры).

Фильтры vs. Actions: в чём разница для цен

Фильтры позволяют изменить значение на лету. Например, вы перехватываете цену товара перед выводом и умножаете её на курс валюты. Оригинальное значение в базе не меняется.

Actions запускают действие в определённый момент жизненного цикла. Например, при сохранении товара вы хотите автоматически записать цену в дополнительное поле.

Для миграции цен чаще используются фильтры — потому что мы хотим трансформировать данные, а не просто выполнить какое-то действие.

Ключевые хуки WooCommerce для работы с ценами

Вот хуки, с которыми вы будете работать чаще всего:

  • woocommerce_product_get_price — фильтрует цену товара при получении
  • woocommerce_product_get_regular_price — обычная (не акционная) цена
  • woocommerce_product_get_sale_price — акционная цена
  • woocommerce_product_variation_get_price — цена вариации товара
  • woocommerce_variation_prices_price — цена вариации в массиве всех вариаций
  • woocommerce_currency — код валюты (USD, RUB, KZT)
  • woocommerce_currency_symbol — символ валюты (₽, $, ₸)

Важный момент: с версии WooCommerce 3.0+ цены хранятся как свойства объекта WC_Product, а не через get_post_meta(). Поэтому хуки вида woocommerce_product_get_* — это правильный способ перехвата.

Пример: миграция цен из долларов в рубли

Допустим, у вас магазин был настроен в долларах, и вы хотите отображать цены в рублях по фиксированному курсу. Самый простой путь — фильтры.

add_filter('woocommerce_product_get_price', 'migrate_usd_to_rub', 10, 2);
add_filter('woocommerce_product_get_regular_price', 'migrate_usd_to_rub', 10, 2);
add_filter('woocommerce_product_get_sale_price', 'migrate_usd_to_rub', 10, 2);

function migrate_usd_to_rub($price, $product) {
    if (empty($price)) {
        return $price;
    }

    // Курс доллара к рублю (в реальном проекте лучше брать с API ЦБ)
    $exchange_rate = 92.50;

    return round($price * $exchange_rate, 0);
}

Этот код перехватывает цену каждого товара и умножает её на курс. Покупатель видит рубли, а в базе всё ещё лежат доллары — оригинальные данные не тронуты.

Но это ещё не миграция в полном смысле. Это отображение. Если вам нужно физически изменить цены в базе данных (например, перед отключением фильтра), тогда нужен другой подход.

Массовое обновление цен: хук + WP Cron

Для реальной миграции — когда нужно пройти по всем товарам и записать новые цены — используйте woocommerce_update_product или кастомный скрипт с WC_Product.

add_action('init', function() {
    // Запускаем миграцию только при наличии специального параметра
    if (!isset($_GET['run_migration']) || !current_user_can('manage_options')) {
        return;
    }

    $exchange_rate = 92.50;

    $products = wc_get_products([
        'limit'  => -1,
        'status' => 'publish',
    ]);

    foreach ($products as $product) {
        $regular_price = $product->get_regular_price();
        $sale_price    = $product->get_sale_price();

        if (!empty($regular_price)) {
            $product->set_regular_price(
                round($regular_price * $exchange_rate, 0)
            );
        }

        if (!empty($sale_price)) {
            $product->set_sale_price(
                round($sale_price * $exchange_rate, 0)
            );
        }

        // Также обновляем цену отображения
        $product->set_price(
            round($product->get_price() * $exchange_rate, 0)
        );

        $product->save();
    }

    wp_die('Миграция завершена! Обработано товаров: ' . count($products));
});

Запуск: https://yourdomain.com/?run_migration=1 — находясь в админке. После выполнения этот параметр больше не сработает, если вы уберёте его из URL.

Проблема больших каталогов

Если у вас 10 000+ товаров,一次性 обработка может упасть по таймауту. Решение — батчевая обработка через WP Cron:

add_action('migrate_prices_batch', 'process_price_batch');

function process_price_batch() {
    $batch_size = 50;
    $offset     = get_option('migration_offset', 0);
    $rate       = 92.50;

    $products = wc_get_products([
        'limit'  => $batch_size,
        'offset' => $offset,
        'status' => 'publish',
    ]);

    if (empty($products)) {
        delete_option('migration_offset');
        update_option('migration_complete', true);
        return;
    }

    foreach ($products as $product) {
        $regular = $product->get_regular_price();
        if (!empty($regular)) {
            $product->set_regular_price(round($regular * $rate, 0));
            $product->set_price(round($product->get_price() * $rate, 0));
            $product->save();
        }
    }

    update_option('migration_offset', $offset + $batch_size);

    // Планируем следующий батч через 30 секунд
    if (!wp_next_scheduled('migrate_prices_batch')) {
        wp_schedule_single_event(time() + 30, 'migrate_prices_batch');
    }
}

Запуск: один раз вызовите do_action('migrate_prices_batch') через WP-CLI или хук init, и система сама обработает каталог порциями.

Смена символа и кода валюты через хуки

Если вы хотите не только пересчитать цены, но и сменить отображаемую валюту — два простых фильтра:

add_filter('woocommerce_currency', function($currency) {
    return 'RUB'; // Код валюты ISO 4217
});

add_filter('woocommerce_currency_symbol', function($symbol, $currency) {
    if ($currency === 'RUB') {
        return '₽';
    }
    return $symbol;
}, 10, 2);

Эти фильтры меняют только визуальное отображение. Само значение цены в базе остаётся прежним — но покупатель видит рубли.

Мультивалютность на хуках: реально ли без плагина?

Короткий ответ: да, но с оговорками. Для простого магазина с 2-3 валютами можно реализовать мультивалютность на чистых хуках:

add_filter('woocommerce_product_get_price', 'dynamic_currency_price', 10, 2);

function dynamic_currency_price($price, $product) {
    if (empty($price)) return $price;

    // Определяем валюту из cookie или параметра
    $user_currency = isset($_COOKIE['shop_currency'])
        ? sanitize_text_field($_COOKIE['shop_currency'])
        : 'RUB';

    $rates = [
        'RUB' => 1,
        'USD' => 0.0108,  // 1 рубль = 0.0108 доллара
        'KZT' => 5.45,    // 1 рубль = 5.45 тенге
        'BYN' => 0.034,   // 1 рубль = 0.034 бел. рубля
    ];

    if (isset($rates[$user_currency])) {
        return round($price * $rates[$user_currency], 2);
    }

    return $price;
}

Плюсы: никаких плагинов, полный контроль, минимальная нагрузка. Минусы: нет автозагрузки курсов с API, нет сохранения валюты в заказе, нет учёта налоговых правил для разных стран. Для серьёзного мультивалютного магазина лучше взять проверенный плагин и допилить его хуками.

Перехват цен в корзине и заказе

Есть нюанс, о котором часто забывают: фильтры woocommerce_product_get_price влияют на отображение, но цена в корзине может кешироваться. Чтобы пересчёт работал и в корзине, добавьте:

add_filter('woocommerce_cart_item_price', function($price_html, $cart_item, $cart_item_key) {
    $rate = 92.50;
    $original_price = $cart_item['data']->get_price();
    $converted = round($original_price * $rate, 0);

    return wc_price($converted);
}, 10, 3);

add_filter('woocommerce_cart_item_subtotal', function($subtotal_html, $cart_item, $cart_item_key) {
    $rate = 92.50;
    $original_subtotal = $cart_item['line_total'];
    $converted = round($original_subtotal * $rate, 0);

    return wc_price($converted);
}, 10, 3);

Важно: если вы мигрируете цены физически (через set_price() и save()), эти фильтры корзины не нужны — WooCommerce сам подхватит новые значения.

Типичные ошибки при миграции цен

Поработав с десятками проектов на WooCommerce, я видел одни и те же ошибки снова и снова:

  • Забывают про вариации. У товара с размерами (S, M, L) каждая вариация — отдельная цена. Обходите не только родительские товары, но и вариации через get_children()
  • Не делают бэкап. Перед любой миграцией — полный дамп базы. Это не обсуждается. Один неправильный курс — и цены улетают в космос
  • Игнорируют кеш. Если стоит Redis или Memcached, после обновления цен в базе нужно сбросить кеш объектов WooCommerce: WC_Cache_Helper::get_transient_version('product', true)
  • Пересчитывают дважды. Если хук с курсом 92.50 уже работает, а вы запустили скрипт миграции с тем же курсом — цены удвоятся. Отключите фильтры перед массовым обновлением

Реальный кейс: перенос магазина с USD на RUB

К нам обращался клиент — интернет-магазин электроники, около 3 200 товаров. Сайт был изначально настроен на доллары (поставщик — из Китая). Задача: перейти на рубли, сохранив все акции и скидки пропорционально.

Что мы сделали:

  1. Сделали полный бэкап базы данных
  2. Получили курс с API ЦБ РФ
  3. Написали батчевый скрипт миграции (по 100 товаров за запуск)
  4. Пересчитали regular_price, sale_price и _price для всех товаров и вариаций
  5. Обновили настройки WooCommerce: валюта → RUB, позиция символа → «справа с пробелом»
  6. Пересчитали купоны (абсолютные скидки тоже конвертировали)
  7. Сбросили весь кеш

Результат: магазин за

Добавить комментарий

Разработка сайтов на Wordpress