Created
November 10, 2020 09:55
-
-
Save kasparsd/94cfe06a546ad08c18a8d450b676ec52 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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