Включение и исключение таксономий из архивов и лент с помощью 'pre_get_posts'
Что я пытаюсь сделать?
В моем блоге используется пользовательская таксономия под названием edition
с терминами us-canada
(6), eu
(7) и india
(8) — слаг термина (ID).
Я хочу, чтобы записи не назначенные ни к одной конкретной 'edition' отображались под всеми терминами (т.е. если запись не назначена для usa, europe или india, она будет показана на архивных страницах всех этих терминов).
Что я пробовал?
Вот пример кода для одного из терминов, который должен дать представление о том, что я пытаюсь сделать, и где я мог ошибиться.
add_filter('pre_get_posts','better_editions_archive');
function better_editions_archive($query) {
// Проверяем, является ли запрос главным и относится ли к таксономии edition с ID 6
if ( $query->is_tax( 'edition', 6 ) && $query->is_main_query() ) {
$query->set( 'post_type', array( 'post' ) );
// Настраиваем параметры таксономического запроса
$query->set( 'tax_query',
array(
'relation' => 'AND',
array(
'taxonomy' => 'category',
'field' => 'id',
'terms' => array( 1, 2, 4, 5 )
),
array(
'taxonomy' => 'edition',
'field' => 'id',
'terms' => array( 7, 8 ),
'operator' => 'NOT IN'
)
)
);
}
return $query;
}
В чем проблема? Приведенный выше код не работает (другие варианты, которые я пробовал: код-1, код-2), он ничего не меняет. Ошибок отладки тоже нет.
Так что же я делаю не так?
Кроме того, чтобы изменения применялись и к лентам этих терминов, я заменил соответствующую строку в коде на эту:
function better_editions_archive($query) {
// Проверяем как для архивов, так и для лент
if ( ( $query->is_tax( 'edition', 6 ) && $query->is_main_query() ) || ( $query->is_feed() && $query->is_tax( 'edition', 6 ) ) ) {
Но это приводит к перенаправлению лент терминов обратно на их архивные страницы. То есть при работе функции example.com/edition/usa/feed/
перенаправляет на example.com/edition/usa/
.
Опять же, я не понимаю, что делаю не так.
ОБНОВЛЕНИЕ: Что сработало? (Но...)
add_filter( 'pre_get_posts', 'better_editions_archive' );
function better_editions_archive( $query ) {
// Проверяем условие для термина с ID 6
if ( $query->is_tax( 'edition', 6 ) && $query->is_main_query() ) {
$args = array(
'post_type' => 'post',
'tax_query' => array(
'relation' => 'AND',
array(
'taxonomy' => 'category',
'field' => 'id',
'terms' => array( 1, 2, 4, 5 )
),
array(
'taxonomy' => 'edition',
'field' => 'id',
'terms' => array( 7, 8 ),
'operator' => 'NOT IN'
)
)
);
// Перезаписываем query_vars
$query->query_vars = $args;
}
return $query;
}
Это работает, но есть проблема. После длительного обсуждения с опытным разработчиком WordPress мне сказали следующее (полный разговор можно посмотреть здесь, но он очень длинный):
Вы присваиваете этот массив как
query_vars
. Ноquery_vars
— это довольно большой объект. И вы фактически перезаписываете все данные в нем, добавляя только свои пользовательские параметры. Это означает, что вы "сбрасываете" все, что добавлено по умолчанию.
Он настоятельно не рекомендовал использовать это решение, а вместо этого использовать метод $query->set();
.
Но, как видно выше, у меня не получилось заставить другой вариант работать. Поэтому я здесь, чтобы узнать, что я делаю не так, в менее технических терминах.

Я попробую еще раз.
Следующий код должен модифицировать основной запрос таким образом, чтобы он включал в цикл любые записи, которые не принадлежат ни к одному термину пользовательской таксономии Edition.
add_filter('pre_get_posts','better_editions_archive');
function better_editions_archive( $query ) {
if ( $query->is_tax( 'edition' ) && $query->is_main_query() ) {
$terms = get_terms( 'edition', array( 'fields' => 'ids' ) );
$query->set( 'post_type', array( 'post' ) );
$query->set( 'tax_query', array(
'relation' => 'OR',
array(
'taxonomy' => 'edition',
'field' => 'id',
'terms' => $terms,
'operator' => 'NOT IN'
)
) );
}
return $query;
}

Только что попробовал. Ничего не изменилось. Никаких ошибок/подсказок в отладке тоже. Спасибо, что попытались помочь. :)

В качестве метода отладки, вы можете попробовать print_r($query) в вашем первом примере кода. Где-то в выводе должен быть сгенерированный SQL. Добавьте его сюда, это может быть полезно.

Да, последняя версия WordPress (всегда!). И для отладки, вы хотите, чтобы я заменил return $query;
на print_r($query);
? Это то, что нужно сделать?

Если да, вот что я получил: http://paste.kde.org/694496/ -- Это что-то говорит? (Я не понял.)

О боже, это сработало! И без каких-либо дополнительных изменений это также модифицирует фиды пользовательских таксономий так же, как и архивы терминов (что, конечно, мне и нужно). Так это работает или мне это кажется? Разве нам не нужно добавить что-то вроде ( is_feed() && is_.... )
?

Заметка для себя: ответ @vancoder — это, по сути, улучшенная (& автоматизированная) версия этого кода:
add_filter('pre_get_posts','better_editions_archive');
function better_editions_archive($query) {
if ( $query->is_tax( 'edition') && $query->is_main_query() ) {
$query->set( 'post_type', array( 'post' ) );
$query->set( 'tax_query',
array(
array(
'taxonomy' => 'edition',
'field' => 'id',
'terms' => array( 6, 7, 8 ),
'operator' => 'NOT IN'
)
)
);
}
return $query;
}
(Что делает код) Если это архив для любого элемента пользовательской таксономии 'edition
', то...
Код не мешает стандартной работе архивных страниц элементов, т.е. записи, привязанные к элементу пользовательской таксономии '
edition
', будут (как обычно) отображаться в соответствующем архиве элемента.Но если запись не привязана ни к одному из указанных элементов таксономии (см.
'terms' => array( 6, 7, 8 )
и'operator' => 'NOT IN'
), то такие записи будут отображаться на архивных страницах всех элементов этой таксономии.Код также влияет на RSS-ленты указанных элементов, поэтому содержимое лент зеркалирует содержимое архивных страниц, что мне и нужно. Если вы не хотите, чтобы изменения затрагивали ленты (т.е. хотите применить изменения только к архивным страницам), замените условие IF на следующее:
if ( $query->is_tax( 'edition') && $query->is_main_query() && ! $query->is_feed() ) {
