Skip to content

Instantly share code, notes, and snippets.

@kasparsd
Created November 10, 2020 09:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kasparsd/94cfe06a546ad08c18a8d450b676ec52 to your computer and use it in GitHub Desktop.
Save kasparsd/94cfe06a546ad08c18a8d450b676ec52 to your computer and use it in GitHub Desktop.
<?php
/**
* Map all categories to the new category tree.
*
* - Create a list of new categories by their path of slugs.
*/
class Terms {
/**
* Get term objects.
*
* @param array $args Query parameters to merge with defaults.
*
* @return array
*/
public static function get_terms( $args = [] ) {
$default_args = [
'taxonomy' => 'category',
'suppress_filter' => true,
'update_term_meta_cache' => false,
'hide_empty' => false,
];
$args = wp_parse_args( $args, $default_args );
return get_terms( $args );
}
/**
* Create a list of term paths by slug from full term objects.
*
* @param array $terms List of WP term objects.
*
* @return array
*/
public static function term_paths_map( $terms ) {
$terms_by_id = [];
$child_parents = [];
$lists = [];
foreach ( $terms as $term ) {
$child_parents[ $term->term_id ] = $term->parent;
$terms_by_id[ $term->term_id ] = $term;
}
$term_paths_ids = self::map_to_paths( $child_parents );
foreach ( $term_paths_ids as $children ) {
$slugs = [];
foreach ( $children as $child_id ) {
$slugs[] = $terms_by_id[ $child_id ]->slug;
}
$lists[ $child_id ] = $slugs;
}
return self::list_to_paths( $lists );
}
/**
* Build a list of all terms and their ancestors.
*
* @param array $terms List of all terms with child -> parent mapping.
*
* @return array
*/
public static function term_ancestors( $terms ) {
$ancestors = [];
$paths = self::map_to_paths( $terms );
foreach ( $paths as $siblings ) {
// Children first, parents last.
$siblings = array_reverse( $siblings );
// Index by the last child to make this unique.
$ancestors[ $siblings[0] ] = array_slice( $siblings, 1 );
}
return $ancestors;
}
/**
* Get all children term IDs of parent term IDs.
*
* @param array $parent_ids List of term IDs.
*
* @return array
*/
public static function term_children( $parent_ids ) {
$children = [];
$paths = self::map_to_paths( self::get_terms( [
'fields' => 'id=>parent',
] ) );
foreach ( $paths as $siblings ) {
if ( in_array( $siblings[0], $parent_ids, true ) ) {
$children = array_merge( $children, $siblings );
}
}
return array_unique( $children );
}
/**
* Build a string path out of lists.
*
* @param array $lists A set of lists with children for each path.
*
* @return array
*/
public static function list_to_paths( $lists, $delimiter = '/' ) {
$paths = [];
foreach ( $lists as $id => $list ) {
// Use the last child for reference.
$paths[ $id ] = implode( $delimiter, $list );
}
return $paths;
}
/**
* Build a one dimensional tree out of id=>parent mapping.
*
* From:
* [
* 1 => 2,
* 2 => 0,
* 4 => 3,
* 3 => 0,
* 5 => 4
* ]
*
* to:
*
* [
* [2],
* [2, 1],
* [3],
* [3, 4]
* [3, 4, 5]
* ]
*
* @param array $terms A list of IDs by their parent IDs.
* @param integer|string $root Name of the root node.
*
* @return array
*/
public static function map_to_paths( $terms, $root = 0 ) {
$parents = [ $root ];
$tree = [];
$refs = [];
$paths = [];
while ( count( $terms ) ) {
$children = [];
// Get all children of the current parent.
foreach ( $terms as $key => $parent ) {
if ( isset( $parents[ $parent ] ) ) {
$children[ $key ] = $parent;
unset( $terms[ $key ] );
}
}
// Build a reference tree for the current child nodes.
foreach ( $children as $child => $parent ) {
if ( isset( $refs[ $parent ] ) ) {
$refs[ $parent ][ $child ] = [];
$refs[ $child ] = &$refs[ $parent ][ $child ];
} else {
$tree[ $parent ][ $child ] = [];
$refs[ $child ] = &$tree[ $parent ][ $child ];
}
}
// Nest deeper.
$parents = $children;
}
// Flatten the multidimensional tree into a list of paths for each node.
foreach ( $tree[0] as $parent => $children ) {
$paths = array_merge( $paths, self::flatten_nodes( $children, [ $parent ] ) );
}
return $paths;
}
/**
* Helper to flatten tree structure into a list of paths to each child.
*
* @param array $children Current list of nodes and thei parents.
* @param array $parents List of parent nodes to merge.
*
* @return array
*/
protected static function flatten_nodes( $children, $parents ) {
$tree = [
$parents,
];
foreach ( $children as $parent => $child ) {
$path = array_merge( $parents, [ $parent ] );
$tree = array_merge( $tree, self::flatten_nodes( $child, $path ) );
}
return $tree;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment