diff --git a/src/wp-includes/category-template.php b/src/wp-includes/category-template.php index 157b785eea..4e2f6823af 100644 --- a/src/wp-includes/category-template.php +++ b/src/wp-includes/category-template.php @@ -32,35 +32,30 @@ function get_category_link( $category ) { * Retrieve category parents with separator. * * @since 1.2.0 + * @since 4.8.0 The `$visited` parameter was deprecated and renamed to `$deprecated`. * * @param int $id Category ID. * @param bool $link Optional, default is false. Whether to format with link. * @param string $separator Optional, default is '/'. How to separate categories. * @param bool $nicename Optional, default is false. Whether to use nice name for display. - * @param array $visited Optional. Already linked to categories to prevent duplicates. + * @param array $deprecated Not used. * @return string|WP_Error A list of category parents on success, WP_Error on failure. */ -function get_category_parents( $id, $link = false, $separator = '/', $nicename = false, $visited = array() ) { - $chain = ''; - $parent = get_term( $id, 'category' ); - if ( is_wp_error( $parent ) ) - return $parent; +function get_category_parents( $id, $link = false, $separator = '/', $nicename = false, $deprecated = array() ) { - if ( $nicename ) - $name = $parent->slug; - else - $name = $parent->name; - - if ( $parent->parent && ( $parent->parent != $parent->term_id ) && !in_array( $parent->parent, $visited ) ) { - $visited[] = $parent->parent; - $chain .= get_category_parents( $parent->parent, $link, $separator, $nicename, $visited ); + if ( ! empty( $deprecated ) ) { + _deprecated_argument( __FUNCTION__, '4.8.0' ); } - if ( $link ) - $chain .= ''.$name.'' . $separator; - else - $chain .= $name.$separator; - return $chain; + $format = $nicename ? 'slug' : 'name'; + + $args = array( + 'separator' => $separator, + 'link' => $link, + 'format' => $format, + ); + + return get_term_parents_list( $id, 'category', $args ); } /** @@ -1232,6 +1227,71 @@ function get_the_term_list( $id, $taxonomy, $before = '', $sep = '', $after = '' return $before . join( $sep, $term_links ) . $after; } +/** + * Retrieve term parents with separator. + * + * @since 4.8.0 + * + * @param int $term_id Term ID. + * @param string $taxonomy Taxonomy name. + * @param string|array $args { + * Array of optional arguments. + * + * @type string $format Use term names or slugs for display. Accepts 'name' or 'slug'. + * Default 'name'. + * @type string $separator Separator for between the terms. Default '/'. + * @type bool $link Whether to format as a link. Default true. + * @type bool $inclusive Include the term to get the parents for. Default true. + * } + * @return string|WP_Error A list of term parents on success, WP_Error or empty string on failure. + */ +function get_term_parents_list( $term_id, $taxonomy, $args = array() ) { + $list = ''; + $term = get_term( $term_id, $taxonomy ); + + if ( is_wp_error( $term ) ) { + return $term; + } + + if ( ! $term ) { + return $list; + } + + $term_id = $term->term_id; + + $defaults = array( + 'format' => 'name', + 'separator' => '/', + 'link' => true, + 'inclusive' => true, + ); + + $args = wp_parse_args( $args, $defaults ); + + foreach ( array( 'link', 'inclusive' ) as $bool ) { + $args[ $bool ] = wp_validate_boolean( $args[ $bool ] ); + } + + $parents = get_ancestors( $term_id, $taxonomy, 'taxonomy' ); + + if ( $args['inclusive'] ) { + array_unshift( $parents, $term_id ); + } + + foreach ( array_reverse( $parents ) as $term_id ) { + $parent = get_term( $term_id, $taxonomy ); + $name = ( 'slug' === $args['format'] ) ? $parent->slug : $parent->name; + + if ( $args['link'] ) { + $list .= '' . $name . '' . $args['separator']; + } else { + $list .= $name . $args['separator']; + } + } + + return $list; +} + /** * Display the terms in a list. * diff --git a/tests/phpunit/tests/category/getCategoryParents.php b/tests/phpunit/tests/category/getCategoryParents.php index 35253921f7..da6b2fa227 100644 --- a/tests/phpunit/tests/category/getCategoryParents.php +++ b/tests/phpunit/tests/category/getCategoryParents.php @@ -50,16 +50,14 @@ class Tests_Category_GetCategoryParents extends WP_UnitTestCase { $this->assertSame( $expected, $found ); } - public function test_visited_should_also_exclude_children_of_visited_categories() { - $c3 = self::factory()->category->create_and_get( array( - 'parent' => $this->c2->term_id, - ) ); - $c4 = self::factory()->category->create_and_get( array( - 'parent' => $c3->term_id, - ) ); + public function test_deprecated_argument_visited() { + $this->setExpectedDeprecated( 'get_category_parents' ); + $found = get_category_parents( $this->c2->term_id, false, '/', false, array( $this->c1->term_id ) ); + } - $expected = $this->c1->name . '/'. $this->c2->name . '/'; - $found = get_category_parents( $this->c2->term_id, false, '/', false, array( $c3->term_id ) ); + public function test_category_without_parents() { + $expected = $this->c1->name . '/'; + $found = get_category_parents( $this->c1->term_id ); $this->assertSame( $expected, $found ); } } diff --git a/tests/phpunit/tests/term/getTermParentsList.php b/tests/phpunit/tests/term/getTermParentsList.php new file mode 100644 index 0000000000..db517175b0 --- /dev/null +++ b/tests/phpunit/tests/term/getTermParentsList.php @@ -0,0 +1,113 @@ + true ) ); + + self::$c1 = $factory->term->create_and_get( array( 'taxonomy' => 'wptests_tax' ) ); + self::$c2 = $factory->term->create_and_get( array( + 'taxonomy' => 'wptests_tax', + 'parent' => self::$c1->term_id, + ) ); + } + + public static function wpTearDownAfterClass() { + wp_delete_term( self::$c1->term_id, 'wptests_tax' ); + wp_delete_term( self::$c2->term_id, 'wptests_tax' ); + } + + public function setUp() { + parent::setUp(); + register_taxonomy( 'wptests_tax', 'post', array( 'hierarchical' => true ) ); + } + + public function test_should_return_wp_error_for_empty_id() { + $this->assertWPError( get_term_parents_list( '', 'wptests_tax' ) ); + } + + public function test_should_return_empty_for_invalid_id() { + $this->assertEquals( '', get_term_parents_list( 99999999, 'wptests_tax' ) ); + } + + public function test_should_return_wp_error_for_invalid_taxonomy() { + $this->assertWPError( get_term_parents_list( self::$c2->term_id, 'foo' ) ); + } + + public function test_with_default_parameters() { + $expected = '' . self::$c1->name . '/'. self::$c2->name . '/'; + $found = get_term_parents_list( self::$c2->term_id, 'wptests_tax' ); + $this->assertSame( $expected, $found ); + } + + public function test_array_parameters() { + $args = array( + 'separator' => ' --- ', + 'link' => false, + 'format' => 'slug', + 'inclusive' => false, + ); + + $expected = self::$c1->slug . ' --- '; + $found = get_term_parents_list( self::$c2->term_id, 'wptests_tax', $args ); + $this->assertSame( $expected, $found ); + } + + public function test_link_false() { + $expected = self::$c1->name . '/' . self::$c2->name . '/'; + $found = get_term_parents_list( self::$c2->term_id, 'wptests_tax', 'link=false' ); + $this->assertSame( $expected, $found ); + } + + public function test_separator() { + $expected = self::$c1->name . ' --- ' . self::$c2->name . ' --- '; + $found = get_term_parents_list( self::$c2->term_id, 'wptests_tax', 'link=false&separator= --- ' ); + $this->assertSame( $expected, $found ); + } + + public function test_format_name() { + $expected = self::$c1->name . '/'. self::$c2->name . '/'; + $found = get_term_parents_list( self::$c2->term_id, 'wptests_tax', 'link=false&format=name' ); + $this->assertSame( $expected, $found ); + } + + public function test_format_slug() { + $expected = self::$c1->slug . '/'. self::$c2->slug . '/'; + $found = get_term_parents_list( self::$c2->term_id, 'wptests_tax', 'link=false&format=slug' ); + $this->assertSame( $expected, $found ); + } + + public function test_inclusive_false() { + $expected = '' . self::$c1->name . '/'; + $found = get_term_parents_list( self::$c2->term_id, 'wptests_tax', 'inclusive=false' ); + $this->assertSame( $expected, $found ); + } + + public function test_term_without_parents() { + $expected = '' . self::$c1->name . '/'; + $found = get_term_parents_list( self::$c1->term_id, 'wptests_tax' ); + $this->assertSame( $expected, $found ); + } + + public function test_order_should_go_from_distant_to_nearest_ancestor() { + $c3 = self::factory()->term->create_and_get( array( + 'taxonomy' => 'wptests_tax', + 'parent' => self::$c2->term_id, + ) ); + + $expected = self::$c1->name . '/' . self::$c2->name . '/' . $c3->name . '/'; + $found = get_term_parents_list( $c3->term_id, 'wptests_tax', array( 'link' => false ) ); + $this->assertSame( $expected, $found ); + } + + public function test_should_accept_term_object() { + $expected = self::$c1->name . '/' . self::$c2->name . '/'; + $found = get_term_parents_list( self::$c2, 'wptests_tax', array( 'link' => false ) ); + $this->assertSame( $expected, $found ); + } +}