Запрос для сортировки списка сначала по мета-полю (если существует), и показа оставшихся записей без мета-поля, отсортированных по заголовку

17 дек. 2013 г., 18:06:39
Просмотры: 54.2K
Голосов: 32

Я работаю над шаблоном страницы терминов пользовательской таксономии, где мы хотим отсортировать элементы, связанные с термином, по дате публикации (пользовательское поле даты). Если есть несколько элементов с одной и той же датой (в формате YYYY-MM-DD), то отсортировать их по заголовку, и наконец, сортировать по заголовку, если пользовательское поле не заполнено (старые элементы).

Я пробовал сотню разных способов с WP_Query, и это возвращает большинство результатов так, как я хочу - но в данном случае возвращаются только элементы, у которых есть meta_key publication_date. Все остальные элементы игнорируются и не отображаются. Я пробовал meta_query, используя relation "or" и сравнивал publication_date как EXISTS и NOT EXISTS, но это вернуло 0 результатов.

Кроме того, сайт все еще работает на версии 3.5.2, и они не хотят обновляться.

Вот мой последний запрос, который возвращает записи с пользовательским полем publication_date в правильном порядке:

$term = get_queried_object(); // находим термин таксономии страницы, на которой находимся
$wp_query = new WP_Query( array(
'post_type' => 'resource',
'tax_query' => array(
    array(
        'taxonomy' => 'resource_types',
        'field' => 'slug',
        'terms' => $term->name,
    )), 

'meta_key' => 'publication_date',
'orderby' => 'meta_value_num',
'order' => 'DESC',
'paged' => $paged,
'posts_per_page' => '10',
));

Я также пробовал использовать wpdb и выполнять SQL-запрос, но я действительно не уверен, как достичь того, чего я хочу, делая это. Если кто-то может помочь мне, это было бы здорово!

Заранее спасибо.

3
Комментарии

Удивительно, что подход с meta_query не сработал, но с другой стороны, нельзя сортировать по meta_value с meta_query без указания meta_key.

sanchothefat sanchothefat
17 дек. 2013 г. 18:54:33

Думаю, это именно та проблема, с которой я столкнулся. В итоге мне удалось заставить meta_query работать: `'meta_query' => array( 'relation' => 'OR', array( //проверяем, заполнена ли дата 'key' => 'publication_date', 'compare' => '!=', 'value' => date('Y-m-d'), ), array( //если дата не добавлена, показываем эти записи тоже 'key' => 'publication_date', 'value' => date('Y-m-d'), 'compare' => 'NOT EXISTS' )

    ),` но сортировка не работает :\
CSSgirl CSSgirl
17 дек. 2013 г. 19:01:23

Да, к сожалению, сортировка зависит от meta_key, указанного вне tax_query. Хотя мой ответ ниже может помочь.

sanchothefat sanchothefat
18 дек. 2013 г. 18:09:05
Все ответы на вопрос 5
5
25

Спасибо всем за помощь!

В итоге приведенный ниже запрос дал мне желаемый результат - отображать и сортировать записи сначала по пользовательскому полю "publication_date", сортируя по дате, а если есть несколько записей с одинаковой датой (например, 4 записи с датой июня 2013), то сортировать их по заголовку. Затем, после обработки всех записей с заполненной датой публикации, он проходит оставшиеся записи, сортируя их по алфавиту по заголовку.

Это позволяет получить нужный набор результатов в одном запросе и сохраняет пагинацию:

$term = get_queried_object();
the_post();
$wp_query = new WP_Query( array(
'post_type' => 'resource',
    'tax_query' => array(
        array(
            'taxonomy' => 'resource_types',
            'field' => 'slug',
            'terms' => $term->name,
        )),
 'meta_query' => array(
       'relation' => 'OR',
        array( //проверяем, заполнена ли дата
                'key' => 'publication_date',
                'compare' => '=',
                'value' => date('Y-m-d')
            ),
          array( //если дата не добавлена, показываем и эти записи
                'key' => 'publication_date',
                'value' => date('Y-m-d'),
                'compare' => 'NOT EXISTS'
            )
        ),
'meta_key' => 'publication_date',
'orderby' => 'meta_value title',
'order' => 'ASC',
'paged' => $paged,
'posts_per_page' => '10',
));
18 дек. 2013 г. 19:00:10
Комментарии

Круто. Никогда не думал запускать два meta_query по одному ключу!

GhostToast GhostToast
19 дек. 2013 г. 18:13:05

У меня (использую WordPress 4.1.1), если я устанавливаю meta_key, он автоматически не включает его, даже с NOT EXISTS. Очень надеюсь, что я делаю что-то не так.

Ryan Taylor Ryan Taylor
25 мар. 2015 г. 00:59:59

@RyanTaylor то же самое - meta_key не должен быть установлен в запросе для работы этого, но похоже, что сортировка по значению meta работает корректно, даже когда meta key не установлен.

jammypeach jammypeach
21 янв. 2016 г. 12:50:50

Как сказано в комментариях выше, для WP 4.1+ удалите или закомментируйте строку 'meta_key' => 'publication_date',.

MikeiLL MikeiLL
14 июл. 2016 г. 08:35:29

Буквально все, что мне нужно было сделать — это удалить meta key. Столько бесполезных ответов на stackoverflow, но спасибо вам!!

Jacob Raccuia Jacob Raccuia
11 июл. 2020 г. 07:34:55
2
18

Несколько лет спустя код, опубликованный CSSGirl, перестал работать для меня, потому что некоторые записи не имели мета-ключа или он был пустым. Вот что мне пришлось сделать, чтобы все записи сортировались по дате, а записи со значением мета-ключа отображались первыми:

$args          = array(
'post_type'   => $type,
'post_status' => 'publish',
'nopaging'    => TRUE,
'meta_query'  => array(
    'relation' => 'OR',
    array(
        'key'     => $meta_key,
        'compare' => 'NOT EXISTS',
    ),
    array(
        'relation' => 'OR',
        array(
            'key'   => $meta_key,
            'value' => 'on',
        ),
        array(
            'key'     => $meta_key,
            'value'   => 'on',
            'compare' => '!=',
        ),
    ),
),
'orderby'     => array( 'meta_value' => 'DESC', 'date' => 'DESC' ),
);
26 февр. 2016 г. 18:27:56
Комментарии

спасибо, это очень помогло.

Dragi Postolovski Dragi Postolovski
8 сент. 2021 г. 16:39:45

То же самое у меня, уже отчаялся, а теперь работает идеально, СПАСИБО!

physalis physalis
27 июн. 2023 г. 14:39:05
3

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

$found_posts = array();
while($loop->have_posts()): $loop->the_post();
    // действия в цикле
    $found_posts[] = get_the_id();
endwhile;

wp_reset_query();

$args = array(
    // другие аргументы
    'post__not_in' => $found_posts,
);

Затем запустите ваш второй цикл.

17 дек. 2013 г. 18:16:17
Комментарии

Пробую сейчас, спасибо. Сообщу, если сработает!

CSSgirl CSSgirl
17 дек. 2013 г. 19:00:47

Это сработало, но сломалось пагинация - есть идеи, как это исправить? Вот как это выглядит сейчас: echo paginate_links( array( 'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ), 'format' => '?page=%#%', 'current' => max( 1, get_query_var('paged') ), 'total' => $publication_query->max_num_pages, 'prev_text' => __('Previous |'), 'next_text' => __('| Next'), ) );

CSSgirl CSSgirl
17 дек. 2013 г. 19:11:19

Хм. На данный момент ничего не приходит в голову.

GhostToast GhostToast
17 дек. 2013 г. 19:18:11
1

Есть ли причина, по которой вы не можете обязать наличие мета-ключа publication_date для каждой записи, даже с пустым значением?

Таким образом, в вашем действии save_post вы будете добавлять/обновлять мета-ключ независимо от того, пустое ли значение в $_POST или нет.

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

add_action( 'admin_init', 'update_old_posts' );
function update_old_posts() {
    if ( ! isset( $_GET[ 'update_old_posts' ] ) )
         return;

    foreach( get_posts() as $post ) {
        if ( false === get_post_meta( $post->ID, 'publication_date', true ) ) {
             update_post_meta( $post->ID, 'publication_date', '' );
             echo "Обновлено {$post->post_title} <br />";
        }
    }

    die;
}

Запустите его, перейдя по ссылке http://example.com/wp-admin/?update_old_posts

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

add_filter( 'posts_orderby', 'multicolumn_orderby', 10, 2 );
function multicolumn_orderby( $orderby, $query ) {
    global $wpdb;

    // проверяем, что это нужный запрос
    if ( $query->get( 'meta_key' ) == 'publication_date' ) {
         $orderby = "$wpdb->postmeta.meta_value+0 DESC, $wpdb->posts.post_title ASC";
    }

    return $orderby;
}
17 дек. 2013 г. 19:25:32
Комментарии

Хм, я об этом не подумал. Попробую сделать так и посмотрю, что получится, спасибо!

CSSgirl CSSgirl
17 дек. 2013 г. 20:24:44
0

Я создал пользовательское условие WHERE. Протестировал его с помощью $wp_query->request прямо перед основным циклом, я не особо разбираюсь в SQL, но это сработало.

add_action('pre_get_posts', 'add_trending_sort', 11, 1);
function add_trending_sort($query){
  if(!$query->is_main_query())
    return;

  // Переопределяем аргументы запроса
  $query->set('meta_query', array(
    array(
      'key' => 'TRENDING',
      //'value' => 'asdfasdf',// может потребоваться значение для старых версий WordPress
      'compare' => 'NOT EXISTS',
    )
  ));
  $query->set('orderby', 'meta_value_num date');
  $query->set('order', 'DESC');
}

add_filter('posts_where', 'add_trending_where');
function add_trending_where($where = ''){
  global $wpdb, $wp_query;
  if(!$wp_query->is_main_query())// Не уверен, работает ли это. Должно быть нормально
    return $where;

  $where .= " OR ( $wpdb->postmeta.meta_key = 'TRENDING' )";

  // Не выполнять это дважды
  remove_filter('posts_where', 'add_trending_where');

  return $where;
}

Альтернативно, вы можете установить compare в 'EXISTS' и изменить строку в add_trending_where на $where .= " OR ($wpdb->postmeta.post_id IS NULL)";. Тогда вам нужно будет изменить значение ключа только в одном месте. Опять же, выведите $wp_query->request и поэкспериментируйте, если хотите лучше понять или доработать это.

РЕДАКТИРОВАНО: Я только что заметил, что это не работает, если в запросе установлен meta_key. Вы можете использовать $query->set('meta_key', NULL);, если это необходимо.

РЕДАКТИРОВАНО 2: Я заставил это работать с помощью метода выше. По какой-то причине сначала это не сработало (возможно, meta_key был установлен... я не знаю).

add_action('pre_get_posts', 'add_trending_sort', 11, 1);
function add_trending_sort($query){
  // Пропускаем, если это не основной "скрытый" запрос, в отличие от вызова 'new WP_Query()'
  if(!$query->is_main_query())
    return;

  // Устанавливаем meta_query для получения долей для orderby, а также контента без долей.
  $query->set('meta_query', array(
    'relation' => 'OR',
    array(
      'key' => 'TRENDING',
      'compare' => 'NOT EXISTS',
    ),
    array(
      'key' => 'TRENDING',
      'compare' => 'EXISTS',
    )
  ));
  //$query->set('meta_key', NULL);
  $query->set('orderby', array('meta_value_num' => 'DESC', 'date' => 'DESC'));
}
25 мар. 2015 г. 02:08:04