WordPress.org

Make WordPress Core

Opened 2 years ago

Last modified 2 years ago

#36171 new enhancement

Proposed clean up of get_the_category_list() and link filter

Reported by: pietergoosen Owned by:
Milestone: Future Release Priority: normal
Severity: normal Version: 4.4.2
Component: Taxonomy Keywords: needs-testing needs-unit-tests
Focuses: template Cc:

Description

The current source code of the get_the_category_list() function is quite messy and there is also a lot of duplicate code which we can avoid.

Also, on a micro optimization level, in stead of passing the category ID to get_category_link(), we can pass the whole category object. Doing this will avoid the extra overhead of having to get the complete category object from the term cache.

There is also no filter to filter each category link on category level, we only have the the_category filter which filters the string of links. We have being receiving a couple of questions on wordpress.stackexchange.com regarding filtering these category links according to category. Here is such one question.

My proposal is included in my code, a filter called the_category_list_links which we can use to filter each category link individually according to the category. One such use-case of the filter can look something like

add_filter( 'the_category_list_links', function ( $the_link_list, $category, $cat_parents )
{
	$category_color = get_field( 'category_color', $category );
	if ( !$category_color )
		return $the_link_list;
	
	$the_link_list = str_replace( '<a', '<a style="color:' . $category_color . '"', $the_link_list );
	
	return $the_link_list;
}, 10, 3 );

On initial testing, I can confirm that my code is slightly faster that the current code, and the new the_category_list_links filter is working as intended

Here is the proposed cleanup

/**
 * Retrieve category list in either HTML list or custom format.
 *
 * @since 1.5.1
 *
 * @global WP_Rewrite $wp_rewrite
 *
 * @param string $separator Optional, default is empty string. Separator for between the categories.
 * @param string $parents Optional. How to display the parents.
 * @param int $post_id Optional. Post ID to retrieve categories.
 * @return string
 */
function get_the_category_list( $separator = '', $parents = '', $post_id = false ) 
{
	global $wp_rewrite;
	
	if ( ! is_object_in_taxonomy( get_post_type( $post_id ), 'category' ) ) {
		/** This filter is documented in wp-includes/category-template.php */
		return apply_filters( 'the_category', '', $separator, $parents );
	}

	/**
	 * Filter the categories before building the category list.
	 *
	 * @since 4.4.0
	 *
	 * @param array    $categories An array of the post's categories.
	 * @param int|bool $post_id    ID of the post we're retrieving categories for. When `false`, we assume the
	 *                             current post in the loop.
	 */
	$categories = apply_filters( 'the_category_list', get_the_category( $post_id ), $post_id );

	if ( empty( $categories ) ) {
		/** This filter is documented in wp-includes/category-template.php */
		return apply_filters( 'the_category', __( 'Uncategorized' ), $separator, $parents );
	}

	$rel = ( is_object( $wp_rewrite ) && $wp_rewrite->using_permalinks() ) ? 'rel="category tag"' : 'rel="category"';

	$links = array();
	foreach ( $categories as $category ) {
		/** 
		 * Break the link for better link building
		 */
		$start_link = '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>';
		$end_link   = $category->name . '</a>';
		
		/** 
		 * Build the category links
		 */
		$the_link_list = '';	
		switch ( strtolower( $parents ) ) {
			case 'multiple':
				$cat_parents   = $category->parent ? get_category_parents( $category->parent, true, $separator ) : '';
				$the_link_list = $cat_parents . ' ' . $start_link . $end_link;
				break;
			case 'single':
				$cat_parents   = $category->parent ? get_category_parents( $category->parent, false, $separator ) : '';
				$the_link_list = $start_link . $cat_parents . $end_link;
				break;
			case '':
			default:
				$the_link_list = $start_link . $end_link;
		}

		/**
		 * Filter the category links on category level.
		 *
		 * @since X.X.X
		 *
		 * @param string $the_link_list Post category link.
		 * @param object $category      The current category object
		 * @param $cat_parents          Link list of parents of the current category
		 */
		$links[] = apply_filters( 'the_category_list_links', $the_link_list, $category, $cat_parents );
	}

	$thelist = '';
	if( '' === $separator ) {
		$thelist .= '<ul class="post-categories">';
			$thelist .= "\n\t<li>";

				$thelist .= implode( "</li>\n\t<li>", $links );

			$thelist .= '</li>';
		$thelist .= '</ul>';
	} else {
		$thelist .= implode( $separator, $links );
	}	

	/**
	 * Filter the category or list of categories.
	 *
	 * @since 1.2.0
	 *
	 * @param array  $thelist   List of categories for the current post.
	 * @param string $separator Separator used between the categories.
	 * @param string $parents   How to display the category parents. Accepts 'multiple',
	 *                          'single', or empty.
	 */
	return apply_filters( 'the_category', $thelist, $separator, $parents );
}

Attachments (2)

category-template.php (44.3 KB) - added by pietergoosen 2 years ago.
36171.diff (4.2 KB) - added by boonebgorges 2 years ago.

Download all attachments as: .zip

Change History (5)

#1 @pietergoosen
2 years ago

  • Keywords needs-testing added

#2 follow-up: @boonebgorges
2 years ago

  • Component changed from Formatting to Taxonomy
  • Focuses template added; performance removed
  • Keywords needs-unit-tests added
  • Milestone changed from Awaiting Review to Future Release

Hi @pietergoosen - Thanks very much for the suggested improvements.

The changes generally look good. The DRY cleanup looks fine at a glance, and the additional filter will be quite welcome.

I've reformatted your suggestions as a proper patch. See https://make.wordpress.org/core/handbook/tutorials/trac/submitting-a-patch/ for more info on how patches are generated. 36171.diff

Unfortunately, get_the_category_list() currently has zero coverage by automated tests. Fairly decent tests are critical before we can consider this kind of refactor.

@boonebgorges
2 years ago

#3 in reply to: ↑ 2 @pietergoosen
2 years ago

Replying to boonebgorges:

Hi @pietergoosen - Thanks very much for the suggested improvements.

The changes generally look good. The DRY cleanup looks fine at a glance, and the additional filter will be quite welcome.

I've reformatted your suggestions as a proper patch. See https://make.wordpress.org/core/handbook/tutorials/trac/submitting-a-patch/ for more info on how patches are generated. 36171.diff

Unfortunately, get_the_category_list() currently has zero coverage by automated tests. Fairly decent tests are critical before we can consider this kind of refactor.

Thank you for your feedback, I really appreciate it. Thanks for the tutorial, this is my first submit, so the tutorial will help in future ;-)

I do agree that some testing is needed. I'm not particulary happy with how parent are displayed, and the fact that the new proposed filter do not directly target parents. I have kept the original way how the function handles parents for now. Ideally I would like to drop get_category_parents() from the function and let get_the_category_list() handles the parents so we can apply our filter on parent level as well.

I originally thought of also applying the new proposed filter (the_category_list_links) to the get_category_parents(), but I'm not sure how useful it will be there.

Any thoughts on this will be appreciated

Note: See TracTickets for help on using tickets.