Как исключить записи подкатегорий из отображения в родительской категории WordPress
У меня такая структура категорий записей:
- новости
- новости / конференции
- новости / рассылка
В разделе Внешний вид > Меню я добавил 3 пункта меню.
По одному для каждой категории: Новости, Конференции, Рассылка.
Однако, страница Новостей также показывает записи из Конференций и Рассылки.
Как исключить записи из подкатегорий, чтобы они не отображались на странице родительской категории?
Спасибо

Я переписал код из поста на WP Engineer:
function wpse_filter_child_cats( $query ) {
if ( $query->is_category ) {
$queried_object = get_queried_object();
$child_cats = (array) get_term_children( $queried_object->term_id, 'category' );
if ( ! $query->is_admin )
//исключаем записи из дочерних категорий
$query->set( 'category__not_in', array_merge( $child_cats ) );
}
return $query;
}
add_filter( 'pre_get_posts', 'wpse_filter_child_cats' );
Попробуйте, вставив этот сниппет в ваш functions.php
. Учтите, что запись не может одновременно принадлежать и родительской, и дочерней категории, иначе она не будет отображаться.

Привет @1fixdotio. Спасибо за помощь. Я добавил это в functions.php дочерней темы, но записи подкатегорий всё равно отображаются в родительской категории.

Вы получали какие-либо сообщения об ошибках? Могу я узнать, включены ли у вас постоянные ссылки (permalink)?

@Steve Я проверил код, и он работает на моём сервере. Немного обновил код, надеюсь, это поможет.

Ошибок нет. Я загрузил обновленный код, но он всё ещё не работает. Моя тема является дочерней темой Twenty Twelve. Посмотрите эту страницу, где содержатся "2012 WA Men’s Shed Conference Presentations" и "The Warbler – WA Men’s Shed newsletter". Эти два элемента не находятся в родительской категории.

@Steve Я создал дочернюю тему для twentytwelve и вставил свой код в functions.php, всё заработало правильно. Можешь отключить плагины, например jetpack, и попробовать снова?

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

pre_get_posts
— это хук действия, а не хук фильтра. Поэтому следует использовать add_action()
без возврата значений, так как аргумент $query
передается по ссылке.

Используйте category__in
вместо cat
в вашем запросе.
Как вы уже знаете, cat
будет запрашивать категорию и её подкатегории:
$category = get_category (get_query_var('cat'));
WP_Query( 'cat'=>$category, 'post_type' => 'post' );
Однако при использовании category__in
вы не получите результаты из подкатегорий.
$category = get_category (get_query_var('cat'));
WP_Query( 'category__in'=>$category, 'post_type' => 'post' );
Документация по запросам на основе категорий доступна здесь.

Привет и добро пожаловать на WP.SE. Учтите, что get_category()
возвращает OBJECT
, ARRAY_A,
или ARRAY_N
. Прямое присваивание не сработает. Вам нужно проверить возвращаемое значение и либо присвоить значение через ассоциативный/числовой ключ, либо через свойство объекта. Убедитесь, что проверяете существование значения перед использованием.

Если вы хотите включить все записи, которые принадлежат категории, по которой в данный момент выполняется запрос, вы можете использовать следующий код. Это может потенциально включать записи, которые имеют дочерние категории от запрашиваемой категории, но только если они также имеют родительскую категорию. Записи, которые принадлежат только дочерней категории, включены не будут. Другими словами, рассмотрим следующий пример:
Категория 1
Категория 2, которая является дочерней для категории 1
Запись A, которая принадлежит только категории 1
Запись B, которая принадлежит только категории 2
Запись C, которая принадлежит и категории 1, и категории 2
Данный запрос вернёт записи A и C, но не B
function cat_query_filter($query) {
if (!is_admin() && $query->is_main_query() && is_category()) {
$queried_cat_obj = get_queried_object();
$queried_cat_id = $queried_cat_obj->term_id;
$query->set('category__in', $queried_cat_id);
}
}
add_action('pre_get_posts', 'cat_query_filter');
Ответ, предоставленный @createscape, является хорошим решением, но исключает записи, подобные записи B, что может быть или не быть тем, что вам нужно. Таким образом, в зависимости от того, что вы хотите включить в свой список результатов, вы можете использовать либо то решение, либо это. Дополнительная информация здесь: https://developer.wordpress.org/reference/classes/wp_query/#category-parameters

Это хорошо работает и предотвращает показ записей из дочерних категорий в списке родительской категории. Однако, убедитесь, что в родительской категории есть хотя бы одна запись, иначе при клике на пункт меню родительской категории пользователь получит ошибку "не найдено".

Я написал свою собственную функцию, чтобы исключить записи из подкатегорий из цикла, так как обнаруженный выше код не сработал для меня.
В моем файле archive.php темы, перед циклом, я вывожу подкатегории:
<?php
$current_cat = get_queried_object();
$args = array( 'parent'=>$current_cat->term_id, 'child_of' => $current_cat->term_id, );
$categories = get_categories( $args );
foreach($categories as $category) { ?>
<h2><?php echo $category->name ;?></h2>
<p> и т.д....</p>
<?php } ?>
В файл functions.php я добавил следующую пользовательскую функцию, используя pre_get_posts:
add_action( 'pre_get_posts', 'main_query_without_subcategory_posts' );
function main_query_without_subcategory_posts( $query ) {
if ( ! is_admin() && $query->is_main_query() ) {
// Не запрос для страницы админки.
// Это основной запрос для фронтенд-страницы вашего сайта.
if ( is_category() ) {
//Получаем текущую категорию
$current_category = get_queried_object();
//получаем ID текущей категории
$current_cat_id = $current_category->term_id;
//находим дочерние элементы текущей категории
$cat_args = array( 'parent'=>$current_category->term_id, 'child_of' => $current_category->term_id, );
$subcategories = get_categories( $cat_args );
//Получаем список ID подкатегорий, добавляем минус перед каждым
$subcat_id = array();
foreach($subcategories as $subcategory) {
$subcat_id[] = " -". $subcategory->term_id;
}
//Объединяем их в строку с разделителем-запятой
$excludesubcatlist = join(',', $subcat_id);
//Если у вас несколько параметров, используйте $query->set несколько раз
$query->set( 'posts_per_page', '10' );
$query->set( 'cat', ''.$current_cat_id.','.$excludesubcatlist.'' );
}
}
}
Затем в archive.php, после подкатегорий, я добавил стандартный цикл WordPress, который теперь модифицируется вышеуказанной функцией:
<?php while (have_posts() ) : the_post(); ?>
<h2><?php the_title();?></h2>
<p> и т.д....</p>
<?php endwhile;?>
Хотя в кодексе WordPress указано, что использование "category__in" исключает записи из подкатегорий, у меня это не сработало, и записи из подкатегорий все равно отображались.
https://codex.wordpress.org/Class_Reference/WP_Query#Category_Parameters
https://developer.wordpress.org/reference/hooks/pre_get_posts/

Хорошо, начиная с 2019 года мне пришлось использовать слегка модифицированный код, который работает с любой таксономией. Не только со стандартными категориями. Я использовал его в своей настройке WooCommerce и WordPress, чтобы предотвратить отображение товаров из дочерних категорий в родительской категории:
add_action('parse_tax_query', 'wpse_filter_child_cats');
function wpse_filter_child_cats( $query ) {
if ( is_tax( 'product_cat' ) ) {
$queried_object = get_queried_object();
$child_cats = (array) get_term_children( $queried_object->term_id, 'product_cat' );
if ( ! $query->is_admin ) //исключаем записи в дочерних категориях
{
$query->tax_query->queries[] = [
'taxonomy' => 'product_cat',
'field' => 'term_id',
'terms' => array_merge( $child_cats ),
'operator' => 'NOT IN'
];
}
}
}
