WordPress · 6 мин чтения

Шаблон WooCommerce: переопределение без потери обновлений

Шаблон WooCommerce, который правишь напрямую в папке плагина, слетает при первом же обновлении. Я видел это на нескольких проектах: до меня кто-то отредактировал content-product.php прямо в /wp-content/plugins/woocommerce/templates/, и после апдейта до версии 8.3 карточка товара откатилась к дефолтному виду, а клиент неделю не понимал, куда делись отзывы и кастомные бейджи наличия. Правильный путь - копировать файлы шаблонов в тему, а лучше в дочернюю, и работать с приоритетом поиска, который WooCommerce использует сам.

Зачем переопределять шаблоны WooCommerce, а не править файлы плагина

Плагин WooCommerce обновляется часто, и разработчики регулярно меняют структуру шаблонов: добавляют хуки, убирают устаревшую разметку, переводят часть интерфейса на блоки Gutenberg. Если правки внесены прямо в файлы плагина, при следующем обновлении WordPress их просто перезапишет без предупреждения и без бэкапа. На практике это выглядит так: сайт месяц работал с кастомной версткой карточки товара, потом клиент нажал «Обновить» в админке, и дизайн за секунду вернулся к дефолту WooCommerce.

Второй риск не такой очевидный, но бьет больнее. Если правки лежат в плагине, их не видно в git-диффе темы, и через полгода никто, включая меня, не вспомнит, что именно меняли и зачем. Переопределение через тему решает обе проблемы: WooCommerce сначала ищет файл в теме и только потом в своей папке, поэтому обновление плагина трогает только его собственные файлы, а копия в теме остается нетронутой.

Как WooCommerce ищет файлы шаблонов в теме

Логика поиска зашита в функции wc_locate_template() и построена на трех уровнях приоритета:

  • yourtheme/woocommerce/имя-шаблона.php - переопределение в активной теме, высший приоритет;
  • yourtheme/имя-шаблона.php - старый вариант без подпапки, WooCommerce поддерживает его для обратной совместимости, но я так не делаю - слишком легко перепутать шаблон темы с шаблоном магазина;
  • wp-content/plugins/woocommerce/templates/имя-шаблона.php - дефолтный файл плагина, подключается, если в теме ничего не найдено.

Структура папки в теме зеркалит структуру плагина один в один, вплоть до вложенных каталогов:

themename/
  woocommerce/
    content-product.php
    single-product/
      price.php
      add-to-cart/
        simple.php
    checkout/
      thankyou.php

Практика: копирую шаблон карточки товара в дочернюю тему

Беру для примера content-product.php - шаблон карточки товара в архиве магазина. Порядок действий у меня почти всегда один и тот же:

  1. Создаю дочернюю тему, если её еще нет - переопределения в родительской теме слетят при обновлении темы точно так же, как правки в плагине;
  2. Копирую файл из wp-content/plugins/woocommerce/templates/content-product.php в wp-content/themes/моя-дочерняя-тема/woocommerce/content-product.php, сохраняя относительный путь;
  3. Смотрю на комментарий в шапке файла - там указана версия шаблона, например @version 8.6.0. Эту версию фиксирую у себя в заметке, чтобы после апдейта WooCommerce было с чем сверить;
  4. Вношу правки и проверяю карточку сразу в каталоге, в результатах поиска и в блоке «Похожие товары» - один и тот же шаблон нередко подключается в нескольких местах магазина.

Дочернюю тему подключаю штатно, без плагинов-прослоек:

<?php
add_action( 'wp_enqueue_scripts', 'kalinkindev_child_theme_styles' );
function kalinkindev_child_theme_styles() {
    wp_enqueue_style( 'parent-style', get_template_directory_uri() . '/style.css' );
    wp_enqueue_style( 'child-style', get_stylesheet_directory_uri() . '/style.css', array( 'parent-style' ) );
}

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

Хуки WooCommerce vs полное переопределение файла

Перед тем как копировать файл в тему, смотрю, нельзя ли обойтись хуком - большинство шаблонов WooCommerce вызывают do_action() в нескольких точках, и туда можно добавить свою функцию без единой строки готовой верстки:

<?php
add_action( 'woocommerce_before_shop_loop_item_title', 'kalinkindev_add_stock_badge', 15 );
function kalinkindev_add_stock_badge() {
    global $product;
    if ( $product->is_in_stock() ) {
        echo '<span class="stock-badge">В наличии</span>';
    }
}

Способ Что меняешь Риск при обновлении Когда применяю
Хуки (add_action/remove_action) добавление, скрытие или перестановку блоков без изменения верстки низкий, сигнатура хука переживает большинство релизов бейдж наличия, доп. поле, изменение порядка блоков на карточке
Полный override шаблона саму HTML-структуру файла выше - при мажорном апдейте нужно вручную сверять с новым дефолтным файлом меняю разметку целиком, встраиваю сторонний виджет в структуру страницы
Override + хуки внутри своего файла точечные вставки внутри скопированного файла средний, зависит от объема правок стандартный случай для большинства задач клиентов

На практике почти всегда выигрывает третий вариант: беру дефолтный файл почти без изменений и добавляю в него хуки вместо того, чтобы переписывать разметку целиком. Тогда при обновлении WooCommerce достаточно сравнить свой файл с новым дефолтным через diff и перенести две-три строки, а не разбираться, что изменилось во всем шаблоне.

Что ломается при обновлении WooCommerce и как это отследить

Самое неприятное обновление за последние пару лет для меня - переход WooCommerce на блочный чекаут (Cart and Checkout blocks), который стал вариантом по умолчанию начиная с версии 8.3. У меня был проект с интеграцией эквайринга Т‑Банка, где я переопределял checkout/form-checkout.php, чтобы добавить свои поля и вывод виджета оплаты. После обновления страница оформления заказа у части покупателей стала открываться в блочном режиме, где старый файл шаблона вообще не подключается - подключение полей ушло в другой механизм, через Store API и React-компоненты. Пришлось явно оставить магазин на классическом чекауте в настройках WooCommerce и параллельно переписывать интеграцию под блочный API, чтобы не откладывать переход на потом.

Похожая история была с зоной доставки СДЭК: кастомный вывод стоимости и сроков в cart/cart-shipping-method.php перестал получать нужные данные после того, как WooCommerce изменил формат объекта rate в одном из минорных релизов. Помогло то, что этот шаблон я не переопределял целиком, а вставлял свой код через хук woocommerce_cart_shipping_method_full_label - его сигнатура не менялась несколько лет.

Состояние переопределений отслеживаю через раздел WooCommerce Status: там показан список файлов, у которых версия в шапке отличается от версии в актуальном плагине, и они подсвечены желтым. Это первое, что проверяю после любого обновления WooCommerce на боевом сайте, до того как об этом напишет клиент. Если правок много и магазин рассчитывает работать без сюрпризов после каждого релиза плагина, такие вещи разумнее не проверять от случая к случаю, а отдать на постоянное сопровождение интернет-магазина на WooCommerce, куда входит в том числе проверка шаблонов после обновлений.

Частые ошибки при кастомизации шаблонов WooCommerce

  • Правки прямо в /wp-content/plugins/woocommerce/ - обнуляются при любом обновлении плагина, без вариантов;
  • Переопределение в родительской теме вместо дочерней - слетает при обновлении темы тем же способом;
  • Копирование всего файла ради одной строки правки - потом невозможно быстро понять, что изменил именно я, а что было в оригинале;
  • Игнорирование раздела WooCommerce Status - «устаревшие шаблоны» там не декоративная надпись, а конкретный список файлов, которые разошлись с логикой плагина;
  • Проверка магазина только в классическом чекауте, хотя часть установок WooCommerce уже работает с блочным чекаутом по умолчанию;
  • Хардкод обращений к файлам темы внутри плагинов интеграций - эквайринга, СДЭК, доставки - при смене темы такие правки теряются, разумнее выносить их в отдельный must-use плагин.

Частые вопросы

Как понять, что мой шаблон устарел после обновления WooCommerce?

Открываю WooCommerce, раздел Статус, вкладку Шаблоны. Там список файлов с пометкой об устаревании и указанием версии ядра рядом с версией моего переопределения - если они разошлись, сверяю свой файл с актуальным через diff.

Можно ли переопределить только часть шаблона, а не весь файл?

Да, и это предпочтительный способ в большинстве случаев. Шаблоны WooCommerce вызывают хуки вроде woocommerce_before_shop_loop_item или woocommerce_single_product_summary с привязанными к ним функциями через add_action и remove_action - через них закрывается почти все, что обычно нужно клиентам, без копирования файла целиком.

У меня уже не дочерняя тема, а прямые правки в родительской - что делать перед следующим обновлением темы?

Перед обновлением создаю дочернюю тему, переношу в нее папку woocommerce/ со всеми переопределениями и функции из functions.php, которые относятся к магазину. Только после этого обновляю родительскую тему, иначе правки исчезнут при первом же апдейте.

Ломает ли блочный чекаут старые переопределения шаблонов?

Частично да. Файлы checkout/form-checkout.php и cart/cart.php в блочном режиме не используются - вместо них работают React-компоненты и Store API. Если у магазина есть кастомизация чекаута, перед обновлением WooCommerce до версии с блоками по умолчанию стоит либо явно оставить классический чекаут в настройках, либо переносить логику в хуки блочного API.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно