Вопрос или проблема
Я уже много исследовал этот вопрос, но нигде не нашел подходящего ответа. Это очень небольшая задача для меня, если нужно сделать запрос MySQL. Но так как у меня нет хорошего опыта работы в WP, я очень расстроен из-за решения этого вопроса.
Как сортировать wp_query по пользовательскому столбцу, который я добавил в таблицу wp_posts.
$args = array(
/*'base' => '%_%',
'format' => '?paged=%#%',
'total' => 1,
'current' => 0,
'show_all' => false,
'end_size' => 1,
'mid_size' => 2,
'prev_next' => true,
'prev_text' => __('« Previous'),
'next_text' => __('Next »'),
'type' => 'plain',
'add_args' => false,
'add_fragment' => '',
'before_page_number' => '',
'after_page_number' => ''*/
'posts_per_page' => 10,
'post_type' => array('food', 'buffets'),
'meta_key' => 'post_priority',
'orderby' => 'meta_value_num',
'order' => 'ASC'
);
Когда я пробую с этим, на самом деле сортировка идет по таблице wp_postmeta wp_postmeta.meta_value+0, что означает, что вместо таблицы wp_posts она сортируется по таблице postmeta. Post_priority — это мой пользовательский столбец в таблице wp_posts
Вот результат запроса, который я получил:
"SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts
INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )
WHERE 1=1 AND ( \n wp_postmeta.meta_key = 'post_priority'\n)
AND wp_posts.post_type IN ('food', 'buffets')
AND (wp_posts.post_status="publish" OR wp_posts.post_author = 2
AND wp_posts.post_status="private")
GROUP BY wp_posts.ID
ORDER BY wp_postmeta.meta_value+0
ASC LIMIT 0, 10","posts"
Вы указали сортировку по meta_value, поэтому сортировка идет по этому значению. В таблице wp-posts нет столбца “post_priority”, поэтому сортировка по нему невозможна. Допустимые значения для “orderby” не безграничны, это фиксированный набор.
Вы не можете изменить базовые таблицы и ожидать, что код будет об этом знаеть. Основная причина наличия таблицы postmeta заключается в том, чтобы позволить вам вставлять произвольные метаданные вместо того, чтобы изменять таблицы, добавляя столбцы, о которых код не знает.
Удалите ваш дополнительный столбец и храните данные как postmeta. Установите meta_key в “priority” и meta_value в значение приоритета. Тогда ваш запрос будет работать.
Я немного исследовал вашу проблему, и она заключается в функции WP_Query::parse_orderby()
. Там все, что вы хотите использовать в качестве сортировки, проходит санитизацию, как указал пользователь Otto. К сожалению, нет фильтра для вмешательства. И да, лучше использовать некоторые мета поля, если вы можете позволить себе дорогой JOIN в каждом get_posts. Если нет, имейте в виду, что поле menu_order
тоже может быть использовано, если вы не имеете дела с типами постов nav_menu_item
.
В любом случае, я нашел два решения и хочу поделиться ими здесь. Для получения более подробной информации прочтите https://wp-includes.org/296/custom-wp_posts-column-sortable/
Расширение WP_Query
Самый чистый способ — написать собственный класс запроса:
<?php
class Enhanced_Post_Table_Query extends \WP_Query {
/**
* Расширить порядок клаузов собственными столбцами.
*
* @param string $order_by
*
* @return bool|false|string
*/
protected function parse_orderby( $order_by ) {
$parent_orderby = parent::parse_orderby( $order_by );
if ( $parent_orderby ) {
// WordPress знает, что делать => оставляем как есть
return $parent_orderby;
}
// список разрешенных полей, которые мы расширили
$additional_allowed = array(
'something',
);
if ( ! in_array( $order_by, $additional_allowed, true ) ) {
// не разрешенный столбец => ранний выход
return false;
}
// По умолчанию: сортировать по полю поста.
global $wpdb;
return $wpdb->posts . '.post_' . sanitize_key( $order_by );
}
}
Теперь вы можете выполнять запросы и сортировать с помощью пользовательского поля:
$get_posts = new Enhanced_Post_Table_Query;
$get_posts->query(
array(
'orderby' => 'something'
)
);
Поздний фильтр для перезаписи
Немного грязный способ, но он работает. Так как нет прямого фильтра для выполнения этой задачи, вы можете выбрать более поздний фильтр и манипулировать запросом (чем позже, тем более грязно):
<?php
/**
* Добавить пользовательский столбец wp_posts для сортировки.
*
* @param string $order_clause SQL-клаузула для сортировки.
* @param WP_Query $query Объект запроса.
*
* @return string Клаузула порядка, например, "wp_posts.post_foo DESC" или подобное.
*/
function custom_column_sort_filter( $order_clause, $query ) {
// список разрешенных полей, которые мы расширили
$additional_allowed = array(
'something',
);
if (
! in_array(
$query->get('orderby'),
$additional_allowed,
true
)
) {
// неизвестный столбец => оставить как было
return $order_clause;
}
global $wpdb;
return $wpdb->posts
. '.post_'
. sanitize_key( $query->get('orderby') )
. ' ' . $query->get( 'order' );
}
add_filter( 'posts_orderby', 'custom_column_sort_filter', 10, 2 );
Теперь почти каждый вызов get_posts получает информацию о вашем пользовательском столбце:
get_posts(
[
'orderby' => 'something',
// ВАЖНО:
'suppress_filters' => false,
]
);
Но только если установлено “suppress_filters”. Это должно использоваться всеми плагинами. Есть еще больше решений через preg_replace, но они очень поздние и замена с помощью REGEXP всегда грязная и опасная.
Надеюсь, вы сможете с этим работать!
.
Ответ или решение
Чтобы ответить на ваш вопрос о сортировке wp_query
по пользовательской колонке, добавленной в таблицу wp_posts
, нам нужно более подробно рассмотреть структуру базы данных WordPress и то, как она обрабатывает пользовательские данные.
Теория:
WordPress обрабатывает настроенные поля и метаданные с использованием специализированной таблицы wp_postmeta
, а не изменяет структуру основной таблицы wp_posts
. Это позволяет более безопасно и гибко работать с метаданными без модификации базовой модели данных WordPress. Ключевой функцией этой архитектуры является отделение данных от логики, что позволяет избежать ошибок и конфликтов при обновлении WordPress.
Стандартный метод сортировки записей по пользовательскому полю заключается в использовании параметра meta_key
в WP_Query
, который затем указывает на сортировку по meta_value
.
Пример:
Ваш текущий запрос предполагает, что сортировка должна происходить по колонке post_priority
в wp_posts
, которая не поддерживается WordPress по умолчанию. Вместо этого, рекомендуется использовать таблицу wp_postmeta
.
Пример использования настройки для сортировки:
$args = array(
'posts_per_page' => 10,
'post_type' => array('food', 'buffets'),
'meta_key' => 'post_priority',
'orderby' => 'meta_value_num',
'order' => 'ASC'
);
Теперь вы указали WordPress, что нужно использовать поле metakey для сортировки, что он обрабатывает через добавление внутреннего соединения на wp_postmeta
.
Применение:
Если действительно нужна сортировка по колонке из wp_posts
, например post_priority
, у которой нет стандартного интерфейса, вам следует прибегнуть к расширению функционала через создание специальных классов или использование фильтров.
Первый подход: Создание собственного класса запроса, расширяя WP_Query
, чтобы вручную обрабатывать параметр orderby
.
class Enhanced_Post_Table_Query extends WP_Query {
protected function parse_orderby( $order_by ) {
$parent_orderby = parent::parse_orderby( $order_by );
if ( $parent_orderby ) {
return $parent_orderby;
}
// Допустимые дополнительные поля
$additional_allowed = array(
'post_priority',
);
if ( ! in_array( $order_by, $additional_allowed, true ) ) {
return false;
}
global $wpdb;
return $wpdb->posts . '.post_' . sanitize_key( $order_by );
}
}
Второй подход: Использование фильтра posts_orderby
на более позднем этапе обработки запроса, чтобы вручную указать WordPress использовать настроенную колонку.
function custom_column_sort_filter( $order_clause, $query ) {
$additional_allowed = array(
'post_priority',
);
if ( ! in_array( $query->get('orderby'), $additional_allowed, true ) ) {
return $order_clause;
}
global $wpdb;
return $wpdb->posts . '.post_' . sanitize_key( $query->get('orderby') ) . ' ' . $query->get( 'order' );
}
add_filter( 'posts_orderby', 'custom_column_sort_filter', 10, 2 );
Эти решения дадут вам нужную гибкость и безопасность при работе с нестандартными потребностями сортировки в WordPress. Следует всегда помнить, что модификация базовых таблиц может привести к проблемам совместимости в будущем.