Могу ли я исключить сообщение по мета-ключу, используя функцию pre_get_posts?

24

Я вижу, что многие люди предпочитают использовать pre_get_postsкрюк вместо query_posts. Код ниже работает и показывает все сообщения, которые имеют мета-ключ "признакам"

function show_featured_posts ( $query ) {
    if ( $query->is_main_query() ) {
       $query->set( 'meta_key', 'featured' );
       $query->set( 'meta_value', 'yes' );
    }
}

add_action( 'pre_get_posts', 'show_featured_posts' );

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

Carlisle
источник

Ответы:

33

Я вижу, что многие люди предпочитают использовать хук pre_get_posts вместо query_posts

Ура!

Так что pre_get_postsфильтрует WP_Queryобъект, что означает, что все, что вы можете сделать через query_posts()вас, можно сделать через $query->set()и $query->get(). В частности, мы можем использовать meta_queryатрибут (см. Кодекс ):

$meta_query = array(
                 array(
                    'key'=>'featured',
                    'value'=>'yes',
                    'compare'=>'!=',
                 ),
);
$query->set('meta_query',$meta_query);

Но .. это заменяет оригинальный «мета-запрос» (если он был). Поэтому, если вы не хотите полностью заменить исходный мета-запрос, я предлагаю:

//Get original meta query
$meta_query = $query->get('meta_query');

//Add our meta query to the original meta queries
$meta_query[] = array(
                    'key'=>'featured',
                    'value'=>'yes',
                    'compare'=>'!=',
                );
$query->set('meta_query',$meta_query);

Таким образом, мы добавляем наш мета-запрос вместе с существующими мета-запросами.

Вы можете / не можете устанавливать relationсвойство $meta_queryдля ANDили OR(для возврата сообщений, которые удовлетворяют всем или хотя бы одному мета-запросам).

* Примечание: этот тип запроса возвращает сообщения с мета-ключом «Featured», но его значение не равно yes. Он не будет включать посты, в которых метаданный ключ «Featured» не существует. Вы сможете сделать это в 3.5 .

Стивен Харрис
источник
Таким образом, нет способа проверить, существует ли meta_key для сообщения или нет / пуст или нет? Я буду ждать 3,5. тогда. Спасибо за ваш ответ.
Карлайл
Я просто создать поле мета с Yesи Noварианты и «Нет» будет выбран по умолчанию. Когда я хочу добавить пост, я выберу Yes. Тем не менее, я хочу, чтобы последние 5 постов оставались в списке, а другие отображались в основном запросе. Я не хочу возвращаться и каждый раз менять выбор, поэтому мне нужно найти способ исключить только самые последние 5 сообщений. Я вижу много похожих вопросов на stackexchange, и должен быть простой способ управлять этими публикациями. (способ, который не влияет на общую производительность, не создает много запросов и не требует смешанных SQL-запросов)
Карлайл,
Кстати, я не уверен, будет ли хорошей идеей создать дополнительный ключ meta_key Yesили Noзначение для всех сообщений. Было бы здорово исключить те посты, в которых просто нет featuredключа.
Карлайл
Эта функция только Uncaught Error: [] operator not supported for stringsчто вышла из строя на моем сайте после обновления до PHP 7, выдавая ошибку, поскольку оригинал meta_queryвозвращался как ноль. Вы можете обойти это, возвращаясь к пустому массиву, если ни один не существует, переключаясь $meta_query = $query->get('meta_query');на $meta_query = ( is_array( $query->get('meta_query') ) ) ? $query->get('meta_query') : [];.
Кевин Ньюджент
2

Я хочу опубликовать свое временное решение для избранных сообщений на случай, если некоторые люди могут использовать его. Я здесь не пользуюсь pre_get_postsкрючком, но не query_postsтоже. Проблема в том, что мне нужно поиграть с основным запросом и выполнить часть SQL-запроса. Я был бы рад, если бы кто-нибудь из экспертов смог проверить код и сообщить, все ли в порядке и не вызовет ли это проблем с производительностью. Также будет хорошо, если у кого-то будет лучший подход и поделится им с нами.

Создать запрос на популярные сообщения

<?php 

$featured_query = new WP_query( array(
    'meta_key'       =>'featured', 
    'meta_value'     =>'yes', 
    'posts_per_page' => 5, 
    'no_found_rows'  => true
    )
);

while ($featured_query->have_posts()) : 

    $featured_query->the_post(); 
    //Stuff...

endwhile; 
wp_reset_postdata(); 

?>

Создайте основной запрос, исключите посты, в которых есть метаданный ключ, ограничьте исключение 5 последними постами и покажите все остальные.

<?php 

$excludeposts = $wpdb->get_col( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'featured' AND meta_value != '' ORDER BY post_id DESC LIMIT 0, 5" );

$main_query = new WP_Query( array(
    'post__not_in' => $excludeposts, 
    'paged' => $paged 
    ) 
);  

while ($main_query->have_posts()) : 

    $main_query->the_post();
    //Stuff...

endwhile;

?>
Carlisle
источник
0

В ответ @Carlisle, если вы хотите исключить последние 5 постов, отмеченных как избранные, вы можете сделать следующее. Измените posts_per_page на то, сколько вы хотите исключить, а meta_query на то, как вы определяете избранную категорию.

function cmp_exclude_featured_posts($query) {
    $exclude = array();  //Create empty array for post ids to exclude
    if ( $query->is_main_query() ) {
            $featured = get_posts(array(
                'post_type' => 'post',
                'meta_query' => array(
                    array(
                        'key' => 'featured',
                        'value' => '1',
                        'compare' => '==',
                    ),
                ),
                'posts_per_page' => 2
            ));

            foreach($featured as $hide) {
                $exclude[] = $hide->ID;
            }   

            $query->set('post__not_in', $exclude);
        }
}

add_filter( 'pre_get_posts', 'cmp_exclude_featured_posts' );
cpeckens
источник