Skip to content

Все для WordPress, веб-розробки — і не тільки

🔧 Як зробити тему WordPress готовою до WooCommerce: повне керівництво для розробників

🔧 Як зробити тему WordPress готовою до WooCommerce: повне керівництво для розробників

Пишете свою тему й хочете вписати в неї магазин? Технічно WooCommerce працює з будь-якою темою — це просто плагін. Але «працює» не означає «виглядає як треба»: роз’їжджається сітка товарів, пливуть сайдбари, стандартні стилі конфліктують із вашими.

Проблема глибша за пару CSS-правил. Починаючи з WooCommerce 3.3, тема без оголошеної підтримки отримує спрощений рендеринг через шорткоди та попередження в адмінці. Повний контроль над версткою ви втрачаєте.

Нижче, готові сніпети від оголошення підтримки до живого кошика в меню. Кожен перевірено на актуальній версії WooCommerce і розбито за розділами, від фундаменту до тонкого налаштування.

💡 Швидкий огляд:

  • Спочатку тема має явно заявити про підтримку WooCommerce, без цього не ввімкнуться перевизначення шаблонів і частина функцій
  • Код розміщуйте в окремому файлі (наприклад inc/woocommerce.php), який підвантажується лише за активного плагіна
  • Налаштування сітки магазину потребує двох фільтрів: один змінює колонки, другий додає клас на body
  • Галерея товарів, зум, лайтбокс і слайдер вмикаються окремими викликами add_theme_support
  • Кошик у меню має оновлюватися через AJAX, використовуйте фільтр add_to_cart_fragments

1. Фундамент: оголошення підтримки та перевірка активності

Перше й найважливіше: тема має явно сказати WooCommerce: «я тебе підтримую». Без цього плагін не активує перевизначення шаблонів і виводить попередження в адмінці. Починаючи з WooCommerce 3.3, тема без оголошеної підтримки отримує спрощений рендеринг через шорткоди — це працює, але обмежує контроль над версткою.

1add_action( 'after_setup_theme', function() {
2 add_theme_support( 'woocommerce' );
3} );

Важливо використовувати саме хук after_setup_theme, а не init, так рекомендує документація WooCommerce. З версії 3.3+ можна одразу передати налаштування сітки:

1add_action( 'after_setup_theme', function() {
2 add_theme_support( 'woocommerce', array(
3 'thumbnail_image_width' => 150,
4 'single_image_width' => 300,
5 'product_grid' => array(
6 'default_rows' => 3,
7 'min_rows' => 2,
8 'max_rows' => 8,
9 'default_columns' => 4,
10 'min_columns' => 2,
11 'max_columns' => 5,
12 ),
13 ) );
14} );

Ці параметри задають значення за замовчуванням для кастомайзера (WooCommerce → Product Images / Product Catalog) і позбавляють користувача необхідності налаштовувати сітку вручну.

Перевірка активності плагіна

Коли ви віддаєте тему для розповсюдження, не можна просто скинути WooCommerce-сніпети в кінець functions.php, їх викличе фатальну помилку, якщо плагін не активний. Код має завантажуватися лише за працюючого WooCommerce:

1define( 'MYTHEME_WOOCOMMERCE_ACTIVE', class_exists( 'WooCommerce' ) );
2
3if ( MYTHEME_WOOCOMMERCE_ACTIVE ) {
4 require_once get_template_directory() . '/inc/woocommerce.php';
5}

Константа обчислюється один раз, і далі ви в будь-якому місці теми можете обгорнути виклик в if ( MYTHEME_WOOCOMMERCE_ACTIVE ). Це чистіше, ніж щоразу смикати class_exists або is_plugin_active.

2. Керування стилями WooCommerce

WooCommerce постачається з трьома CSS-файлами: woocommerce-general, woocommerce-layout і woocommerce-smallscreen. В ідеалі ви не видаляєте їх, а перевизначаєте у своїй темі, тоді сторонні WooCommerce-розширення, які спираються на ці класи, не ламаються. Але якщо ви пишете все з нуля й хочете повного контролю, ось як їх прибрати.

Видалити всі стилі разом:

1add_filter( 'woocommerce_enqueue_styles', '__return_empty_array' );

Або прибрати вибірково:

1function mytheme_remove_woo_styles( $styles ) {
2 unset( $styles['woocommerce-general'] );
3 unset( $styles['woocommerce-layout'] );
4 unset( $styles['woocommerce-smallscreen'] );
5 return $styles;
6}
7add_filter( 'woocommerce_enqueue_styles', 'mytheme_remove_woo_styles' );

На практиці частіше застосовують другий варіант: прибирають woocommerce-layout (відсоткова сітка), але залишають woocommerce-general (кнопки, повідомлення, форми) і точково перебивають їх своїм CSS із вищим пріоритетом.

3. Налаштування сторінок магазину

Скільки товарів показувати

Фільтр loop_shop_per_page керує кількістю товарів на сторінці магазину та в архівах (категорії, теги):

1function mytheme_woo_posts_per_page( $cols ) {
2 return 12;
3}
4add_filter( 'loop_shop_per_page', 'mytheme_woo_posts_per_page' );

Кількість колонок у сітці

Тут потрібні два фільтри. Одного loop_shop_columns недостатньо: на відміну від шорткодів WooCommerce (які мають обгортку з класом columns-N), на сторінках магазину такого класу немає. Тому додаємо його на body:

1function mytheme_woo_shop_columns( $columns ) {
2 return 4;
3}
4add_filter( 'loop_shop_columns', 'mytheme_woo_shop_columns' );
5
6function mytheme_woo_shop_columns_body_class( $classes ) {
7 if ( is_shop() || is_product_category() || is_product_tag() ) {
8 $classes[] = 'columns-4';
9 }
10 return $classes;
11}
12add_filter( 'body_class', 'mytheme_woo_shop_columns_body_class' );

Тепер у CSS можна писати селектори .columns-4 ul.products li.product, і вони застосовуватимуться саме до сітки магазину.

Заголовок магазину

WooCommerce за замовчуванням виводить «Shop» як заголовок на сторінці магазину. Якщо ваша тема вже вміє показувати заголовки архівів, цей дублікат заважає:

1add_filter( 'woocommerce_show_page_title', '__return_false' );

Якщо ж ви використовуєте the_archive_title(), можна підмінити заголовок на назву сторінки магазину (яку задають в адмінці):

1function mytheme_woo_archive_title( $title ) {
2 if ( is_shop() && $shop_id = wc_get_page_id( 'shop' ) ) {
3 $title = get_the_title( $shop_id );
4 }
5 return $title;
6}
7add_filter( 'get_the_archive_title', 'mytheme_woo_archive_title' );

4. Галерея товарів: зум, лайтбокс і слайдер

З версії WooCommerce 3.0 з’явилася нова галерея товарів на базі FlexSlider, PhotoSwipe та jQuery Zoom. У версіях 3.0-3.2 її вимкнено за замовчуванням, і вона потребує явного ввімкнення. Починаючи з 3.3 галерею ввімкнено для не-WooCommerce-тем і вимкнено для тем, які заявили підтримку, ви вмикаєте потрібні компоненти самостійно:

1add_theme_support( 'wc-product-gallery-slider' );
2add_theme_support( 'wc-product-gallery-zoom' );
3add_theme_support( 'wc-product-gallery-lightbox' );

Можна ввімкнути лише частину: наприклад, слайдер і зум, так, а лайтбокс замінити власним рішенням із Fancybox. Кожна директива підвантажує свої скрипти, тому зайве краще не вмикати.

Кількість колонок у мініатюрах галереї (під основним зображенням товару):

1function mytheme_woo_product_thumbnails_columns() {
2 return 4;
3}
4add_action( 'woocommerce_product_thumbnails_columns', 'mytheme_woo_product_thumbnails_columns' );

5. Супутні та продані товари (cross-sells / up-sells)

На сторінці товару WooCommerce виводить блоки «Супутні товари» (related) та «З цим також купують» (up-sells). Їхня кількість і сітка налаштовуються окремо.

Скільки супутніх товарів показувати:

1function mytheme_woo_related_posts_per_page( $args ) {
2 $args['posts_per_page'] = 4;
3 return $args;
4}
5add_filter( 'woocommerce_output_related_products_args', 'mytheme_woo_related_posts_per_page' );

Колонки для up-sells і related-блоків, та сама історія, що й із сіткою магазину: змінюємо і колонки, і клас на body:

1function mytheme_woo_single_loops_columns( $columns ) {
2 return 4;
3}
4add_filter( 'woocommerce_up_sells_columns', 'mytheme_woo_single_loops_columns' );
5
6function mytheme_woo_related_columns( $args ) {
7 $args['columns'] = 4;
8 return $args;
9}
10add_filter( 'woocommerce_output_related_products_args', 'mytheme_woo_related_columns', 10 );
11
12function mytheme_woo_single_loops_columns_body_class( $classes ) {
13 if ( is_singular( 'product' ) ) {
14 $classes[] = 'columns-4';
15 }
16 return $classes;
17}
18add_filter( 'body_class', 'mytheme_woo_single_loops_columns_body_class' );

Зверніть увагу: у woocommerce_output_related_products_args два фільтри з різними пріоритетами, mytheme_woo_related_posts_per_page (за замовчуванням 10) змінює posts_per_page, а mytheme_woo_related_columns (пріоритет 10) змінює columns. Вони не конфліктують, бо працюють із різними ключами одного масиву $args.

6. Пагінація та значок розпродажу

Стрілки пагінації магазину можна замінити на іконки вашої теми:

1function mytheme_woo_pagination_args( $args ) {
2 $args['prev_text'] = '<i class="fa fa-angle-left"></i>';
3 $args['next_text'] = '<i class="fa fa-angle-right"></i>';
4 return $args;
5}
6add_filter( 'woocommerce_pagination_args', 'mytheme_woo_pagination_args' );

Замість Font Awesome підставте свої класи іконок або прямі SVG.

Текст на значку розпродажу (Sale!) легко перевизначити, зручно для сайтів іншою мовою або просто щоб прибрати знак оклику:

1function mytheme_woo_sale_flash() {
2 return '<span class="onsale">' . esc_html__( 'Sale', 'woocommerce' ) . '</span>';
3}
4add_filter( 'woocommerce_sale_flash', 'mytheme_woo_sale_flash' );

Для української мови замініть 'Sale' на 'Скидка', переклад підхопиться автоматично, якщо у вас є MO-файли WooCommerce українською.

7. Динамічний кошик у меню

Мабуть, найзатребуваніший елемент у будь-якій магазинній темі, іконка кошика в навігації з актуальною сумою. Реалізація складається з трьох частин: додавання елемента в меню, генерація HTML кошика та AJAX-оновлення.

1// Добавление ссылки в меню
2function mytheme_add_menu_cart_item( $items, $args ) {
3 if ( $args->theme_location === 'primary' ) {
4 $css_class = 'menu-item menu-item-type-cart menu-item-type-woocommerce-cart';
5 if ( is_cart() ) {
6 $css_class .= ' current-menu-item';
7 }
8 $items .= '<li class="' . esc_attr( $css_class ) . '">';
9 $items .= mytheme_menu_cart_item();
10 $items .= '</li>';
11 }
12 return $items;
13}
14add_filter( 'wp_nav_menu_items', 'mytheme_add_menu_cart_item', 10, 2 );
15
16// HTML корзины
17function mytheme_menu_cart_item() {
18 $cart_count = WC()->cart->cart_contents_count;
19 $css_class = 'wpex-menu-cart-total wpex-cart-total-' . intval( $cart_count );
20 $url = $cart_count ? WC()->cart->get_cart_url() : wc_get_page_permalink( 'shop' );
21 $html = WC()->cart->get_cart_total();
22 $html = str_replace( 'amount', '', $html );
23
24 return '<a href="' . esc_url( $url ) . '" class="' . esc_attr( $css_class ) . '">'
25 . '<span class="cart-icon"></span>'
26 . wp_kses_post( $html )
27 . '</a>';
28}
29
30// AJAX-обновление фрагментов
31function mytheme_menu_cart_link_fragments( $fragments ) {
32 $fragments['.wpex-menu-cart-total'] = mytheme_menu_cart_item();
33 return $fragments;
34}
35add_filter( 'add_to_cart_fragments', 'mytheme_menu_cart_link_fragments' );

Критично важливий момент: **не обгортайте ці функції в **is_admin(). AJAX-запити кошика приходять через admin-ajax.php, і якщо функція недоступна в адмін-контексті, оновлення ціни в меню просто не спрацює. theme_location замініть на ідентифікатор свого меню (у прикладі primary).

8. Альтернативний підхід: шаблон woocommerce.php

Описані вище хуки, найгнучкіший і найбезпечніший з погляду оновлень метод. Але якщо вам потрібно радикально перекроїти всю обгортку магазину та продуктових сторінок, є шлях через окремий шаблон.

Створіть у корені теми файл woocommerce.php (копія page.php) і замініть головний цикл на виклик woocommerce_content():

1<?php
2get_header(); ?>
3
4<div id="primary" class="content-area">
5 <main id="main" class="site-main">
6 <?php woocommerce_content(); ?>
7 </main>
8</div>
9
10<?php get_footer();
11

Цей метод простіший, але він має свою ціну: один шаблон на всі WooCommerce-сторінки, і магазин, і категорії, і картку товару. Тонке налаштування (різну сітку, різний сайдбар на різних сторінках) робити складніше. Обирайте його, тільки якщо поточна тема зовсім не товаришує з WooCommerce на рівні розмітки, і ви готові прийняти обмеження.

⁉️🤔 Часті запитання

Чи обов’язково видаляти стандартні стилі WooCommerce?

Не обов’язково. Безпечніший шлях, залишити їх і перевизначати селектори у своїй темі з вищим пріоритетом. Так ви зберігаєте сумісність із розширеннями, які спираються на стандартні класи WooCommerce. Повне видалення (__return_empty_array) виправдане лише тоді, коли ви пишете магазинну тему з нуля і точно знаєте, що жодні сторонні плагіни не додаватимуть елементи, стилізовані під WooCommerce. У більшості випадків достатньо вибірково вимкнути woocommerce-layout (відсоткова сітка конфліктує з флексбоксами та гридами сучасних тем), а woocommerce-general і woocommerce-smallscreen залишити й акуратно перебити через каскад CSS, так ви отримуєте чисту сітку без втрати сумісності.

Чому не працює зміна колонок у магазині?

Тому що самого фільтра loop_shop_columns недостатньо. WooCommerce використовує класи .columns-N на обгортці для роботи сітки, і вони автоматично додаються лише до шорткодів ([products columns="4"]). На сторінках магазину та архівах цієї обгортки немає, клас потрібно вішати на body через body_class. Два фільтри з розділу 3 повністю вирішують проблему.

Фільтр loop_shop_columns змінює PHP-змінну, яку WooCommerce передає в шаблон, але CSS-сітка прив’язана до класу-обгортки. Без класу columns-4 на body ваші стилі ul.products li.product не мають селектора-контексту й або не застосовуються, або б’ють по всіх продуктових списках на сайті.

Чи потрібно перезбирати тему після оновлення WooCommerce?

Якщо ви використовуєте хуки (усі сніпети з цієї статті), ні. Хуки змінюються вкрай рідко. Якщо ви перевизначаєте шаблони (файли з woocommerce/templates/), так, потрібно звіряти свої копії з актуальними після кожного мажорного оновлення. Саме тому офіційна документація рекомендує хуки як кращий метод кастомізації.

Хуки — це контракт. WooCommerce гарантує їхню стабільність між версіями. Шаблони — це реалізація, яка може змінитися будь-якої миті: додадуться нові екшени, зміниться розмітка, перейменуються класи. За п’ять років існування хуків loop_shop_columns і woocommerce_output_related_products_args їхня сигнатура не змінювалася жодного разу, а файл content-product.php за той самий період оновлювався десятки разів.

Чи можна використовувати is_admin() для захисту функцій кошика?

Ні, і це часта пастка. AJAX-запити WooCommerce (додавання в кошик, оновлення кількості) обробляються через admin-ajax.php, у цьому контексті is_admin() повертає true. Якщо ви обгорнете функції кошика в if ( ! is_admin() ), AJAX-оновлення фрагментів (add_to_cart_fragments) перестане працювати, і сума в іконці кошика не змінюватиметься без перезавантаження сторінки.

Огортати в ! is_admin() потрібно лише те, що не повинно виконуватися в адмінці (виведення HTML на фронті), але функції-обробники AJAX мають бути доступні завжди. Для коду з розділу 7 не ставте is_admin() взагалі, WooCommerce сам викликає потрібні колбеки через свій AJAX-обробник, і обмеження зламає логіку.

Як додати підтримку WooCommerce у блокову тему (FSE)?

Для блокових тем декларація підтримки не змінилася, той самий add_theme_support('woocommerce'). Але замість хуків і перевизначення PHP-шаблонів ви працюєте в редакторі сайту: WooCommerce надає блоки (Products, Product Search, Cart, Checkout), які вставляються в шаблони через Appearance → Editor. Тонка кастомізація робиться через theme.json і CSS-змінні.

Блокові теми принципово змінюють підхід: ви не пишете PHP-хуки для верстки, а збираєте сторінки візуально з готових блоків WooCommerce. Але фундамент, add_theme_support у functions.php, залишається тим самим. А для тонких правок, які блоками не зробити (наприклад, кастомний текст на кнопці «У кошик»), старі добрі фільтри з цієї статті працюють і в FSE-темах.

Яку стратегію обрати під ваш проєкт

Якщо ви допрацьовуєте наявну тему, почніть із кроків 1 і 2: оголосіть підтримку та налаштуйте сітку. У більшості випадків цього достатньо, щоб магазин виглядав пристойно. Далі додавайте лише ті правки, які вирішують конкретну проблему: криву галерею, негарну пагінацію, відсутність кошика в меню.

Пишете тему з нуля для продажу чи розповсюдження, пройдіть увесь список. Кожен пункт додає користувачеві те, що він очікує від магазинної теми «з коробки»: керовану сітку, зум на товарах, живу іконку кошика. Код винесіть в inc/woocommerce.php і підвантажуйте за константою, тоді тема однаково добре працює і як блог, і як магазин.

Відправна точка завжди одна й та сама:

1add_action( 'after_setup_theme', function() {
2 add_theme_support( 'woocommerce' );
3} );

Вісім рядків. Усе решта, деталі, які відрізняють тему «сумісну» від теми «заточеної».