Сортировать посты по числовому значению пользовательского поля, а также показывать любые другие посты без этого пользовательского поля.

Вопрос или проблема

Я хотел бы сделать сортировку постов на страницах архива (теги, категории, авторы и т.д.) с использованием 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 также должен быть присутствовать в запросе.

К сожалению, это приведет к тому, что будут найдены только посты с установленным мета-ключом.

Отсюда есть два варианта:

  1. Использовать запрос, чтобы получить все посты, и сортировать посты с помощью PHP.
  2. Выполнить несколько запросов и объединить результаты.

Каждый вариант имеет свои трудности, но я бы предложил второй, поскольку вы можете выполнить дополнительный запрос с идентификаторами постов, чтобы поддержать пагинацию (не протестировано):

$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", а затем посты, у которых это поле отсутствует.

Шаги по реализации

  1. Создание функции для обработки запроса:

    Ваша функция должна проверять, установлен ли параметр sort в URL, и вызывать подготовку запроса на вывод постов.

  2. Отдельные запросы для постов:

    Необходимо сделать два запроса: один для получения постов с полем "rating" и сортировкой по значению этого поля, и другой для получения всех остальных постов.

  3. Объединение результатов:

    После выполнения обоих запросов, нужно объединить результаты в один массив и отобразить их.

Пример кода

Вот более детализированный и улучшенный код, который решает описанную задачу:

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');

Объяснение кода

  1. Проверка условий:

    • Основной запрос, архив (категории, теги и т.д.), не админка.
  2. Запрос на получение постов с рейтингом:

    • Используется WP_Query для получения постов, где поле "rating" существует, сортировка осуществляется сначала по мета-значению, а затем по дате.
  3. Запрос на получение постов без рейтинга:

    • Здесь также используется WP_Query, но с условием, что поле "rating" не существует.
  4. Объединение результатов:

    • Сначала получаем массив идентификаторов постов с рейтингом, затем добавляем к нему идентификаторы постов без рейтинга и устанавливаем их в основной запрос.
  5. Установка порядка сортировки:

    • Используется orderby для последовательного отображения постов в заданном порядке.

Заключение

Данный подход позволяет эффективно сортировать посты на страницах архива, демонстрируя посты с рейтингом сверху и посты без рейтинга ниже. Он обеспечивает гибкость и простоту в использовании и может быть расширен для других кастомных полей или дополнительных параметров сортировки.

Оцените материал
Добавить комментарий

Капча загружается...