WP_Query для пользовательской таксономии со всеми терминами?
Есть ли простой способ сделать запрос для получения всех записей, которые отмечены любым термином из определенной таксономии?
Я знаю этот метод:
$custom_taxonomy_query = new WP_Query(
array(
'taxonomy_name' => 'term_slug',
)
);
Но я хотел бы либо использовать wildcard вместо term_slug, либо просто пустую строку. Тогда это дало бы мне все записи, которые отмечены любым термином в этой таксономии, а не только одним конкретным термином.
Спасибо за помощь, Dave

Я столкнулся с похожей ситуацией, Дейв. Этот код отлично сработал для моих целей. Это не самое оптимальное решение в мире, но оно хорошо выполняет свою работу:
// Получаем все ID терминов в указанной таксономии
$taxonomy = 'taxonomy_name';
$taxonomy_terms = get_terms( $taxonomy, array(
'hide_empty' => 0,
'fields' => 'ids'
) );
// Используем новый аргумент tax_query в WP_Query (доступен с версии 3.1)
$taxonomy_query = new WP_Query( array(
'tax_query' => array(
array(
'taxonomy' => $taxonomy,
'field' => 'id',
'terms' => $taxonomy_terms,
),
),
) );
Надеюсь, это поможет тебе или кому-то еще, кто столкнулся с такой проблемой.
Кевин

Что-то вроде этого может сработать:
$args = array(
'post_type' => 'post',
'tax_query' => array(
array(
'taxonomy' => 'your_custom_taxonomy',
'operator' => 'EXISTS'
),
),
);
$query = new WP_Query( $args );
По сути, вы запрашиваете любую запись, привязанную к любому термину в вашей пользовательской таксономии.

Привет, @Dave Morris:
Ты прав, WordPress решает, что если у тебя нет термина, он просто проигнорирует твою таксономию.
Есть три (3) основных подхода, которые ты можешь попробовать:
Использовать полный SQL-запрос с
$wpdb->get_results()
,Получить список
$post->ID
для всех записей в твоей таксономии и затем передать их с помощью аргумента'post__id'
, илиДобавить SQL, используемый
WP_Query
, с помощью одного из хуков, который позволяет добавить SQLINNER JOIN
, ссылающийся на таблицы таксономий.
Я стараюсь избегать полных SQL-запросов в WordPress до тех пор, пока это действительно необходимо или если просто возвращается список ID. И в этом случае я бы избегал получения списка $post-ID
для использования с аргументом 'post__id'
, потому что это может привести к проблемам с производительностью и даже с памятью, если у тебя много записей. Так что остаётся вариант №3.
Я создал класс для расширения WP_Query
под названием PostsByTaxonomy
, который использует хук 'posts_join'
. Вот он:
class PostsByTaxonomy extends WP_Query {
var $posts_by_taxonomy;
var $taxonomy;
function __construct($args=array()) {
add_filter('posts_join',array(&$this,'posts_join'),10,2);
$this->posts_by_taxonomy = true;
$this->taxonomy = $args['taxonomy'];
unset($args['taxonomy']);
parent::query($args);
}
function posts_join($join,$query) {
if (isset($query->posts_by_taxonomy)) {
global $wpdb;
$join .=<<<SQL
INNER JOIN {$wpdb->term_relationships} ON {$wpdb->term_relationships}.object_id={$wpdb->posts}.ID
INNER JOIN {$wpdb->term_taxonomy} ON {$wpdb->term_taxonomy}.term_taxonomy_id={$wpdb->term_relationships}.term_taxonomy_id
AND {$wpdb->term_taxonomy}.taxonomy='{$this->taxonomy}'
SQL;
}
return $join;
}
}
Ты можешь вызвать этот класс, как показано ниже. Аргумент 'taxonomy'
обязателен, но ты также можешь передать любые (все?) другие параметры, которые ожидает WP_Query
, например 'posts_per_page'
:
$query = new PostsByTaxonomy(array(
'taxonomy' => 'category',
'posts_per_page' => 25,
));
foreach($query->posts as $post) {
echo " {$post->post_title}\n";
}
Ты можешь скопировать класс PostsByTaxonomy
в файл functions.php
твоей темы или использовать его в .php
файле плагина, который ты, возможно, пишешь.
Если ты хочешь быстро протестировать его, я опубликовал самодостаточную версию кода на Gist, которую ты можешь скачать, скопировать в корень веб-сервера как test.php
, изменить под свои нужды и запросить в браузере по URL типа http://example.com/test.php
.
ОБНОВЛЕНИЕ
Чтобы исключить sticky posts из записей, включаемых в запрос, попробуй так:
$query = new PostsByTaxonomy(array(
'taxonomy' => 'category',
'posts_per_page' => 25,
'caller_get_posts' => true,
));
Или, если для тебя важно, чтобы класс PostsByTaxonomy
никогда не включал sticky posts, ты можешь добавить это в конструктор:
function __construct($args=array()) {
add_filter('posts_join',array(&$this,'posts_join'),10,2);
$this->posts_by_taxonomy = true;
$this->taxonomy = $args['taxonomy'];
$args['caller_get_posts'] = true // Без Sticky Posts
unset($args['taxonomy']);
parent::query($args);
}
ОБНОВЛЕНИЕ 2
После публикации вышеизложенного я узнал, что 'caller_get_posts' будет устаревшим, и в WordPress 3.1 будет использоваться 'ignore_sticky_posts'
.

Майк, спасибо за помощь. По какой-то причине у меня не получается заставить это работать. Запрос возвращает не только записи с терминами из моей пользовательской таксономии. Всегда возвращаются и другие записи. Однако он не возвращает все записи, так что определенно что-то делает... Можно ли использовать функцию $query->have_posts() для итерации? Ни один из методов у меня не работает.

А, это интересно. Я нашел в логе mysql запрос, который получает две записи, которые я ожидаю, и он работает. Но почему-то при переборе $query->posts возвращается пять записей. Единственное, что я заметил - сразу после выполнения запроса к пользовательской таксономии выполняется еще один запрос, который выбирает три дополнительные записи по их post_id. И затем, видимо, все пять записей помещаются в один массив результатов.

Кажется, я разобрался. Похоже, этот пользовательский запрос включает sticky-записи, даже если они не принадлежат этой пользовательской таксономии. Есть идеи, как правильно учитывать sticky-записи или хотя бы исключить их из этого конкретного запроса? Спасибо, Дейв

Ну они же "прилипчивые", верно? :)
Довольно странное поведение, на мой взгляд, но если использовать caller_get_posts=1
, они должны исчезнуть:
http://codex.wordpress.org/Function_Reference/query_posts#Sticky_Post_Parameters
Надеюсь, это поможет.

Этот if(isset($query->posts_by_taxonomy))
— отличный трюк для совмещения объектно-ориентированной методологии с хуковой методологией WordPress.

@Jan Fabry - Да, спасибо! Мне потребовалось всего около 2 лет проб и ошибок, прежде чем до меня это дошло. Конечно, теперь это кажется очевидным... :)

Это сработало! Большое спасибо за помощь. Мне нравится изучать новые трюки в WordPress, особенно связанные с ООП.

@Dave Morris - Пожалуйста. Кстати, я только что узнал, что в WP 3.1 это будет называться 'ignore_sticky_posts'
.

@MikeSchinkel А что насчёт пагинации? Всё работает хорошо, но пагинация для этого запроса в моём случае не работает должным образом... http://wordpress.stackexchange.com/q/57884/12261

Строка 1432 в query.php проверяет, пусты ли таксономия ИЛИ термин, поэтому просто не передавать slug не получится... Есть другие идеи?

@t31os - Моей первой реакцией было то же самое; я уже не раз попадался на этом, потому что постоянно забываю. Но @Dave Morris прав; если это не пара таксономия/термин, то WP_Query
просто игнорирует это.

Вау, я этого не знал, это действительно немного глупо... урок усвоен... :) Я почему-то ожидал, что это будет работать как параметры meta_key / meta_value (не знаю почему)...

@t31os - Да, WP_Query
, к сожалению, реализован не так элегантно. Это почти 1200 строк жёстко закодированных частных случаев.

Оглядываясь назад, я объединил предложения MikeSchinkel и t31os. Можно динамически добавить это в существующие запросы, но для этого требуется WordPress 3.1:
Плагин для получения RSS-ленты записей, содержащих любой термин из таксономии.
