Продолжаю цикл заметок о разработке интернет-магазинов на WordPress & WooCommerce.
Главными героями этой заметки будут вот эта замечательная табличка «Доступные Варианты» с псевдо-3D эффектом и супер-полезная панелька «Тулбар» для посетителей (aka покупателей) интернет-магазина, с секциями «Пользователь», «Избранное», «Сравнение», «Корзина», «Поиск»:
Я опишу основные моменты, интеграцию со сторонними плагинами, примеры кода — информация, более ориентированная на разработчиков.
Если вы просто хотите получить описанный функционал на свой интернет-магазин, то переходите сразу в конец заметки.
Таблица «Доступные Варианты»
Обычно подобный список в экосистеме WooCommerce формируется из вариаций (Product Variation), в карточке вариативного товара (Variable Product). Но в моем случае, список формировался из товаров, находящихся в одной категории (подкатегории) — это более простой способ организовать «варианты», хоть это и не right way с точки зрения разработчиков WooCommerce. «Дайте мне точку опоры, и я переверну Землю» — слова Архимеда, а нам нужен какой-то признак/поле/атрибут по которому будут группироваться «варианты», и один из самых простых — категории.
Так получаем «варианты» в открытой карточке, передав текущий товар (global $product;).
/**
*
* @param WC_Product $product
*
* @return WC_Product[] variations
*/
function wcvl_get_variations($product) {
$cat_id = end($product->get_category_ids());
$category = get_term($cat_id, 'product_cat');
$args = [
'category' => isset($category->slug) ? $category->slug : [],
'exclude' => [$product->get_id()]
];
$variations = wc_get_products($args);
return $variations;
}
Отбираем с них массив атрибутов для генерации колонок, следующих после «Артикул» и заканчивающихся на колонке «Цена», у нас это «Количество ступеней», «Рабочая высота», …, «Вес».
Атрибуты должны быть заполнены, чтобы попасть в список (спасибо, Кэп). Таким образом, таблица формируется автоматически, без какой-либо дополнительной конфигурации, просто на основании заполненных атрибутов.
Эта функция возвращает многомерный массив с variation_id (product_id) => массив объектов WC_Product_Attribute (атрибуты товара).
/**
*
* @param WC_Product[] $variations
*
* @return WC_Product_Attribute[] attributes
*/
function wcvl_get_attributes($variations) {
if (empty($variations))
return null;
$attributes = [];
foreach ($variations as $variation) {
$attributes[$variation->get_id()] = $variation->get_attributes();
}
return $attributes;
}
Берем из массива первую «строку» (массив атрибутов первого товара в списке).
/**
*
* @param WC_Product_Attribute[] $attributes
*
* @return WC_Product_Attribute[] attributes
*/
function wcvl_get_key_attributes($attributes) {
return empty($attributes) ? null : current($attributes);
}
Так формируется часть заголовка таблицы с атрибутами:
<?php foreach ($key_attributes as $key => $attribute): ?>
<td class="variation-table-th">
<div class="variation-table-th__wrap">
<span class="variation-table-th__inner">
<?= $attribute->get_name() ?>
</span>
</div>
</td>
<?php endforeach; ?>
Магия псевдо-3D заключается в повороте заголовка таблицы под углом в 45 градусов — это можно сделать одними средствами CSS, правда не обойтись без лишних тегов, первый из которых, див-враппер с магическим свойством transform:
.variation-table-th__wrap {
transform: translate(58px,-34px) rotate(315deg);
width: 60px;
}
Чтобы граница-разделитель заголовков столбцов была по длине надписи, необходимо заключить ее в инлайн тег, со стилем:
.variation-table-th__inner {
border-bottom: 1px solid #ddd;
padding: 24px 0;
}
Свойство white-space: nowrap не позволит переносить текст.
Ячейки со значениями свойства товара:
<?php foreach ($key_attributes as $key => $attribute): ?>
<td class="<?= wcvl_get_attribute_class($variation, $key, $attributes) ?>" data-th="<?= esc_attr($key_attributes[$key]->get_name()) ?>">
<div class="variation-item__val">
<?= wcvl_get_attribute_val($variation, $key, $attributes) ?>
</div>
</td>
<?php endforeach; ?>
В мобильной версии строки таблицы превращаются в «карточки» (card/thumbnail component). Тут секрет прост: все, что было display: table/table-row/table-cell (соотв. table/tr/td) преобразуем в block, и стилизуем по вкусу под мобайл.
@media (max-width: 991px) {
.variation-item {
display: block;
box-shadow: rgba(0, 0, 0, 0.05) 0px 2px 2px 0px;
border-radius: 4px;
margin-bottom: 15px;
}
}
Кнопки добавления в избранное, сравнение и пр. рассмотрим вместе с соответствующими секциями тулбара.
Тулбар для WooCommerce
Некоторые секции задействуют сторонние плагины, остальные на голом WP/WC API. Разберем каждую секцию по порядку.
Пользователь
Простая секция с 2-мя сосотяниями/сценариями использования:
Авторизованный пользователь: иконка с отображением юзернейма, при нажатии — переход в личный кабинет.
Гость: иконка, при нажатии — переход на страницу регистрации/входа.
TODO: сделать дополнительные опции для пользователя, всплывающее юзер-меню, аватары для магазинов с развитым комьюнити.
Избранное
Как «движок» для списка желаемого, используется плагин YITH WooCommerce Wishlist.
У плагина есть небольшое JS API с нужными событиями, которые просто перехватываем.
added_to_wishlist — добавлено в избранное.
$('body').on('added_to_wishlist', function (e, el, el_wrap) {
$('.svg-spin-loader').hide() // Прячем индикатор-спиннер
// Показываем бутстраповское всплывающее окно (html-dom предварительно прописан в footer.php)
$('.global-modal__title').text('Товар добавлен в избранное')
$('.global-modal__button_cancel').text('Вернуться к сайту')
$('.global-modal__button_ok').attr('href', '/wishlist')
$('.global-modal__button_ok').text('Перейти в «Избранное»')
$('.global-modal__body').hide()
$('.global-modal').modal('show')
// Получаем обновленные счетчики для панельки (на сервере собственный wp_ajax hook)
$.post(ajax_object.ajax_url, {action: 'b2w_wc_stat'}, function (a) {
$('.b2w-app-bar__count_wishlist').text(a.wishlist_count)
})
})
removed_from_wishlist — соответственно, удалено из вишлист.
Шаблон блока, кнопки добавить/удалить, а так же вид самого списка избранного можно легко изменять, просто скопировав
соотв. файл из директории /wp-content/plugins/yith-woocommerce-wishlist/templates в директорию с вашей темой.
Сравнение
Для сравнения выбран простенький плагин WooCommerce Compare List
К сожалению, у него не оказалось JS API, но я решил исправить этот недостаток 🙂
Вешаем на кнопку обработчик onclick, и по наличию класса (в примере variation-item__button_active) определяем находится ли товар в списке сравнения; удалять его от туда или добавлять.
$('.variation-item__button_compare').on('click', function (e) {
e.preventDefault()
const t = $(this)
if (t.hasClass('variation-item__button_active')) {
$('body').trigger('removing_from_compare', [t])
$.get(t.data('remove-url'), function (a) {
$('body').trigger('removed_from_compare', [t])
})
} else {
$('body').trigger('adding_to_compare', [t])
$.get(t.data('add-url'), function (a) {
$('body').trigger('added_to_compare', [t])
})
}
})
Url для GET-запроса берется из data-атрибутов кнопки, значения которых получаются из функций на бекенде.
$remove_url = wccm_get_compare_link($product_id, 'remove-from-list');
$add_url = wccm_get_compare_link($product_id, 'add-to-list');
В итоге имеем 4 события: adding_to_compare, removing_from_compare — на старте операции добавления/удаления из списка сравнения — можно на эти события повесить показ индикатора загрузки, а added_to_compare и removed_from_compare — события при успешном завершении операции — убираем индикатор, показываем сообщение, изменяем состояние кнопки/иконки и пр.
$('body').on('added_to_compare', function (e, el) {
$('.svg-spin-loader').hide()
$('.global-modal__title').text('Товар добавлен в список сравнения')
$('.global-modal__button_cancel').text('Вернуться к сайту')
$('.global-modal__button_ok').attr('href', '/compare')
$('.global-modal__button_ok').text('Список сравнения')
$('.global-modal__body').hide()
$('.global-modal').modal('show')
el.addClass('variation-item__button_active')
el.attr('title', 'Удалить из сравнения')
el.tooltip('hide')
el.tooltip('fixTitle')
$.post(ajax_object.ajax_url, {action: 'b2w_wc_stat'}, function (a) {
$('.b2w-app-bar__count_compare').text(a.compare_count)
})
})
Корзина
Тут стандартное JS API WooCommerce: вешаем обработчик на adding_to_cart, если хотим сделать что-то в самом начале процесса добавления в корзину — я запускаю все тот же крутящийся спин-
прогрессбар, как индикатор того, что операция выполняется. И added_to_cart — по завершению добавления в корзину, убираем спин, выводим бутсраповский диалог с вариантами, обновляем количество в разделе тулбара.
Поиск
На десктопе: при наведении курсора — расширяется область поля запроса, устанавливается фокус; при уходе указателя мыши — возвращается в исходное положение.
На мобилках: при тапе/клике — инпут раздвигается на всю ширину экрана, устанавливается фокус; при тапе вне поля — исходный вид.
Как получить подобные улучшения на свой сайт?
Этот, и любой другой функционал, я готов реализовать для вашего интернет-магазина!
Для связи: bridge2web@gmail.com или @bridge2web