Фильтрация get_categories() для термина таксономии в WordPress
Я хочу показывать категорию только если кастомный пост находится в этой категории И регион = $name для этого поста.
Например, у меня есть кастомный пост (тип "business") с названием "Mamma Mia" в дочерней категории: "pizzerias" (и в родительской категории "food"), и в регионе "Rotterdam" (кастомная таксономия: "region", термин таксономии: "rotterdam").
=> должна отображаться категория "pizzerias" (и родительская категория "food")
Но я не понимаю, как этого добиться. Буду благодарен за помощь с этой задачей.
<?php
// $filter = array('region'=>$name);
$categories = get_categories();
foreach ($categories as $cat)
{
if($cat->parent < 1)
{
$cat_name = $cat->cat_name;
$catid = get_cat_ID( $cat_name );
echo $cat_name. '<br/>';
$args=array(
'orderby' => 'name',
'order' => 'ASC',
'child_of' => $catid
);
$categories=get_categories($args);
foreach($categories as $category) {
echo '<a href="' . get_category_link( $category->term_id ) . '" title="' . sprintf( __( "Просмотреть все записи в %s" ), $category->name ) . '" ' . '>' . $category->name.'</a><br/>';
}
}
}
// print_r($categories);
?>
цель - показать список категорий, но только те категории, которые содержат записи с установленной пользовательской таксономией region для Rotterdam
Это реализовано в плагине Query Multiple Taxonomies:
http://plugins.trac.wordpress.org/browser/query-multiple-taxonomies/trunk/core.php?rev=308185#L10

И более конкретно, в его виджете детализированной навигации. Отлично! Теперь это нужно расширить, чтобы показывать также родительские категории: если пост прикреплен к Пиццериям
, я также вижу его при просмотре Еды
, но Еда
не отображается в виджете, если пост не прикреплен к ней явно.

Основная идея - сделать запрос для определенного набора записей, собрать все их категории, отфильтровать и привести в нужный вид с помощью функции.
Общий код:
$posts = get_posts(array(
'tag' => 'linux'
));
$categories = array();
foreach( $posts as $post ) {
$cats = get_the_category($post->ID);
foreach($cats as $c)
$categories[] = $c->term_id;
}
$categories = array_unique($categories);
sort($categories);
$categories = implode(',', $categories);
$result = get_categories(array(
'include' => $categories
));
Проблемы в том, что, насколько я помню, нормальные запросы для нескольких таксономий появятся только в WordPress 3.1, и этот подход может быть очень ресурсоемким, поэтому потребуется кэширование (вероятно, для каждого региона
).

Как параметр include
влияет на иерархические результаты? Если есть записи в Пиццерии
, но ни одной напрямую прикрепленной к родительской категории Еда
, будет ли отображаться Еда
? Или записи автоматически прикрепляются и к родительским категориям?

@Jan Fabry проверил, если передать только ID дочерней категории в get_categories()
, то родительская категория не извлекается. :( Так что придётся дополнительно пройтись по категориям и получить родителей. Ещё большая путаница.

В версии WordPress, которую я использую (3.1.2), если добавить 'taxonomy' => 'taxonomy_term' в массив аргументов, это должно сработать. Вот модифицированная версия вашего исходного кода с включением таксономии в массив. Правда, я не знаю название таксономии, которую вы пытаетесь использовать:
<?php
// $filter = array('region'=>$name);
$categories = get_categories();
foreach ($categories as $cat)
{
if($cat->parent < 1)
{
$cat_name = $cat->cat_name;
$catid = get_cat_ID( $cat_name );
echo $cat_name. '<br/>';
$args=array(
'taxonomy' => 'taxonomy_term',
'orderby' => 'name',
'order' => 'ASC',
'child_of' => $catid
);
$categories=get_categories($args);
foreach($categories as $category) {
echo '<a href="' . get_category_link( $category->term_id ) . '" title="' . sprintf( __( "Просмотреть все записи в %s" ), $category->name ) . '" ' . '>' . $category->name.'</a><br/>';
}
}
}
// print_r($categories);
?>

Я адаптировал ответ @rarst из вышеприведённого примера для работы с 3 пользовательскими таксономиями. Мне нужно выводить только те термины, которые прикреплены к одному или нескольким записям в цикле.
Вот моя функция, которую я добавил в functions.php:
function dv_setup_sidebar_cats() {
global $wp_query;
$designers = array();
$sizes = array();
$colors = array();
foreach( $wp_query->posts as $post ) {
$des = get_the_terms($post->ID, 'designer');
$siz = get_the_terms($post->ID, 'size');
$col = get_the_terms($post->ID, 'color');
foreach($des as $d) {
$designers[] = $d->term_id;
}
foreach($siz as $s) {
$sizes[] = $s->term_id;
}
foreach($col as $c) {
$colors[] = $c->term_id;
}
}
if ( !empty($designers) ) {
$designers = array_unique($designers);
sort($designers);
$designers = implode(',', $designers);
$dresult = get_categories(array(
'include' => $designers,
'taxonomy' => 'designer'
));
}
if ( !empty($sizes) ) {
$sizes = array_unique($sizes);
sort($sizes);
$sizes = implode(',', $sizes);
$sresult = get_categories(array(
'include' => $sizes,
'taxonomy' => 'size'
));
}
if ( !empty($colors) ) {
$colors = array_unique($colors);
sort($colors);
$colors = implode(',', $colors);
$cresult = get_categories(array(
'include' => $colors,
'taxonomy' => 'color'
));
}
$return = array(
'size' => $sresult,
'color' => $cresult,
'designer' => $dresult
);
return $return;
}
Надеюсь, это поможет кому-то ещё.
-J

Прошу прощения, если я что-то упустил, но разве не будет так просто:
if (is_object_in_term($post->ID, 'region', 'rotterdam'))
the_category(); // отображает категории, связанные с текущей записью
В админке вы прикрепляете запись и к 'pizzerias', и к 'food' (отмечая оба чекбокса), или только к 'pizzerias'?
Если последнее, то the_category()
по умолчанию не покажет 'Food', поэтому вам придется получать иерархию категорий самостоятельно.

Нет, цель — показать список категорий, но только тех категорий, у которых есть записи, где пользовательская таксономия region
установлена в значение Rotterdam
. Кстати, этот вопрос также задавался на SO.

Если я правильно понимаю, то это кажется подходящим ответом. Если Mamma Mia
— это pizzeria
в rotterdam
, то по определению там есть пиццерии в Роттердаме, и категория должна отображаться. Иначе я не понимаю, как категория должна быть связана с регионом.

@Rarst: Как я понял вопрос, список категорий должен отображаться не только на странице записи, но, например, на region/rotterdam/
: для всех записей, использующих этот термин пользовательской таксономии, нужно получить одно дерево всех категорий, к которым они привязаны. Автор использует get_categories()
, который не ограничен категориями текущей записи.

Вы можете получить таксономию для пользовательского типа записи с помощью небольшой функции:
function get_the_taxonomy( $taxonomy, $id = FALSE ) {
global $post;
$id = (int) $id;
if ( !$id )
$id = (int) $post->ID;
$categories = get_object_term_cache( $id, $taxonomy );
if ( FALSE === $categories ) {
$categories = wp_get_object_terms( $id, $taxonomy );
wp_cache_add($id, $categories, $taxonomy . '_relationships');
}
if ( !empty( $categories ) )
usort( $categories, '_usort_terms_by_name' );
else
$categories = array();
foreach ( (array) array_keys( $categories ) as $key ) {
_make_cat_compat( $categories[$key] );
}
return $categories;
}
Также вы можете использовать встроенную функцию WordPress:
$taxonomys = get_the_term_list($id, YOUR_CUSTOM_POST_TYPE, '', ', ', '' );
Или проверить таксономию с помощью is_tax()
, см. кодекс для этого условного тега
Возможно, это поможет, если я правильно понял вашу проблему. Извините за мой плохой английский.

Я думаю, Henk Jan хочет получить список категорий, независимо от текущей записи. Просто все категории, у которых есть записи, связанные с пользовательским термином rotterdam
.

Спасибо за подсказку; я думаю, можно использовать пользовательский запрос с пользовательским термином. Запись возвращает все термины, и запрос должен использовать все или первый термин, чтобы в цикле вывести все записи для этого термина. Также возможно вернуть все категории для записей в этом запросе.

Спасибо всем за помощь — это действительно здорово. Хочу поделиться с вами тем, что у меня получилось. Я знаю, что это не самое умное решение. Недостаток в том, что родительская категория отображается для региона, когда к ней не прикреплены пользовательские записи для этого региона (но есть пользовательские записи для другого региона).
Я использую это на странице региона (архив таксономии).
<?php
$categories = get_categories();
foreach ($categories as $cat)
{
if($cat->parent < 1)
{
$cat_name = $cat->cat_name;
$catid = get_cat_ID( $cat_name );
echo '<div class="indexcolumn-top">';
$img = $taxonomy_images_plugin->get_image_html( 'fullsize', $cat->term_taxonomy_id );
if( !empty( $img ) )
print $img;
else
echo '<h2>' .$cat_name. '</h2>';
$input = array();
$args=array(
'child_of' => $catid,
'orderby' => 'name',
'order' => 'ASC'
);
$categories = get_categories($args);
foreach ($categories as $cat)
{
$cat_name = $cat->cat_name;
$catid = get_cat_ID( $cat_name );
$args = array(
'post_status' => 'publish',
'taxonomy_name' => 'region',
'taxonomy_term' => $name,
);
$custom_posts = get_posts_by_taxonomy($args);
if ($custom_posts):
foreach ($custom_posts as $post):
setup_postdata($post);
$postcategory = get_the_category(); $postcat = $postcategory[0]->cat_name;
if ($postcat == $cat_name):
$category = get_the_category();
$input[] = $catid;
endif;
endforeach;
endif;
}
echo '<br/>';
$output = array_unique($input);
if (empty($output)):
echo '<p>Нет компаний</p>';
echo '</div>';
else :
echo '<ul class="port-box">';
foreach ($output as $output) {
$cat_name = get_cat_name($output);
echo '<li><a href="' . get_category_link($output) . '" title="' . sprintf( __( "Просмотреть все записи в %s" ), $cat_name ) . '" ' . '>' . $cat_name .'</a></li>';
}
echo '</ul></div>';
endif;
}
}
?>

Я не считаю это эффективным способом: вы выполняете множество запросов, и мне не понятен внутренний цикл по записям: если запись найдена в текущей категории, вы, вероятно, можете просто добавить эту категорию в список без перебора всех записей?

@Jan: Я знаю, что это не самое умное решение. Однако мне не понятно ваше замечание насчёт внутреннего цикла по записям. Вам нужен внутренний цикл, чтобы найти совпадение — или нет. Или можно прервать цикл при нахождении совпадения? Но как тогда это сделать?

Неважно, я думал, что ваша функция get_posts_by_taxonomy()
выбирает записи и по таксономии, и по категории. Тогда вопрос: зачем повторять этот запрос снова и снова, вместо того чтобы выполнить его один раз и повторно использовать результаты? И почему проверяете только первую категорию? Если бизнес находится и в Пиццерии
, и в Картошке фри
, вернётся только одна из них, так что в итоге можно упустить одну.

@Jan: OK. Я проверяю только первую категорию, потому что мои бизнесы могут быть только в одной категории. Я не понимаю твой первый вопрос. Конструкцию if ($custom_posts): endif; можно убрать. Но тебе нужно будет сделать цикл, чтобы найти совпадение - или нет. Что ты имеешь в виду под повторным использованием?

<?php $cat_id = $_GET["cat"];?>
<li id="category-active"><a><?php if ($cat_id){echo get_the_category_by_id($li_id);}else{ echo "Выбранная категория";}?></a></li> <?php $subcategories1 = wp_list_categories("title_li&child_of=$cat_id&hide_empty");
wp_list_categories("title_li=¤t_category="); ?>
</ul><?php ?>
Спасибо за вашу помощь.
Но я думаю, что здесь не нужен foreach или любой другой цикл, мы можем получить дочерние элементы любой категории, используя этот простой код. Сначала $li_id
получает ID текущей категории, затем мы динамически передаем этот ID в child_of=$cat_id
, и это показывает дочерние элементы выбранной категории. Я использую это в боковой панели своего сайта.
