Получение нескольких соседних постов за один запрос: решение в WordPress

# Получение нескольких соседних постов в одном запросе

В WordPress есть функции для получения соседних постов, такие как get_previous_post() и get_next_post(). Но они возвращают только один пост. А что делать, если вам нужно получить несколько соседних постов, которые расположены рядом с текущим?

Представим, что у нас есть посты, расположенные следующим образом:

1
2
3
4 - текущий пост
5
6
7


Эту задачу можно решить с помощью двух запросов, используя WP_Query, но мы сделаем это с помощью одного запроса!

## Функция для получения соседних постов

В следующем коде мы создадим функцию, которая возвращает соседние посты по дате:


/**
 * Получает соседние посты (по дате).
 *
 * @param array $args {
 *     Массива аргументов.
 *
 *     @type int         $limit        Количество соседних постов, которые нужно получить.
 *     @type bool        $in_same_term Получать посты только из тех же терминов, где находится текущий пост.
 *     @type string      $taxonomy     Название термина. Когда $in_same_term = true, WordPress нужно знать, с каким термином работать.
 *     @type int/WP_Post $post         Пост, соседние посты которого нужно получить. По умолчанию: текущий пост.
 *     @type string      $order        Порядок сортировки. Если 'DESC' - 'prev' будет содержать новые посты, а 'next' - старые. Если 'ASC' - наоборот.
 *     @type bool        $cache_result Нужно кэшировать результат?
 * }
 *
 * @return array Массив вида: array( 'prev'=>array(posts), 'next'=>array(posts) ) или array(),
 *               если запись не найдена или произошла ошибка в запросе.
 */
function get_post_adjacents( $args = array() ) {
    global $wpdb;

    // Объединяем переданные аргументы с значениями по умолчанию
    $args = (object) array_merge( array(
        'limit'        => 3,
        'in_same_term' => false,
        'taxonomy'     => 'category',
        'post'         => $GLOBALS['post'],
        'order'        => 'DESC',
        'cache_result' => false,
    ), $args );

    $post = is_numeric($args->post) ? get_post($args->post) : $args->post;

    // Если нужно ограничить посты по терминам
    $join = $where = '';
    if ( $args->in_same_term ) {
        $join .= " INNER JOIN $wpdb->term_relationships AS tr ON p.ID = tr.object_id INNER JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id";
        $where .= $wpdb->prepare( "AND tt.taxonomy = %s", $args->taxonomy );

        if ( ! is_object_in_taxonomy( $post->post_type, $args->taxonomy ) )
            return array();

        $term_array = wp_get_object_terms( $post->ID, $args->taxonomy, ['fields'=>'ids'] );

        if ( ! $term_array || is_wp_error( $term_array ) )
            return array();

        $term_array = array_map( 'intval', $term_array );

        $where .= " AND tt.term_id IN (" . implode( ',', $term_array ) . ")";
    }

    // Запрос для получения соседних постов
    $query = "
    (
        SELECT p.* FROM $wpdb->posts p $join
        WHERE
            p.post_date   > '". esc_sql($post->post_date). "' AND
            p.post_type   = '". esc_sql($post->post_type). "' AND
            p.post_status = 'publish' $where
        ORDER BY p.post_date ASC
        LIMIT ". intval($args->limit) ."
    )
    UNION
    ( SELECT * FROM $wpdb->posts WHERE ID = $post->ID )
    UNION
    (
        SELECT p.* FROM $wpdb->posts p $join
        WHERE
            p.post_date   < '". esc_sql($post->post_date) ."' AND
            p.post_type   = '". esc_sql($post->post_type) ."' AND
            p.post_status = 'publish' $where
        ORDER BY p.post_date DESC
        LIMIT ". intval($args->limit) ."
    )
    ORDER by post_date " . ( $args->order === 'DESC' ? 'DESC' : 'ASC' ) . "
    ";

    // Попытка получить кэш
    if( $args->cache_result ){
        $query_key = 'post_adjacents_' . md5( $query );
        $result = wp_cache_get( $query_key, 'counts' );
        if( false === $result )
            $result = $wpdb->get_results( $query, OBJECT_K );

        // Кэш результата
        if( ! $result )
            $result = array();
        wp_cache_set( $query_key, $result, 'counts' );
    }
    else
        $result = $wpdb->get_results( $query, OBJECT_K );

    // Сбор массивов для предыдущих и следующих постов
    if( $result ){
        $adjacents = array( 'prev'=>[], 'next'=>[] );
        $indx = 'prev';
        foreach( $result as $pst ){
            if( $pst->ID == $post->ID ){
                $indx = 'next';
                continue;
            }
            $adjacents[ $indx ][ $pst->ID ] = get_post( $pst ); // Создаем объекты WP_Post
        }
    }
    return $adjacents;
}


## Пример использования функции

После того, как мы создали функцию, давайте посмотрим, как её использовать:


$adjacents = get_post_adjacents([
    'post' => 10253 // ID текущего поста
]);

print_r( $adjacents );


### Результат

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

Array(
[prev] => Array
(
[10415] => WP_Post Object
[ID] => 10415
[post_author] => 141
[post_date] => 2018-07-14 17:23:56
[post_title] => Заголовок поста 1
...
[10339] => WP_Post Object
[ID] => 10339
[post_author] => 141
[post_date] => 2018-06-19 00:40:03
[post_title] => Заголовок поста 2
...
)

[next] => Array
    (
        [10040] => WP_Post Object
            [ID] => 10040
            [post_author] => 1
            [post_date] => 2018-05-21 21:35:48
            [post_title] => Заголовок поста 5
            ...
    )

)


В данном примере массив adjacents содержит два ключа: prev (предыдущие посты) и next (следующие посты), каждый из которых содержит массив постов, полученных на основе даты.

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *