Суббота, 20 ноября 2021 г.

Встала задача вывести информацию о наличии/предзаказе товара в магазине на WooCommerce в нескольких местах: в карточке товара, в корзине и в заказах, что отображаются в админке магазина. Сразу скажу, что я не адепт так называемых "правильных путей" программирования. А потому решаю каждый случай тем способом, который более эффективен для именно этого случая.

Сначала я стал работать с карточкой товара. Пошёл, как мне казалось, самым простым и очевидным путём. Если вордпресс состоит из мелких блоков-хуков, почему бы не добавить в шаблон content-single-product.php дополнительную вёрстку в нужной позиции? Попробовал. Скопировал /wp-content/plugins/woocommerce/templates/content-single-product.php в /wp-content/themes/<тема>/woocommerce/content-single-product.php и в хук woocommerce_single_product_summary добавил строчку

/**
 * Hook: woocommerce_single_product_summary.
 *
 * ...
 * ...
 * ...
 * ...
 * @hooked woocommerce_template_single_stock - 25
 * ...
 * ...
 * ...
 * ...
 */

А в директорию /wp-content/themes/<тема>/woocommerce/single-product добавил файл stock.php с такой вёрсткой:

<?php
/**
 * Single Product Stock
 *
 * @package     WooCommerce\Templates
 * @version     3.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

global $product;
if ($product->stock_status = 'onbackorder') {
    echo '<div class="stock">Предзаказ</div>';
} else {
    echo '<div class="stock">В наличии</div>';
}

По идее должно работать. Но эта вёрстка отчего-то подключалась только в карточках товаров, у которых стоял статус Предзаказ. У товаров В наличии файл не подключался. Можно было поковыряться и всё-таки найти причину неподключения, но я пошёл другим путём. Так как эту надпись надо было выводить над кнопкой добавления в корзину, я посмотрел в файле /wp-content/plugins/woocommerce/templates/single-product/add-to-cart/simple.php название хука, который выводится перед нужной кнопкой. И в functions.php повесил на этот хук woocommerce_before_add_to_cart_form вывод этой же самой вёрстки с немного изменёнными условиями:

add_filter( 'woocommerce_before_add_to_cart_form' , 'jinsite_product_availability' );
function jinsite_product_availability() {
    global $product;
    $availability = $product->get_availability();
    if ( $availability['availability'] == 'Доступно для предзаказа') {
        echo '<div class="stock">Предзаказ</div>';
    } else {
        echo '<div class="stock">В наличии</div>';
    }
}

Далее меняем корзину. Поскольку информация о товарах в корзине подгружается через массив базы данных, я тоже сразу решил обойтись простой вёрсткой корзины. Для этого я скопировал шаблон корзины cart.php из /wp-content/plugins/woocommerce/templates/cart/ в директорию темы /wp-content/themes/<тема>/woocommerce/cart/ и начал править.

Сначала для того, чтобы вывести заголовок "Наличие", добавил в вёрстку заголовков строчку

<th class="product-stock">Наличие</th>

Знаю-знаю, что русские надписи в шаблонах принято выводить через конструкции вида esc_html_e('any_text', 'theme_domain'). Но это относится к мультиязычному содержимому и необходимо только для оформления кода в официальном репозитории Wordpress. В коде для одного сайта, при условия сохранения файлов в юникоде, вполне допустимо включать русские надписи прямо в код. Не вижу в этом ничего дурного.

Чуть ниже, в теле таблицы, где перечисляется вывод данных о заказах, добавил код

<td class="product-stock" data-title="Stock">
   <?php
    if ($_product->stock_status == 'instock') {
        echo '<div class="stock_in_cart">В наличии</div>';
    } else {
        echo '<div class="stock_in_cart">Предзаказ</div>';
    }
   ?>
</td>

Тут главное не промахнуться и добавить новую ячейку, точнее, новый столбец таблицы в ту же позицию, что и заголовок. Как можно заметить, условие вывода изменено. Это зависит от того, что массив $product->get_availability() доступен только в карточке заказа. В корзине приходится использовать другой параметр для выборки значения наличия товара.

Ну и самое интересное - админка. Шаблонов нет, поэтому пришлось работать одними хуками. Благо, прописанные в functions.php активной темы экшены распространяются и на админку тоже. Сначала выбрал место вывода. В списке заказов не получится, так как в одном заказе могут быть собраны товары с разными статусами. Значит, нужно выводить информацию в списке товаров внутри заказа. В результате недолгих поисков был найден хук woocommerce_after_order_itemmeta. Пишем:

add_action( 'woocommerce_after_order_itemmeta', 'jinsite_product_availability_order', 20, 3 );
function jinsite_product_availability_order( $item_id, $item, $product ) {
    if( is_admin() && $product->stock_status == 'instock') {
       echo '<p class="jinsite-console-order-availability">В наличии</p>';
   } else {
        echo '<p class="jinsite-console-order-availability">Предзаказ</p>';
    }
}

Условие вывода то же самое, что и для корзины, плюс указание на вывод в админке. Всё отлично, но появилось одно "но". Дело в том, что этот хук почему-то выводится в двух местах на странице заказа в админке. Собственно в списке товаров и ещё в блоке с выбранным методом доставки. Возможно, это промах команды WooCommerce, один и тот же хук в двух местах. Понятно, что программно исправить это нельзя. Остаётся одно - скрывать стилями. Причём нужно помнить, что файлы стилей для фронта сайта в админке не работают. Добавляем новый файл стилей в директорию нашей темы. Я назвал его style-admin.css. Прописал в нём правило

#order_shipping_line_items p.jinsite-console-order-availability {
    display: none;
}

А в functions.php подключил этот файл в админке:

add_action('admin_head', 'jinsite_style_admin');
function jinsite_style_admin() {
    wp_enqueue_style( 'admin-style', get_stylesheet_directory_uri() . '/style-admin.css' );
}

После этого информация о наличии/предзаказе скрылась из блока с выбранным методом доставки.