Можно ли исключить записи по meta key используя функцию pre_get_posts?
Я вижу, что многие предпочитают использовать хук pre_get_posts
вместо query_posts
. Приведенный ниже код работает и показывает все записи, у которых есть мета-ключ "featured"
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
', были исключены из основного запроса. Есть ли простой способ сделать это?

Я заметил, что многие предпочитают использовать хук 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);
Но... это заменяет оригинальный 'meta query' (если он был). Поэтому, если вы не хотите полностью заменять оригинальный meta query, я предлагаю:
// Получаем оригинальный meta query
$meta_query = $query->get('meta_query');
// Добавляем наш meta query к существующим meta queries
$meta_query[] = array(
'key'=>'featured',
'value'=>'yes',
'compare'=>'!=',
);
$query->set('meta_query',$meta_query);
Таким образом мы добавляем наш meta query вместе с существующими meta queries.
Вы можете (или нет) установить свойство relation
для $meta_query
в значение AND
или OR
(чтобы возвращать записи, удовлетворяющие всем или хотя бы одному из meta queries).
* Примечание: Такой запрос вернёт записи с мета-ключом 'featured', но со значением, отличным от yes
. Он не будет включать записи, где мета-ключ 'featured' отсутствует. Это станет возможным в версии 3.5.

Так что нет способа проверить, существует ли meta_key для записи или нет / пуста она или нет? Придется ждать 3.5. Спасибо за ответ.

Я просто создам метабокс с опциями Да
и Нет
, где по умолчанию будет выбрано 'Нет'. Когда я захочу выделить запись, я выберу Да
. Однако я хочу, чтобы последние 5 записей оставались выделенными, а остальные отображались в основном запросе. Я не хочу каждый раз возвращаться и менять выбор, поэтому мне нужно найти способ исключить только 5 самых последних записей. Я вижу много похожих вопросов на stackexchange, и должно быть простое решение для управления такими выделенными записями. (способ, который не влияет на общую производительность, не создает много запросов или не требует сложных SQL-запросов)

Кстати, я не уверен, что создавать дополнительный meta_key со значением Да
или Нет
для всех записей — это хорошая идея. Было бы лучше исключать те записи, у которых просто отсутствует ключ featured
.

Эта функция перестала работать на моем сайте после обновления до PHP 7, выдавая ошибку Uncaught Error: [] operator not supported for strings
, так как исходный meta_query
возвращал значение null. Это можно обойти, используя пустой массив по умолчанию, если значение не существует, заменив $meta_query = $query->get('meta_query');
на $meta_query = ( is_array( $query->get('meta_query') ) ) ? $query->get('meta_query') : [];
.

Я хочу поделиться своим временным решением для избранных записей на случай, если кому-то оно пригодится. Я не использую здесь хук 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();
// Действия с записью...
endwhile;
wp_reset_postdata();
?>
Создание основного запроса, исключающего записи с мета-ключом featured, ограничение исключения 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();
// Действия с записью...
endwhile;
?>

В ответ @Carlisle, если вы хотите исключить 5 последних записей, помеченных как избранные, вы можете сделать следующее. Измените posts_per_page на количество записей, которые хотите исключить, и meta_query в соответствии с тем, как у вас обозначается избранная категория.
function cmp_exclude_featured_posts($query) {
$exclude = array(); // Создаем пустой массив для ID записей, которые нужно исключить
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' );
