Как отобразить иерархический список терминов?
У меня есть иерархическая таксономия, называемая "географические местоположения". Он содержит континенты на первом уровне, а затем страны для каждого из них. Пример:
Europe
- Ireland
- Spain
- Sweden
Asia
- Laos
- Thailand
- Vietnam
И т.д.
С помощью get_terms() мне удалось вывести полный список терминов, но континенты смешиваются со странами в одном большом плоском списке.
Как я могу вывести иерархический список, подобный приведенному выше?
11 answers
Используйте wp_list_categories
с аргументом 'taxonomy' => 'taxonomy'
, он создан для создания иерархических списков категорий, но также поддерживает использование пользовательской таксономии..
Пример Кодекса:
Отображение терминов в пользовательской таксономии
Если список снова выглядит плоским, возможно, вам просто нужно немного CSS, чтобы добавить дополнения к спискам, чтобы вы могли видеть их иерархическую структуру.
Я понимаю, что это очень старый вопрос, но если у вас есть необходимость создать реальную структуру терминов, это может быть полезным методом для вас:
/**
* Recursively sort an array of taxonomy terms hierarchically. Child categories will be
* placed under a 'children' member of their parent term.
* @param Array $cats taxonomy term objects to sort
* @param Array $into result array to put them in
* @param integer $parentId the current parent ID to put them in
*/
function sort_terms_hierarchically(Array &$cats, Array &$into, $parentId = 0)
{
foreach ($cats as $i => $cat) {
if ($cat->parent == $parentId) {
$into[$cat->term_id] = $cat;
unset($cats[$i]);
}
}
foreach ($into as $topCat) {
$topCat->children = array();
sort_terms_hierarchically($cats, $topCat->children, $topCat->term_id);
}
}
Использование выглядит следующим образом:
$categories = get_terms('my_taxonomy_name', array('hide_empty' => false));
$categoryHierarchy = array();
sort_terms_hierarchically($categories, $categoryHierarchy);
var_dump($categoryHierarchy);
Я не знаю ни одной функции, которая делает то, что вы хотите, но вы можете создать что-то вроде этого:
<ul>
<?php $hiterms = get_terms("my_tax", array("orderby" => "slug", "parent" => 0)); ?>
<?php foreach($hiterms as $key => $hiterm) : ?>
<li>
<?php echo $hiterm->name; ?>
<?php $loterms = get_terms("my_tax", array("orderby" => "slug", "parent" => $hiterm->term_id)); ?>
<?php if($loterms) : ?>
<ul>
<?php foreach($loterms as $key => $loterm) : ?>
<li><?php echo $loterm->name; ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
Я не проверял это, но вы можете видеть, к чему я клоню. Приведенный выше код даст вам только два уровня
РЕДАКТИРОВАТЬ: ах да, вы можете использовать wp_list_categories(), чтобы делать то, что вам нужно.
Вы можете использовать wp_list_categories() с аргументом "таксономия".
Следующий код создаст раскрывающийся список с терминами, но также может создать любой другой элемент/структуру, отредактировав переменную $outputtemplate и отредактировав строки str_replace:
function get_terms_hierarchical($terms, $output = '', $parent_id = 0, $level = 0) {
//Out Template
$outputTemplate = '<option value="%ID%">%PADDING%%NAME%</option>';
foreach ($terms as $term) {
if ($parent_id == $term->parent) {
//Replacing the template variables
$itemOutput = str_replace('%ID%', $term->term_id, $outputTemplate);
$itemOutput = str_replace('%PADDING%', str_pad('', $level*12, ' '), $itemOutput);
$itemOutput = str_replace('%NAME%', $term->name, $itemOutput);
$output .= $itemOutput;
$output = get_terms_hierarchical($terms, $output, $term->term_id, $level + 1);
}
}
return $output;
}
$terms = get_terms('taxonomy', array('hide_empty' => false));
$output = get_terms_hierarchical($terms);
echo '<select>' . $output . '</select>';
Поскольку я искал то же самое, но для того, чтобы получить условия для одного поста, в конце концов я скомпилировал это, и это работает для меня.
Что он делает:
• он получает все термины названия таксономии для определенной должности.
• для иерархической таксономии с двумя уровнями (например, уровень 1: "страна" и уровень 2: "города") создается h4 с уровнем 1, за которым следует список ul уровня 2, и это для всех элементов уровня 1.
• если таксономия не является иерархической, она создаст только список ul всех элементов.
Вот код (я пишу его для себя, поэтому я старался быть как можно более общим, но...) :
function finishingLister($heTerm){
$myterm = $heTerm;
$terms = get_the_terms($post->ID,$myterm);
if($terms){
$count = count($terms);
echo '<h3>'.$myterm;
echo ((($count>1)&&(!endswith($myterm, 's')))?'s':"").'</h3>';
echo '<div class="'.$myterm.'Wrapper">';
foreach ($terms as $term) {
if (0 == $term->parent) $parentsItems[] = $term;
if ($term->parent) $childItems[] = $term;
};
if(is_taxonomy_hierarchical( $heTerm )){
foreach ($parentsItems as $parentsItem){
echo '<h4>'.$parentsItem->name.'</h4>';
echo '<ul>';
foreach($childItems as $childItem){
if ($childItem->parent == $parentsItem->term_id){
echo '<li>'.$childItem->name.'</li>';
};
};
echo '</ul>';
};
}else{
echo '<ul>';
foreach($parentsItems as $parentsItem){
echo '<li>'.$parentsItem->name.'</li>';
};
echo '</ul>';
};
echo '</div>';
};
};
Итак, наконец, вы вызываете функцию с помощью этого (очевидно, вы заменяете my_taxonomy на свою) : finishingLister('my_taxonomy');
Я не притворяюсь, что это идеально, но, как я уже сказал, это работает для меня.
Я использовал код @popsi, который работал очень хорошо, и я сделал его более эффективным и легким для чтения:
/**
* Recursively sort an array of taxonomy terms hierarchically. Child categories will be
* placed under a 'children' member of their parent term.
* @param Array $cats taxonomy term objects to sort
* @param integer $parentId the current parent ID to put them in
*/
function sort_terms_hierarchicaly(Array $cats, $parentId = 0)
{
$into = [];
foreach ($cats as $i => $cat) {
if ($cat->parent == $parentId) {
$cat->children = sort_terms_hierarchicaly($cats, $cat->term_id);
$into[$cat->term_id] = $cat;
}
}
return $into;
}
Использование:
$sorted_terms = sort_terms_hierarchicaly($terms);
У меня была эта проблема, и ни один из ответов здесь не сработал для меня по той или иной причине.
Вот моя обновленная и рабочая версия.
function locationSelector( $fieldName ) {
$args = array('hide_empty' => false, 'hierarchical' => true, 'parent' => 0);
$terms = get_terms("locations", $args);
$html = '';
$html .= '<select name="' . $fieldName . '"' . 'class="chosen-select ' . $fieldName . '"' . '>';
foreach ( $terms as $term ) {
$html .= '<option value="' . $term->term_id . '">' . $term->name . '</option>';
$args = array(
'hide_empty' => false,
'hierarchical' => true,
'parent' => $term->term_id
);
$childterms = get_terms("locations", $args);
foreach ( $childterms as $childterm ) {
$html .= '<option value="' . $childterm->term_id . '">' . $term->name . ' > ' . $childterm->name . '</option>';
$args = array('hide_empty' => false, 'hierarchical' => true, 'parent' => $childterm->term_id);
$granchildterms = get_terms("locations", $args);
foreach ( $granchildterms as $granchild ) {
$html .= '<option value="' . $granchild->term_id . '">' . $term->name . ' > ' . $childterm->name . ' > ' . $granchild->name . '</option>';
}
}
}
$html .= "</select>";
return $html;
}
И использование:
$selector = locationSelector('locationSelectClass');
echo $selector;
Это решение менее эффективно, чем код @popsi, поскольку оно создает новый запрос для каждого термина, но его также проще использовать в шаблоне. Если ваш сайт использует кэширование, вы можете, как и я, не возражать против небольших накладных расходов на базу данных.
Вам не нужно готовить массив, который будет рекурсивно заполнен терминами. Вы просто называете его так же, как вы бы назвали get_terms() (не устаревшая форма, содержащая только массив для аргумента). Он возвращает массив объектов WP_Term
с дополнительное свойство называется children
.
function get_terms_tree( Array $args ) {
$new_args = $args;
$new_args['parent'] = $new_args['parent'] ?? 0;
$new_args['fields'] = 'all';
// The terms for this level
$terms = get_terms( $new_args );
// The children of each term on this level
foreach( $terms as &$this_term ) {
$new_args['parent'] = $this_term->term_id;
$this_term->children = get_terms_tree( $new_args );
}
return $terms;
}
Использование простое:
$terms = get_terms_tree([ 'taxonomy' => 'my-tax' ]);
Убедитесь, что hierarchical=true
передано вашему get_terms()
звони.
Обратите внимание, что hierarchical=true
является значением по умолчанию, поэтому на самом деле просто убедитесь, что оно не было переопределено, чтобы быть false
.
Здесь у меня есть четырехуровневый выпадающий список выбора со скрытым первым пунктом
<select name="lokalizacja" id="ucz">
<option value="">Wszystkie lokalizacje</option>
<?php
$excluded_term = get_term_by('slug', 'podroze', 'my_travels_places');
$args = array(
'orderby' => 'slug',
'hierarchical' => 'true',
'exclude' => $excluded_term->term_id,
'hide_empty' => '0',
'parent' => $excluded_term->term_id,
);
$hiterms = get_terms("my_travels_places", $args);
foreach ($hiterms AS $hiterm) :
echo "<option value='".$hiterm->slug."'".($_POST['my_travels_places'] == $hiterm->slug ? ' selected="selected"' : '').">".$hiterm->name."</option>\n";
$loterms = get_terms("my_travels_places", array("orderby" => "slug", "parent" => $hiterm->term_id,'hide_empty' => '0',));
if($loterms) :
foreach($loterms as $key => $loterm) :
echo "<option value='".$loterm->slug."'".($_POST['my_travels_places'] == $loterm->slug ? ' selected="selected"' : '')."> - ".$loterm->name."</option>\n";
$lo2terms = get_terms("my_travels_places", array("orderby" => "slug", "parent" => $loterm->term_id,'hide_empty' => '0',));
if($lo2terms) :
foreach($lo2terms as $key => $lo2term) :
echo "<option value='".$lo2term->slug."'".($_POST['my_travels_places'] == $lo2term->slug ? ' selected="selected"' : '')."> - ".$lo2term->name."</option>\n";
endforeach;
endif;
endforeach;
endif;
endforeach;
?>
</select>
<label>Wybierz rodzaj miejsca</label>
<select name="rodzaj_miejsca" id="woj">
<option value="">Wszystkie rodzaje</option>
<?php
$theterms = get_terms('my_travels_places_type', 'orderby=name');
foreach ($theterms AS $term) :
echo "<option value='".$term->slug."'".($_POST['my_travels_places_type'] == $term->slug ? ' selected="selected"' : '').">".$term->name."</option>\n";
endforeach;
?>
</select>