Можно ли исключить записи по 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. Спасибо за ответ.
Carlisle
Я просто создам метабокс с опциями Да и Нет, где по умолчанию будет выбрано 'Нет'. Когда я захочу выделить запись, я выберу Да. Однако я хочу, чтобы последние 5 записей оставались выделенными, а остальные отображались в основном запросе. Я не хочу каждый раз возвращаться и менять выбор, поэтому мне нужно найти способ исключить только 5 самых последних записей. Я вижу много похожих вопросов на stackexchange, и должно быть простое решение для управления такими выделенными записями. (способ, который не влияет на общую производительность, не создает много запросов или не требует сложных SQL-запросов)
Carlisle
Кстати, я не уверен, что создавать дополнительный meta_key со значением Да или Нет для всех записей — это хорошая идея. Было бы лучше исключать те записи, у которых просто отсутствует ключ featured.
Carlisle
Эта функция перестала работать на моем сайте после обновления до 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') : [];.
Kevin Nugent
Я хочу поделиться своим временным решением для избранных записей на случай, если кому-то оно пригодится. Я не использую здесь хук 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' );