Решение для серия строительных интернет-магазинов на WooCommerce — часть 1

Задача: создать несколько интернет-магазинов по большей части строительной тематики, напр. Italdiamant — алмазные диски для резки, шлифования различных материалов; Steigtechnik — это лестницы, строительные леса, стремянки и различные; MÜPRO — крепеж, болты, шурупы, хомуты, профили, изоляция — вот это все. WESCO — яркие, стильные аксессуары для дома (Германия).

Была выработана единая платформа на WordPress, подобраны и адаптированы плагины из официального репозитория WP, а также разработаны свои под нестандартный функционал. Разработана уникальная тема WP, c рассчитом на высокую производительность. Шаблон легко кастомизируется под разный фирменный стиль.

Некоторыми плагинами, кейсами и хаками (куда же без них в WP :)) делюсь с читателями блога.

Advanced Custom Fields (ACF) — расширение функционала
WordPress Custom Fields (произвольных полей) как визуально в админке, так и в виде доп API для программистов. По умолчанию в WP, произвольные поля есть только у записей и пользователей (wp_postmeta и wp_usermeta). Для таксономии — рубрик, меток,
и главное, категорий WooCommerce — произвольных полей нет.

ACF не только добавляет функцию get_field() и the_field() для получения значений полей почти всех сущностей WP (post, page, term, user и пр.) в арсенал разработчика, но и позволяет добавлять различные контролы — текстовые инпуты, чекбоксы, списки, кнопки и еще кучу всего; редактировать
их свойства, параметры отображения на экране.

Я добавил поле hide («Скрывать»), тип Переключатель (radio), Варианты 0 : Нет, 1 : Да. Оно используется для настроек отображения категории в меню.

Дополнительные поля категории WooCommerce

Ниже приведен код функции вывода меню каталога WooCommerce с картинкой (превью) и с учетом видимости установленной в свойствах категории.

function woocommerce_categories() {
    return get_terms('product_cat', ['hide_empty' => false]);
}

function catalog_menu() {
    ?>
        <ul class="catalog-menu">
            <?php
            $categories = woocommerce_categories();
            $active = false;
            if (is_tax('product_cat')) {
                $active = get_queried_object()->term_id;
            } elseif (is_product()) {
                $terms = get_the_terms(get_the_ID(), 'product_cat');
                foreach ($terms as $term) {
                    if ($term->parent == 0)
                        $active = $term->term_id;
                }
            }

            foreach ($categories as $cat):
                /* @var $cat WP_Term */
                $hide = get_field('hide', $cat);
                if ($hide)
                    continue;

                $cat_thumb_id = get_woocommerce_term_meta($cat->term_id, 'thumbnail_id', true);
                $cat_thumb_url = wp_get_attachment_thumb_url($cat_thumb_id);
                $term_link = get_term_link($cat, 'product_cat');
                $class = ['catalog-menu-item'];
                if ($cat->term_id == $active)
                    $class[] = 'catalog-menu-item_active';
                if ($cat->parent) {
                    $class[] = 'catalog-menu-item_child';
                    $class[] = 'catalog-menu-item_has-parent_' . $cat->parent;
                } else {
                    $class[] = 'catalog-menu-item_parent';
                }
                ?>
                <li class="<?= implode(' ', $class) ?>" data-parent="<?= $cat->parent ?>" data-id="<?= $cat->term_id ?>">
                    <a href="<?= $term_link; ?>" class="catalog-menu-item__link clearfix">
                        <div class="catalog-menu-item__wrap">
                            <div class="catalog-menu-item__col catalog-menu-item__col_img">
                                <img src="<?= $cat_thumb_url; ?>" alt="" class="catalog-menu-item__img">
                            </div>
                            <div class="catalog-menu-item__col">
                                <div class="catalog-menu-item__label"> 
                                    <?= $cat->name; ?>
                                </div>
                            </div>
                        </div>
                    </a>
                </li>
            <?php endforeach; ?>
        </ul>
    <?php
}

И код в functions.php, где с помощью фильтров WP переопределены подписи статусов товаров (в частности «Под заказ»).

function theme_woocommerce_get_stock_html($html, $product) {

    $availability = $product->get_availability();

    if (!empty($availability['availability'])) {
        return $html;
    }

    ob_start();

    wc_get_template('single-product/stock.php', array(
        'product' => $product,
        'class' => 'in-stock',
        'availability' => 'Есть в продаже'
    ));

    $html = ob_get_clean();
    return $html;
}

add_filter('woocommerce_get_stock_html', 'theme_woocommerce_get_stock_html', 10, 2);

function theme_woocommerce_get_availability_text($availability, $product) {
    if ($product->managing_stock() && $product->is_on_backorder(1)) {
        $availability = 'Под заказ';
    }
    return $availability;
}

add_filter('woocommerce_get_availability_text', 'theme_woocommerce_get_availability_text', 10, 2);

Для разных категорий товаров — разный срок поставки. Текстовое поле backorder, добавленное с помощью ACF, позволяет задать свой срок поставки для всех товаров внутри категории.

В файле вашей темы single-product/stock.php:

<?php
define('DEFAULT_BACKORDER_TIME', 'Предполагаемый срок поставки: 40-45 дней'); // Срок по умолчанию
?>
<p class="stock <?php echo esc_attr($class); ?>"><?php echo wp_kses_post($availability); ?></p>
<?php if ($product->managing_stock() && $product->is_on_backorder(1)): ?>
    <?php
    $categories = get_terms('product_cat', ['hide_empty' => false, 'include' => $product->get_category_ids()]);
    $backorder_a = [];
    foreach ($categories as $cat) {
        $field = get_field('backorder', $cat);
        $backorder_a[] = $field ? $field : DEFAULT_BACKORDER_TIME;
    }
    $backorder = empty($backorder_a) ? DEFAULT_BACKORDER_TIME : array_shift($backorder_a);
    ?>
    <p><?= $backorder ?></p>
<?php endif; ?>

Так можно задавать дополнительные свойства/настройки как для самой категории, так и для товаров внутри нее — тут возможности безграничны.

All In One SEO Pack — я использую этот плагин для всех проектов. Минимум настройки и готово базовое SEO, как говорится, из коробки.

Coming Soon Page & Maintenance Mode — легко кастомизируемая «заглушка», на время разработки и тестирования сайтов, ведь даже на этот период сайт должен выглядеть няшно :3

Cyr to Lat enhanced — плагин для ЧПУ. На данный момент использую его, вместо древнего Cyr-To-Lat, который не умеет в транслитерацию имен загружаемых файлов с кириллицы в латиницу. Так как хочется иметь не только красивые слаги, но и имена аттачей должны быть в порядке, а не что то вроде этого %D0%B0%D1%82%D1%82%D0%B0%D1%87.PNG

Easy Watermark — один из способов защитить оригиналы фотографий товаров интернет-магазина (и не только), с помощью водяных знаков.

В нашем случае, это надпись с доменным именем
в формате PNG32, сделанная в графическом редакторе, белого цвета с тенью, повернутая на 45 градусов (точнее на 45 по версии фотошоп :)) и с прозрачностью всего 4% (значение было выведено опытным путем :))

Изображение ватермарка лучше сразу сделать, как в можно больше высоком разрешении, вплоть до 4K (в зависимости от памяти доступной PHP), а уже потом плагин сам даунсайзит до нужного, в соответствии с разрешением загружаемого фото товара. Иначе знак на фото будет с артефактами и/или «мыльно».

Нужно установить настройки: «Изменение размера» в «Подогнать по ширине», и чекбокс «Только уменьшить».

Можно поиграться с «Масштабом» и «Непрозрачностью», а вот повернуть ватермарк придется в графическом редакторе — такой настройки плагин пока не предоставляет.

Обязательно установите флажок «Backup — Save original images…», по крайней мере на время отладки сайта. Так можно всегда вернуть оригиналы фото, в меню Медиафайлы → Easy Watermark (/wp-admin/upload.php?page=easy-watermark) и нажав «Restore».

Easy Watermark добавляет знак для вновь загружаемых изображений, а для уже добавленных фото есть все то же меню
Медиафайлы → Easy Watermark (/wp-admin/upload.php?page=easy-watermark) и подраздел «Bulk Watermark», с кнопкой «Start».

Google XML Sitemaps — тоже для базового
SEO, установил и забыл, sitemap.xml автоматически генерируется в фоновом режиме, да еще и сжимается gzip, плюс разбивается на части, если карта сайта слишком большая. Вроде еще автоматически должен уведомлять гугл и бинг, судя по всему через какое-то API, но это не точно 🙂

Minimum Order Amount for Woocommerce — плагин для установки минимальной сумма заказа, напр. в этих интернет-магазинах инструментов и строительных приспособ — это 10 000 рублей. Можно конечно самому реализовать подобный функционал с помощью фильтров WP/WC, нескольких строчек кода — как я обычно и поступаю, но когда время ограничено, а ограничить сумму заказа нужно уже сейчас, ставлю готовое решение, которое работает сразу после активации плагина и имеет все нужные настройки в панели управления. Менеджер магазина может самостоятельно менять минимальную сумму и текст сообщений, если потребуется.