Вопрос или проблема
Я хотел бы сделать сортировку постов на страницах архива (теги, категории, авторы и т.д.) с использованием pre_get_posts в файле functions.php.
У меня есть несколько постов с пользовательским полем “рейтинг”, и значение этого поля – целое число, например: 1, 5, 9… Важно отметить, что не у всех постов есть это пользовательское поле.
Если в URL установлен параметр sort=rating, я хотел бы отобразить посты, которые имеют пользовательское поле “рейтинг”, в начале по значению пользовательского поля “рейтинг”, а после постов с этим пользовательским полем я хотел бы показать любые другие посты БЕЗ этого пользовательского поля.
Таким образом, в моем примере это было бы так:
- 1-й – пост с пользовательским полем “рейтинг” со значением “9”
- 2-й – пост с пользовательским полем “рейтинг” со значением “5”
- 3-й – пост с пользовательским полем “рейтинг” со значением “1”
- 4-й – последний опубликованный пост без пользовательского поля “рейтинг”
- …
У меня есть эта функция, но, к сожалению, она показывает посты в каком-то странном порядке, на первом месте пост без пользовательского поля “рейтинг”, также это не чистая сортировка по дате (это не последний пост на первом месте и не самый древний пост на первом месте).
function custom_sort_posts($query) {
if (!is_admin() && $query->is_main_query() && is_archive()) { // Если это основной запрос и не в админской области, и это архив
if (isset($_GET['sort']) && $sort === 'rating') {
$meta_query = [
'relation' => 'OR',
[
'key' => 'rating',
'compare' => 'EXISTS'
],
[
'key' => 'rating',
'compare' => 'NOT EXISTS'
]
];
$query->set('meta_query', $meta_query);
$query->set('orderby', [
'meta_value_num' => 'DESC', // Посты с 'рейтингом' сверху
'date' => 'DESC' // Сортировка по дате
]);
}
}
}
Может кто-то предложить лучшую функцию, если вообще возможно сделать такую сортировку?
Для того чтобы сортировка по мета-значению работала, параметр meta_key
обязателен (источник):
Обратите внимание, что
meta_key=keyname
также должен быть присутствовать в запросе.
К сожалению, это приведет к тому, что будут найдены только посты с установленным мета-ключом.
Отсюда есть два варианта:
- Использовать запрос, чтобы получить все посты, и сортировать посты с помощью PHP.
- Выполнить несколько запросов и объединить результаты.
Каждый вариант имеет свои трудности, но я бы предложил второй, поскольку вы можете выполнить дополнительный запрос с идентификаторами постов, чтобы поддержать пагинацию (не протестировано):
$posts_with_rating = new WP_Query( array(
'posts_per_page' => -1,
'meta_key' => 'rating',
'fields' => 'ids',
'orderby' => array(
'meta_value_num' => 'DESC',
'date' => 'DESC',
),
) );
$posts_without_rating = new WP_Query( array(
'posts_per_page' => -1,
'fields' => 'ids',
'orderby' => 'date',
'order' => 'DESC',
'meta_query' => array(
array(
'key' => 'rating',
'compare' => 'NOT EXISTS',
),
),
) );
$combined = new WP_Query( array(
'post__in' => array_merge( $posts_with_rating->posts, $posts_without_rating->posts ),
'orderby' => 'post__in',
) );
.
Ответ или решение
Для реализации сортировки постов на архивных страницах WordPress с учетом дополнительного поля "rating", необходимо воспользоваться хуком pre_get_posts
в файле functions.php
. При этом потребуется корректно построить запрос так, чтобы сначала отображались посты с указанным значением в поле "rating", а затем посты, у которых это поле отсутствует.
Шаги по реализации
-
Создание функции для обработки запроса:
Ваша функция должна проверять, установлен ли параметр
sort
в URL, и вызывать подготовку запроса на вывод постов. -
Отдельные запросы для постов:
Необходимо сделать два запроса: один для получения постов с полем "rating" и сортировкой по значению этого поля, и другой для получения всех остальных постов.
-
Объединение результатов:
После выполнения обоих запросов, нужно объединить результаты в один массив и отобразить их.
Пример кода
Вот более детализированный и улучшенный код, который решает описанную задачу:
function custom_sort_posts($query) {
// Проверяем, что это основной запрос, архивная страница и не в админке
if (!is_admin() && $query->is_main_query() && is_archive()) {
// Проверяем наличие параметра sort в URL
if (isset($_GET['sort']) && $_GET['sort'] === 'rating') {
// Запрос постов с кастомным полем 'rating'
$posts_with_rating = new WP_Query(array(
'posts_per_page' => -1,
'meta_key' => 'rating',
'orderby' => array(
'meta_value_num' => 'DESC', // Сортировка по мета-значению 'rating'
'date' => 'DESC' // Сортировка по дате
),
));
// Запрос постов без кастомного поля 'rating'
$posts_without_rating = new WP_Query(array(
'posts_per_page' => -1,
'fields' => 'ids',
'meta_query' => array(
array(
'key' => 'rating',
'compare' => 'NOT EXISTS',
),
),
'orderby' => 'date',
'order' => 'DESC', // Сортировка по дате
));
// Объединяем идентификаторы постов
$combined = array_merge($posts_with_rating->posts, $posts_without_rating->posts);
// Устанавливаем идентификаторы постов в основной запрос
$query->set('post__in', $combined);
// Изменяем порядок сортировки
$query->set('orderby', 'post__in');
}
}
}
add_action('pre_get_posts', 'custom_sort_posts');
Объяснение кода
-
Проверка условий:
- Основной запрос, архив (категории, теги и т.д.), не админка.
-
Запрос на получение постов с рейтингом:
- Используется
WP_Query
для получения постов, где поле "rating" существует, сортировка осуществляется сначала по мета-значению, а затем по дате.
- Используется
-
Запрос на получение постов без рейтинга:
- Здесь также используется
WP_Query
, но с условием, что поле "rating" не существует.
- Здесь также используется
-
Объединение результатов:
- Сначала получаем массив идентификаторов постов с рейтингом, затем добавляем к нему идентификаторы постов без рейтинга и устанавливаем их в основной запрос.
-
Установка порядка сортировки:
- Используется
orderby
для последовательного отображения постов в заданном порядке.
- Используется
Заключение
Данный подход позволяет эффективно сортировать посты на страницах архива, демонстрируя посты с рейтингом сверху и посты без рейтинга ниже. Он обеспечивает гибкость и простоту в использовании и может быть расширен для других кастомных полей или дополнительных параметров сортировки.