WordPress.org

Make WordPress Core

Opened 5 years ago

Last modified 7 months ago

#9547 reopened enhancement

Taxonomy - interesting 'unused' term_order column in table term_relationships.

Reported by: michelwppi Owned by: ryan
Milestone: Future Release Priority: high
Severity: normal Version: 2.8
Component: Taxonomy Keywords: has-patch needs-unit-tests
Focuses: Cc:

Description

During development of plugin xili-language, and to sort term by term list of languages in a taxonomy, I discover unused column term_order in term_relationships table and lack of functions in core about this column. Like medias in post, here the user can define languages list with first, second, third,... languages for his website (and xml header). Taxonomy tools are here very powerful without adding tables or annoying coding.

(see code here line 1309-1370).

Before to complete these very basic functions,…

Is it forecast to have more basic / generic functions using term_order in taxonomy.php ?

Related ticket

Attachments (7)

#9547-sw1.diff (2.9 KB) - added by simonwheatley 21 months ago.
Add ordering to term template tags
ordered-post-tags.php (2.2 KB) - added by simonwheatley 21 months ago.
Non-production plugin to turn post_tag into a sorted taxonomy
#9547-sw2.diff (2.9 KB) - added by simonwheatley 21 months ago.
Add ordering to term template tags through $args array type param (removed error_log call)
#9547-sw2.2.diff (2.7 KB) - added by simonwheatley 21 months ago.
Add ordering to term template tags through $args array type param (removed error_log call, var_dump, corrected the_terms args param)
9547.diff (3.1 KB) - added by wonderboymusic 15 months ago.
9547-tests.diff (8.4 KB) - added by tollmanz 12 months ago.
9547.2.diff (3.4 KB) - added by wonderboymusic 9 months ago.

Download all attachments as: .zip

Change History (37)

comment:1 ryan5 years ago

Right now galleries are the only user of term_order. No one is currently working on a generic API for using term_order. A patch adding some API would be welcome though.

comment:2 Denis-de-Bernardy5 years ago

  • Milestone changed from Unassigned to 2.9
  • Version set to 2.8

there are several duplicates of this bug around. most were closed as wontfix, on grounds of all sorts of fishy looking reasons. to find them, search for page order, or category order.

comment:3 Denis-de-Bernardy5 years ago

  • Milestone changed from 2.9 to 2.8
  • Resolution set to fixed
  • Status changed from new to closed

please re-open if r11250 isn't enough.

comment:4 Denis-de-Bernardy5 years ago

  • Milestone changed from 2.8 to Future Release
  • Resolution fixed deleted
  • Status changed from closed to reopened

sorry, mixed up with a separate ticket I remembered about.

comment:5 kevinB4 years ago

  • Cc kevinB added

comment:6 follow-up: lgedeon3 years ago

  • Cc luke.gedeon@… added

I propose adding an object_order column for this purpose. Term_order was intended for prioritizing multiple terms as applied to a single object. (see #5857)

Object_order would be a logical place for storing the order in the opposite direction - multiple objects assigned to the same term. Both are very helpful concepts.

I will be attempting to write functions for the term_order concept to submit as a patch. If the object_order concept is blessed, you may be able to use some of my code for that purpose too.

Last edited 14 months ago by SergeyBiryukov (previous) (diff)

comment:7 lgedeon3 years ago

No patch needed, actually. Here is the code to use term_order to sort tags.

/**
 * Sort post_tags by term_order
 *
 * @param array $terms array of objects to be replaced with sorted list
 * @param integer $id post id
 * @param string $taxonomy only 'post_tag' is changed.
 * @return array of objects
 */
function plugin_get_the_ordered_terms ( $terms, $id, $taxonomy ) {
	if ( 'post_tag' != $taxonomy ) // only ordering tags for now but could add other taxonomies here.
		return $terms;

	$terms = wp_cache_get($id, "{$taxonomy}_relationships_sorted");
	if ( false === $terms ) {
		$terms = wp_get_object_terms( $id, $taxonomy, array( 'orderby' => 'term_order' ) );
		wp_cache_add($id, $terms, $taxonomy . '_relationships_sorted');
	}

	return $terms;
}

add_filter( 'get_the_terms', 'plugin_get_the_ordered_terms' , 10, 4 );

/**
 * Adds sorting by term_order to post_tag by doing a partial register replacing
 * the default
 */
function plugin_register_sorted_post_tag () {
	register_taxonomy( 'post_tag', 'post', array( 'sort' => true, 'args' => array( 'orderby' => 'term_order' ) ) );
}

add_action( 'init', 'plugin_register_sorted_post_tag' );

?>

To use this, you will need to add tags in the order you want them to be and if you get them out of order you may have to remove some and add them back correctly. I might write a better UI later, but for now this at least works.

I guess next actions would be to write a patch for object ordering, and to come up with a UI(s) for both tag-style and category-style term_ordering.

comment:8 jeremyfelt2 years ago

Closed #20200 as duplicate.

comment:9 lkraav2 years ago

  • Cc lkraav added

comment:10 follow-ups: simonwheatley21 months ago

  • Cc simon@… added
  • Keywords has-patch dev-feedback added

I've created a patch which adds the ability to request terms ordered by term_order when using get_the_terms or get_the_term_list. Points to note:

  • I've added “simple” function arguments, rather than using an $args style array as a final parameter as this seems to be the pattern for things nearer template tags than API functions, happy to change the patch if this isn't the right approach.
  • This has affected the caching in get_the_terms in that it no longer simply uses the cache controlled by get_object_term_cache, because this cache does not respect ordering. It is not currently possible to pull the terms from the cache and order in PHP within the get_the_terms function, as the term objects do not contain the term_order property, even if it was deemed appropriate to sort terms in PHP at this point. I'd argue that the cache will still fill, even if there's more keys in it, and when it's full the system is just as cached as it was before, i.e. we've still got the function covered by a cache.

simonwheatley21 months ago

Add ordering to term template tags

comment:11 simonwheatley21 months ago

Almost forgot. I'll attach a proof of concept plugin which converts post_tag into a sorted taxonomy…

simonwheatley21 months ago

Non-production plugin to turn post_tag into a sorted taxonomy

comment:12 in reply to: ↑ 10 westi21 months ago

Replying to simonwheatley:

I've created a patch which adds the ability to request terms ordered by term_order when using get_the_terms or get_the_term_list. Points to note:

  • I've added “simple” function arguments, rather than using an $args style array as a final parameter as this seems to be the pattern for things nearer template tags than API functions, happy to change the patch if this isn't the right approach.
  • This has affected the caching in get_the_terms in that it no longer simply uses the cache controlled by get_object_term_cache, because this cache does not respect ordering. It is not currently possible to pull the terms from the cache and order in PHP within the get_the_terms function, as the term objects do not contain the term_order property, even if it was deemed appropriate to sort terms in PHP at this point. I'd argue that the cache will still fill, even if there's more keys in it, and when it's full the system is just as cached as it was before, i.e. we've still got the function covered by a cache.

I've taken a quick look and it looks good.

Do you fancy creating some unit tests for this new functionality too?

comment:14 in reply to: ↑ 10 SergeyBiryukov21 months ago

  • Keywords taxonomy term_order relationships removed

Replying to simonwheatley:

I've added “simple” function arguments, rather than using an $args style array as a final parameter as this seems to be the pattern for things nearer template tags than API functions, happy to change the patch if this isn't the right approach.

#14634 suggests adding $args.

simonwheatley21 months ago

Add ordering to term template tags through $args array type param (removed error_log call)

comment:15 simonwheatley21 months ago

Replying to SergeyBiryukov:

#14634 suggests adding $args.

Thanks Sergey. I can see this being more flexible, though it does increase complexity and I wasn't sure if this kind of parameter was in the spirit of the WordPress template tags. I've added it as a separate patch to make things easier to compare (diffing diffs… fun).

#9547-sw2.diff demonstratesget_the_terms and get_the_term_list with the $args parameter.

A further caching caveat with this patch: If you pass an empty array in $args (as is the default) it will cache under a different key to a call where the $args param contains values equivalent to the defaults in wp_get_object_terms. This is because the patch is MD5 hashing the serialised $args array. I can't think of a way around this without duplicating the defaults from wp_get_object_terms in get_the_terms (and I suspect in most cases the problem is not serious).

Last edited 21 months ago by simonwheatley (previous) (diff)

comment:16 follow-up: SergeyBiryukov21 months ago

#9547-sw2.diff has a var_dump() left in line 1077 and still passes two separate arguments to get_the_term_list() in the_terms().

simonwheatley21 months ago

Add ordering to term template tags through $args array type param (removed error_log call, var_dump, corrected the_terms args param)

comment:17 in reply to: ↑ 16 simonwheatley21 months ago

Replying to SergeyBiryukov:

#9547-sw2.diff has a var_dump() left in line 1077 and still passes two separate arguments to get_the_term_list() in the_terms().

Thanks for spotting the the_terms param missing, apologies for the var_dump. Attaching a new diff.

wonderboymusic15 months ago

comment:18 wonderboymusic15 months ago

  • Keywords needs-unit-tests added; dev-feedback removed
  • Milestone changed from Future Release to 3.6

Patch no longer applied cleanly, updated and changed the cache key / group to be more friendly with others

comment:19 SergeyBiryukov14 months ago

#14634 was marked as a duplicate.

comment:20 in reply to: ↑ 6 husobj13 months ago

  • Cc ben@… added

Replying to lgedeon:

I propose adding an object_order column for this purpose. Term_order was intended for prioritizing multiple terms as applied to a single object. (see #5857)

Object_order would be a logical place for storing the order in the opposite direction - multiple objects assigned to the same term. Both are very helpful concepts.

If term_order is intended to be used to prioritise the order of terms associated with posts, I second the idea of a object_order column for prioritising the order of posts within terms - essentially the equivalent of menu_order for posts, but for taxonomies.

tollmanz12 months ago

comment:21 tollmanz12 months ago

  • Cc tollmanz@… added

Added unit tests that attempt to test the latest patch as well as do a few other things:

  • Ordering using wp_get_post_terms is tested. The latest patch adds the ability to order by "term_group" and previously no ordering has been tested with wp_get_post_terms. I added those tests.
  • get_terms, get_the_term_list, and the_terms are tested when the $args parameter is used, which was added with the latest patch for this ticket. In all of these tests, I tested to make sure that the cache key was handled correctly so that different $args values would generate different cache objects.
  • The latest patch broke the "test_object_term_cache" test. This has been fixed. The issue was due to the change of the cache key in the latest patch.

comment:22 ryan11 months ago

  • Milestone changed from 3.6 to Future Release

comment:23 emzo11 months ago

  • Cc wordpress@… added

wonderboymusic9 months ago

comment:24 wonderboymusic9 months ago

  • Milestone changed from Future Release to 3.7

9547.2.diff​ refreshes so applying doesn't blow up.

comment:25 greenshady8 months ago

  • Cc justin@… added

comment:26 follow-up: c3mdigital8 months ago

Just adding another use case for the term_order column. I would love to see an additional WP_Query orderby argument 'term_order' to sort the order or posts.

Use Case:
Many enterprise publishers using WordPress require a way to set the precise order of posts on the home page as well as other areas. menu_order is great for the home page but when a post is in multiple categories or more than one sort_order is required for a post depending on where it is being displayed. The term_order column is perfect for this because anytime you do a tax_query the term_relationships table is already INNER JOIN 'd with the posts table. This works just like menu_order except only when the tables are joined via a term archive or new WP_Query

The flexibility of the current term_relationships table allows us to use the term_order column for any object_id the term is assigned to.

WP_Query extension to allow for ordering posts by term_order:

/**
 * Extends WP_Query with a posts_orderby filter that allows you to order posts by the term_relationships.term_order column
 *
 * @class XTeam_Lineup_Query
 */
class XTeam_Lineup_Query extends WP_Query {

	var $lineup_query;

	function __construct( $args = array() ) {
		add_filter( 'posts_orderby', array( &$this, 'posts_orderby' ), 10, 2 );
		$this->lineup_query = true;

		parent::query( $args );
	}

	function posts_orderby( $orderby, $query ) {
		if ( isset( $query->lineup_query ) ) {
			global $wpdb;
			return "$wpdb->term_relationships.term_order ASC, post_date DESC";
		}
		return $orderby;
	}
}

Resulting SQL Query:
`SELECT wpomni_posts.ID FROM wpomni_posts INNER JOIN wpomni_term_relationships ON (wpomni_posts.ID = wpomni_term_relationships.object_id) WHERE 1=1 AND ( wpomni_term_relationships.term_taxonomy_id IN (9) ) AND wpomni_posts.post_type = 'post' AND (wpomni_posts.post_status = 'publish' OR wpomni_posts.post_status = 'private') GROUP BY wpomni_posts.ID ORDER BY wpomni_term_relationships.term_order ASC, post_date DESC LIMIT 0, 10
`

UI for setting the order of posts
Not suggesting a ui for this but wanted to give an example of how the posts could be ordered. Using UI-Sortable we allow the posts to be ordered directly on edit.php when the list table is filtered by term.
Ajax callback bits that set the order:

		$str = str_replace( 'post-', '', $posts );
		$order = explode( ',', $str );
		$count = 1;
		foreach ( $order as $item_id ) {
			$wpdb->query( $wpdb->prepare(
					"UPDATE $wpdb->term_relationships SET term_order = %d WHERE object_id = %d AND term_taxonomy_id = %d",
					$count,
					$item_id,
					$term_tax_id
					)
			);
			$count ++;

I can work on patch or roll this into a plugin if it's something that has any traction.

comment:27 in reply to: ↑ 26 husobj8 months ago

Replying to c3mdigital:

Just adding another use case for the term_order column. I would love to see an additional WP_Query orderby argument 'term_order' to sort the order or posts.

I agree, being able to order posts belonging to a term would be great functionality.

The above discussion seems to indicate that the term_order column was intended for ordering terms associated with a post, not posts associate with a term though.
http://core.trac.wordpress.org/ticket/9547#comment:20

I already use this column for ordering posts in a term which is fine for now unless WordPress core implements a UI that would interfere but allowing people to use it for ordering terms instead.

comment:28 follow-up: nacin7 months ago

  • Type changed from feature request to enhancement

Should get_the_term_list() and the_terms() be converted from $before = '', $sep = '', $after = '', $args = array() to just $args = array() that contains 'before', 'sep', and 'after'?

comment:29 in reply to: ↑ 28 helen7 months ago

Replying to nacin:

Should get_the_term_list() and the_terms() be converted from $before = '', $sep = '', $after = '', $args = array() to just $args = array() that contains 'before', 'sep', and 'after'?

My vote would be yes.

comment:30 nacin7 months ago

  • Milestone changed from 3.7 to Future Release

Can someone change the title of this ticket to better reflect what is actually being proposed? It seems we're just trying to expose lower level API higher up in the stack (and crossing the line between uncached API and cached API). Because of that, this is a safe punt.

Note: See TracTickets for help on using tickets.