Вопрос или проблема
У меня есть обычные/простые продукты и продукты с различиями в базе данных. У обычных продуктов и вариаций есть связанная с ними ширина продукта (_pm_width_inches). Я пытаюсь отсортировать по ширине и вижу, что сортировка дает случайные результаты вместо возрастания или убывания.
О мета-ключе “_pm_width_inches”, его значения хранятся в двойных кавычках, например, 12″, также при выборе всех для некоторых post_id его значение пусто, так как это родительский пост_id вариации. Прилагаю скриншот (select_width.png).
Вот код, который я использую:
add_filter( 'woocommerce_get_catalog_ordering_args', 'sort_by_width_woocommerce_shop' );
function sort_by_width_woocommerce_shop( $args ) {
global $wp_query;
$orderby_value = isset( $_GET['orderby'] ) ? wc_clean( (string) wp_unslash( $_GET['orderby'] ) ) : wc_clean( get_query_var( 'orderby' ) );
if ( 'width-asc' == $orderby_value ) {
$args['orderby'] = 'meta_value_num';
$args['order'] = 'ASC';
$args['meta_key'] = '_pm_width_inches';
}
else if('width-desc'== $orderby_value){
$args['orderby'] = 'meta_value_num';
$args['order'] = 'DESC';
$args['meta_key'] = '_pm_width_inches';
}
return $args;
}
// 2. Добавление нового фильтра продуктов в выпадающий список сортировки
add_filter( 'woocommerce_catalog_orderby', 'custom_woocommerce_catalog_orderby' );
function custom_woocommerce_catalog_orderby( $orderby ) {
unset($orderby);
$orderby['price'] = __('Цена: от низкой к высокой');
$orderby['price-desc'] = __('Цена: от высокой к низкой');
$orderby['popularity'] = __('По популярности');
$orderby['date'] = __('Новинки');
$orderby['width-asc'] = __('Ширина: от маленькой к большой');
$orderby['width-desc'] = _('Ширина: от большой к маленькой');
return $orderby;
}
add_filter( 'woocommerce_catalog_orderby', 'custom_woocommerce_catalog_orderby', 20 );
и когда я сортирую, я вижу этот результат:
Код ниже работает для простых продуктов, но не для продуктов с вариацией
add_filter( 'woocommerce_get_catalog_ordering_args', 'sort_by_width_woocommerce_shop' );
function sort_by_width_woocommerce_shop( $args ) {
global $wp_query;
$orderby_value = isset( $_GET['orderby'] ) ? wc_clean( (string) wp_unslash( $_GET['orderby'] ) ) : wc_clean( get_query_var( 'orderby' ) );
if ( 'width-asc' == $orderby_value ) {
$args['orderby'] = 'meta_value_num';
$args['order'] = 'ASC';
$args['meta_key'] = '_pm_width_inches';
$args['meta_type'] = 'NUMERIC';
$args['type'] = 'product';
}
else if('width-desc'== $orderby_value){
$args['orderby'] = 'meta_value_num';
$args['order'] = 'DESC';
$args['meta_key'] = '_pm_width_inches';
$args['type'] = 'product';
}
/* ?>
<?php echo "<ul>". var_dump( $args['meta_key'] )."</ul>";?>
<?php */
return $args;
}
// 2. Добавление нового фильтра продуктов в выпадающий список сортировки
add_filter( 'woocommerce_catalog_orderby', 'custom_woocommerce_catalog_orderby' );
function custom_woocommerce_catalog_orderby( $orderby ) {
unset($orderby);
$orderby['price'] = __('Цена: от низкой к высокой');
$orderby['price-desc'] = __('Цена: от высокой к низкой');
$orderby['popularity'] = __('По популярности');
$orderby['date'] = __('Новинки');
$orderby['width-asc'] = __('Размер: от маленького к большому');
$orderby['width-desc'] = _('Размер: от большого к маленькому');
/* ?>
<?php echo "<ul>".var_dump($orderby)."</ul>";?>
<?php */
return $orderby;
}
add_filter( 'woocommerce_catalog_orderby', 'custom_woocommerce_catalog_orderby', 20 );
Ответ или решение
Чтобы грамотно решить проблему сортировки товаров в WooCommerce по фактической ширине продукта, а не по ширине упаковки, нужно учесть несколько ключевых моментов, которые, судя по предоставленному коду и описанию, вызывают проблемы. Давайте рассмотрим этот вопрос пошагово.
Теория (Theory)
В мире WooCommerce работа с произвольными полями (meta fields) для сортировки может быть сложной задачей из-за специфики хранения данных и ограничений, связанных с заказами SQL-запросов. Когда дело касается вариативных продуктов, ситуация усложняется, поскольку WooCommerce обрабатывает вариации как отдельные сущности, однако метаданные для них могут быть пустыми, если они присвоены только родительскому продукту.
Вот общая структура работы с мета-данными:
-
Мета-ключи и мета-значения: WooCommerce использует таблицу
wp_postmeta
для хранения дополнительных данных о продуктах. Нам нужны ключи и значения в формате, удобном для сортировки. -
Формат хранения данных: В вашем случае мета-значения представляют собой строки с дюймами в кавычках (например,
"12"
). Это усложняет сортировку по числовым значениям, так как они обрабатываются как строки. -
Сортировка: WooCommerce позволяет изменить порядок сортировки на страницах магазина через фильтры, такие как
woocommerce_get_catalog_ordering_args
, однако ошибки в конфигурации SQL-запроса могут привести к неожиданным результатам.
Пример (Example)
Возьмем за основу код, который вы привели:
add_filter( 'woocommerce_get_catalog_ordering_args', 'sort_by_width_woocommerce_shop' );
function sort_by_width_woocommerce_shop( $args ) {
$orderby_value = isset( $_GET['orderby'] ) ? wc_clean( (string) wp_unslash( $_GET['orderby'] ) ) : wc_clean( get_query_var( 'orderby' ) );
if ( 'width-asc' == $orderby_value ) {
$args['orderby'] = 'meta_value_num';
$args['order'] = 'ASC';
$args['meta_key'] = '_pm_width_inches';
}
else if('width-desc'== $orderby_value){
$args['orderby'] = 'meta_value_num';
$args['order'] = 'DESC';
$args['meta_key'] = '_pm_width_inches';
}
return $args;
}
Код выше предполагает сортировку по мета-значению _pm_width_inches
, однако не учитывает формат хранения данных и наличие пустых значений в случаях вариативных продуктов. Это приводит к неправильной обработке на уровне SQL-запроса.
Применение (Application)
Для корректной сортировки по фактической ширине продукта, включающей работу с вариативными продуктами, необходимо внести следующие изменения в вашу текущую реализацию:
-
Тип данных и форматирование: Убедитесь, что строки с кавычками преобразованы в числа перед сортировкой. Это можно сделать, применив функцию для предварительной обработки мета-значений. Рекомендуется использовать хук
woocommerce_product_query
для модификации запроса о продуктах до выполнения. -
Обработка вариативных продуктов: При работе с вариативными продуктами важно убедиться, что вы выбираете данные для каждой вариации отдельно, а не наследуете значения от родительского продукта.
-
Оптимизация запроса: Избегайте избыточных запросов к базе данных. Это можно сделать путем объединения запросов и применения кэширования.
-
Миграция и обновление значений: Если возможно, обновите формат хранения широт в базе данных, чтобы они были сохранены как числа, это значительно упростит последующую работу с ними.
Вот пример улучшенного подхода:
add_action( 'pre_get_posts', 'custom_sort_by_width' );
function custom_sort_by_width( $query ) {
if ( is_shop() && $query->is_main_query() && ($orderby_value = get_query_var( 'orderby' )) ) {
if ( 'width-asc' === $orderby_value || 'width-desc' === $orderby_value ) {
$query->set('meta_key', '_pm_width_inches');
$query->set('orderby', 'meta_value_num');
$query->set('meta_query', array(
array(
'key' => '_pm_width_inches',
'type' => 'DECIMAL',
'compare' => 'EXISTS',
),
));
$order = 'width-asc' === $orderby_value ? 'ASC' : 'DESC';
$query->set('order', $order);
}
}
}
В этом кусочке кода мы добавляем обработку типа данных как DECIMAL
, что может оказаться необходимым, если изменится формат данных в базе. Этот подход обеспечивает более надежное выполнение запросов и корректную сортировку, включая продукты с вариациями.
Заключение
Таким образом, для реализации надежной сортировки продуктов в WooCommerce по ширине, хранимой в качестве мета-данных, требуется комплексный подход, который включает форматирование данных, корректную работу с вариативными продуктами и оптимизацию запроса к базе данных. Следуя вышеизложенным рекомендациям, вы сможете реализовать эффективное и надежное решение задачи, избегая хаотичных результатов сортировки.