it-swarm.com.ru

WP_Query orderby настраиваемое поле, затем post_date в одном запросе

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

  • Сообщение 4 (field_order: 1)
  • Сообщение 3 (field_order: 2)
  • Сообщение 2 (field_order: 3)
  • Сообщение 1 (field_order: null)
  • Сообщение 0 (field_order: null)

Post 1 и Post 0 должны быть заказаны post_date.

Я могу сделать заказ по настраиваемому полю достаточно легко, но когда я добавляю сообщение без настраиваемого поля (сообщение 1 и 0), оно не появляется в запросе.

Ниже приведен запрос, который я сейчас пытаюсь:

    $args = array(
        'post_type' => 'insights',
        'posts_per_page' => '9',
        'meta_query' => array(
            array(
                'key' => 'field_order',
                'value'   => '0',
                'compare' => '>'
            )
        ),
        'orderby' => array(
            'field_order' => 'ASC',
            'post_date' => 'ASC',
        ),
        'order' => 'ASC'
    );
5
londonfed

Несколько аргументов meta_query с OR

Вы можете использовать аргумент 'relation' => 'OR' в meta_query с двумя наборами аргументов field_order: один с meta_value >= '0', а другой с NOT EXISTS для генерации основного запроса.

        'meta_query'     => array(
            'relation'   => 'OR',
            field_order' => array(
                'key'       => 'field_order',
                'value'     => '0',
                'compare'   => '>=',
                'type'      => 'NUMERIC'
            ),  
            'field_order_withnulls' => array(
                'key'       => 'field_order',
                'compare'   => 'NOT EXISTS',
                'type'      => 'NUMERIC'
            )
        )

Правильный порядок по

Мы можем использовать 'field_order_withnulls' => 'ASC', однако, из-за LEFT JOIN, будут строки со значениями nullfield_order, и они будут стоять перед числовыми значениями field_order в порядке ASC.

Чтобы исправить это, мы будем использовать трюк ORDER BY -field_order DESC, как описано здесь .

Это исправит порядок, преобразовав DESC в ASC, но сохранив строки со значениями null после числовых значений.

Реализация оператора - (обратный) в orderby

Проблема в том, что WordPress не предоставляет прямой способ установки оператора - (обратный) в orderby. Поэтому мы введем собственный атрибут WP_Query с именем _inverse_order, а затем используем фильтр posts_orderby для его реализации.

Образец кода:

    // posts_orderby filter callback function
    // place this function in theme's functions.php file or in a custom plugin
    function wpse311227_inverse_orderby( $orderby, $query ) {
        remove_filter( 'posts_orderby', 'wpse311227_inverse_orderby', 10, 2 );
        $idx = (int) $query->get( '_inverse_order' ) - 1;
        if( $idx >= 0 ) {
            $orders = preg_split( "/(?<=ASC|DESC),[\s]*/i", $orderby );
            if( $idx < count( $orders ) ) {
                $orders[$idx] = '-' . $orders[$idx];
            }
            return implode( $orders, ', ' );
        }

        return $orderby;
    }


    // adding the posts_orderby filter to implement the custom '_inverse_order' attribute
    // this should be placed just before the WP_Query call
    add_filter( 'posts_orderby', 'wpse311227_inverse_orderby', 10, 2 );
    $args = array(
        'post_type'      => 'insights',
        'posts_per_page' => '9',
        'meta_query'     => array(
            'relation'   => 'OR',
            field_order' => array(
                'key'       => 'field_order',
                'value'     => '0',
                'compare'   => '>=',
                'type'      => 'NUMERIC'
            ),  
            'field_order_withnulls' => array(
                'key'       => 'field_order',
                'compare'   => 'NOT EXISTS',
                'type'      => 'NUMERIC'
            )
        ),
        'orderby' => array(
            'field_order_withnulls' => 'DESC',
            'post_date'             => 'ASC'
        ),
        // this custom attribute is implemented in wpse311227_inverse_orderby() function
        // to correct the ordering by placing a '-' operator
        // value of _inverse_order attribute is the position of the
        // orderby attribute to be be inversed,
        // (position starts with 1)
        // in this case, since: 'field_order_withnulls' => 'DESC'
        // is in position 1 of 'orderby' attribute array, so:
        '_inverse_order'  => 1
    );
    $query = new WP_Query( $args );

Это создаст все сообщения с field_order > 0 и сообщения, которые не имеют метаданных field_order с ожидаемым порядком.

Примечание:Вам нужно будет передать непустую value в meta_query для проверки NOT EXISTS, если версия WordPress ниже 3.9. Проверьте это примечание от Кодекса :

Из-за ошибки # 23268, значение требуется для правильной работы сравнений NOT EXISTS до версии 3.9. Вы должны предоставить некоторую строку для параметра значения. Пустая строка или NULL НЕ будет работать. Тем не менее, любая другая строка будет работать, и НЕ будет отображаться в вашем SQL при использовании NOT EXISTS.


Warning:Этот WP_Query будет использовать два LEFT JOIN, то есть не очень эффективно. Хотя даже для нескольких тысяч постов это терпимо. Я протестировал более 15 000 сообщений, и запрос занимает в среднем около 0,3 секунды. Однако, если у вас есть миллионы или даже сотни тысяч сообщений, вам придется оптимизировать запрос или найти более эффективный метод для достижения того же результата.

5
Fayaz