Skip to content

Instantly share code, notes, and snippets.

@tlovett1
Last active April 20, 2018 04:24
Show Gist options
  • Save tlovett1/ec0e2f6c4efcb7555f653e7bd4d52463 to your computer and use it in GitHub Desktop.
Save tlovett1/ec0e2f6c4efcb7555f653e7bd4d52463 to your computer and use it in GitHub Desktop.
Performant utility function for building a tree of WordPress terms.
<?php
/**
* Performant utility function for building a term tree.
*
* Tree will look like this:
* [
* WP_Term(
* name
* slug
* children ->[
* WP_Term()
* ]
* ),
* WP_Term()
* ]
*
* Example usage:
* $terms = get_terms( [ 'taxonomy' => 'category' ] );
* $term_tree = wp_get_term_tree( $terms );
*
* @param array $all_terms Pass get_terms() as this argument where terms are objects NOT arrays
* @param string|bool $orderby Can be count|name|false. This is how each tree branch will be ordered
* @param string $order Can be asc|desc. This is the direction ordering will occur.
* @param bool $flat If false, a tree will be returned e.g. an array of top level terms
* which children linked within each node. If true, the tree will be
* "flattened"
* @since 2.5
* @return array
*/
function wp_get_term_tree( $all_terms, $orderby = 'count', $order = 'desc', $flat = false ) {
$terms_map = array();
$terms_tree = array();
$iteration_id = 0;
while ( true ) {
if ( empty( $all_terms ) ) {
break;
}
foreach ( $all_terms as $key => $term ) {
$iteration_id++;
if ( ! isset( $term->children ) ) {
$term->children = array();
}
if ( ! isset( $terms_map[ $term->term_id ] ) ) {
$terms_map[ $term->term_id ] = $term;
}
if ( empty( $term->parent ) ) {
$term->level = 0;
if ( empty( $orderby ) ) {
$terms_tree[] = $term;
} elseif ( 'count' === $orderby ) {
/**
* We add this weird number to get past terms with the same count
*/
$terms_tree[ ( ( $term->count * 10000000 ) + $iteration_id ) ] = $term;
} elseif ( 'name' === $orderby ) {
$terms_tree[ strtolower( $term->name ) ] = $term;
}
unset( $all_terms[ $key ] );
} else {
if ( ! empty( $terms_map[ $term->parent ] ) && isset( $terms_map[ $term->parent ]->level ) ) {
if ( empty( $orderby ) ) {
$terms_map[ $term->parent ]->children[] = $term;
} elseif ( 'count' === $orderby ) {
$terms_map[ $term->parent ]->children[ ( ( $term->count * 10000000 ) + $iteration_id ) ] = $term;
} elseif ( 'name' === $orderby ) {
$terms_map[ $term->parent ]->children[ $term->name ] = $term;
}
$parent_level = ( $terms_map[ $term->parent ]->level ) ? $terms_map[ $term->parent ]->level : 0;
$term->level = $parent_level + 1;
$term->parent_term = $terms_map[ $term->parent ];
unset( $all_terms[ $key ] );
}
}
}
}
if ( ! empty( $orderby ) ) {
if ( 'asc' === $order ) {
ksort( $terms_tree );
} else {
krsort( $terms_tree );
}
foreach ( $terms_map as $term ) {
if ( 'asc' === $order ) {
ksort( $term->children );
} else {
krsort( $term->children );
}
$term->children = array_values( $term->children );
}
$terms_tree = array_values( $terms_tree );
}
if ( $flat ) {
$flat_tree = array();
foreach ( $terms_tree as $term ) {
$flat_tree[] = $term;
$to_process = $term->children;
while ( ! empty( $to_process ) ) {
$term = array_shift( $to_process );
$flat_tree[] = $term;
if ( ! empty( $term->children ) ) {
$to_process = $term->children + $to_process;
}
}
}
return $flat_tree;
}
return $terms_tree;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment