Сортировка товаров по атрибуту и мета полям Woocommerce

Изменить стандартную сортировку отображения товаров в каталоге довольно просто, если конечно использовать стандартные мета поля, а не атрибуты.
Для изменения сортировки есть специальный фильтр woocommerce_get_catalog_ordering_args, давай посмотрим как он работает.
Предположим мы хотим сделать сортировку по названию товара для этого создадим функцию, и пропишем необходимые переменные.
add_action( 'woocommerce_get_catalog_ordering_args', 'new_sort' );
function new_sort($args){
$args['order'] = 'ASC';
$args['orderby'] = 'title';
return $args;
}Вместо значения «title» можно подставить любое из существующих полей в бд например author или status. Если в переменную «orderby» прописать несуществующие поле, то каталог покажет уведомление об отсутствие товаров.

Так же можно сделать сортировку по метаполю, которых у товара ни так уж и мало:
- _price (цена)
- _sku (артикул)
- _stock_status (Наличие)
- _stock (Кол-во товаров в наличие)
- _weight (Вес товара)
- _length — Длина
- _width — Ширина
- _height — Высота
Пример сортировки по Артикулу
function new_sort($args){
$args['order'] = 'ASC';
$args['meta_key'] = '_sku';
$args['orderby'] = 'meta_value';
return $args;
}Предположим у нас есть атрибут размер или цвет, с альтернативным названием (Слаг) color и razmer.

Как сортировать по атрибутам, если значения не сохраняются отдельной ячейкой pa_color и pa_razmer, как это делается с Артикулом или Ценой товара.
Пример из PhpMyAdmin таблица wp_postmeta

Сохраняются атрибуты товара в поле meta_key со значением _product_attributes, и хранится в виде сериализованного массива. Естественно сортировать по таким данным будет не правильно, и результата не будет.
Чтобы убедиться в этом, сделайте обычный sql запрос в phpmyadmin, только не забываем подставить ID товара.
SELECT * FROM `wp_postmeta` WHERE post_id='id продукта'

Что делать?
Как вариант сохранять атрибуты товара в базе данных как отдельные записи, например вот так:

Для это задачи есть специальная функция update_post_meta, которая должна выполняться после нажатия кнопки обновить внутри карточки товара.
В файле function.php вашей темы пишем следующий код.
//Для обычного товара
add_action( 'woocommerce_process_product_meta_simple', 'update_attr_to_meta' );
//Для вариативного товара
add_action( 'woocommerce_process_product_meta_variable', 'update_attr_to_meta' );
function update_attr_to_meta($post_id){
//Проверяем наличие атрибутов
if(isset($_POST['attribute_names']) and !empty($_POST['attribute_names'])){
//Перебираем массив с атрибутами
foreach($_POST['attribute_names'] as $num=>$pa_name){
//Проверяем добавлено ли значение к атрибуту
if(isset($_POST['attribute_values'][$num])){
//Если у атрибута несколько значений, берём самое первое
if(is_array($_POST['attribute_values'][$num])){
$pa_value=$_POST['attribute_values'][$num][0];
}else{
//Если одно, обычно это Индивидуальный атрибут
$pa_value=$_POST['attribute_values'][$num];
}
//Обновляем мета данные всех добавленных атрибутов
update_post_meta($post_id,'_'.$pa_name,$pa_value);
}
}
}
}Обратите внимание, что добавление нижнего подчёркивания к _pa_name скрывает его отображение в Произвольных полях.
Если нужно добавить мета поля определённых атрибутов, строчку
update_post_meta($post_id,'_'.$pa_name,$pa_value);
Меняем на нижнюю строку, предварительно заменив строку pa_razmer на слаг вашего атрибута,например pa_color, pa_cvet,pa_shirina и т.д
if($pa_name=='pa_razmer')update_post_meta($post_id,'_'.$pa_name,$pa_value);
Что делает php скрипт?
При сохранение товара в админ.панели, проверяются добавленные атрибуты, и они сохраняются в БД.
Предупреждение! Для одного атрибута по которому предполагается сортировка, может быть добавлено лишь ОДНО значение, иначе будет взято самое первое из массива.

Когда у всех товаров будут добавлены мета поля , в нашем случае _pa_razmer и _pa_color мы можем выполнить сортировку
Сортировка по Размеру
add_action( 'woocommerce_get_catalog_ordering_args', 'new_sort');
function new_sort($args){
$args['order'] = 'ASC';
$args['meta_key'] = '_pa_razmer';
$args['orderby'] = 'meta_value_num';
return $args;
}Сортировка по цвету
add_action( 'woocommerce_get_catalog_ordering_args', 'new_sort');
function new_sort($args){
$args['order'] = 'ASC';
$args['meta_key'] = '_pa_color';
$args['orderby'] = 'meta_value';
return $args;
}В моём случае нужно было сделать сортировку уже заполненному каталогу, в котором более 5000 товаров, поэтому понадобилась ещё одна функция, которая заполнит мета поля Размер и Цвет каждому товару автоматически.
Данную функцию нужно выполнить всего один раз, а затем либо удалить её из файла function.php , либо закоментировать её, чтобы не создавать лишнюю нагрузку на базу данных.
add_action('woocommerce_loaded','new_attribute_to_meta');
function new_attribute_to_meta(){
global $wpdb;
$num=3000; //Кол-во обрабатываемых записей
foreach(wc_get_products(array('numberposts' => $num)) as $pr){
foreach($pr->get_attributes() as $key=>$attr){
$val=wc_get_product_terms( $pr->get_id(), $attr->get_name(), array( 'fields' => 'all' ) );
if($key=='pa_razmer' or $key=='pa_marka' or $key=='pa_size'){
update_post_meta($pr->get_id(),'_'.$key,$val[0]->name);
}
}
}
}Обратите внимание на условие, я использую СЛАГ существующего атрибута, в вашем случае слаг атрибута может иметь другое название. Данную функцию так же нужно подключить в файл function.php, при условие если необходимо создать мета поля уже существующим товарам.


Анатолий
Делаю по этой инструкции, на первый взгляд всё работает, но на самом деле у меня сортировка происходит не по атрибутам, а по их ID. Т.е. в ‘meta_value’ хранится не значение атрибута, а его ID. Подскажите, пожалуйста, какой ещё момент я упускаю из виду?