Control term order on a per-post basis

I have a taxonomy in which term order matters, but varies from post to post. So I want to be able to set the order that terms are displayed in on a per-post basis:

Since this would vary from post to post, I need something other than the taxonomy-wide term ordering supported by plugins like Custom Taxonomy Sort and Taxonomy Terms Order.

I am looking for advice on how to implement this. Here is an outline of my current thinking:

Per-post term ordering using a custom field

  1. Remove standard taxonomy metabox using remove_meta_box()

  2. Add custom metabox containing term ordering interface, e.g., drag-and-drop (or simple text input next to each term for an integer describing term’s order. (Or perhaps it’s possible to add drag-and-drop to the existing meta_box without having to remove and add a new one?**)

  3. Using javascript, save a comma-separated list of term IDs, in order, in a hidden custom field, _mytermorder_taxonomyname. (Separate custom field for each taxonomy, e.g., _mytermorder_category, __mytermorder_tags, etc.)

  4. Create a custom function my_get_the_terms() that loops through the IDs in the _taxonomyname_termorder custom field. If a post has terms but no _taxonomyname_termorder field (as with old posts), just return get_the_terms().

  5. In relevant theme files, replace instances of get_the_terms() or other native term functions with my_get_the_terms().

Do you have advice, alternatives, warnings, or related snippets? I’d be grateful for any thoughts. Thank you!

Concerns

  • What if the list in the custom field gets out of sync with the actual term list (as it would if terms were added through an interface other than the post edit screen, such as the quick-edit/bulk edit form or Front-End Editor).

Solution: At the beginning of my_get_the_terms(), we can check whether the IDs in the custom field match the terms returned by get_the_terms. If not, default to get_the_terms.

  • New terms wouldn’t be captured on first use, because they don’t yet have an ID. You’d have to save a new term before it would work. (I could live with this, but it is a problem.)

  • The meta box has to list terms in the right order when the post edit page loads.


** For drag-and-drop sorting in the default metabox, I discovered that I can use jQuery UI’s .sortable, already available in the admin; for a non-hierarchical taxonomy, it’s $('#taxonomyname .tagchecklist').sortable().

Related posts

Leave a Reply

3 comments

  1. I’m not sure if I understand exactly what your trying to accomplish but I’ve outlined a way for you to sort the order of terms associated with the current post.

    Html for the term order metabox:

     echo '<ul id="the-terms">'
             $terms = get_the_terms( $post->ID, $taxonomy );
                foreach ( $terms as $term ) {
                echo '<li class="item" id="term-'.$term->term_id.'">'. $term->name .'</li>';        
                }
                echo '</ul>';
                echo '<a href="javascript: void(0); return false;" id="save_term_order" class="button-primary">Update Order</a>';
    

    Javascript to make the above list sortable and save the order using ajax.

    *Note: requires jQuery UI-Sortable.

    jQuery(document).ready(function() {  
    // Make the term list sortable
            jQuery("#the-terms").sortable({
                items: '.item',
                placeholder: 'sortable-placeholder',
                tolerance: 'pointer',
                distance: 1,
                forcePlaceholderSize: true,
                helper: 'clone',
                cursor: 'move'
            });
    // Save the order using ajax        
       jQuery("#save_term_order").live("click", function() {
            var postID = $("#post_ID").val();
            jQuery.post(ajaxurl, {
            action:'save_term_order', 
            cache: false, 
            post_id: postID,  
            order: jQuery("#the-terms").sortable('toArray').toString(),
            success: ajax_response()
           });
                return false; 
        });   
     });
    

    WordPress ajax function to save the order as a custom field:

    add_action ( 'wp_ajax_save_term_order', 'term_order_save' );
    function term_order_save () {
        global $wpdb;
        $wpdb->flush ();
        $item_id = $_POST['post_id'];
        $meta_key = '_term_order';
    
        $order = $_POST[ 'order' ];
        $str = str_replace ( "term-", "", $order );
        $int = str_replace ( "'", "", $str );
    
        update_post_meta ( $item_id, $meta_key, array ( 'term_order' => $int ) );
    
        $response = '<p>Term order updated</p>';
        echo $response;
    
        die(1);
    }
    

    This will save a list of the term id’s in order sorted as a serialized array:

    _term_order => array(
    term_order => ‘123,312,110,34,44,27’
    )

    To show the list of ordered terms on the front end:

    $orderd_terms = get_post_meta ( $post->ID, '_term_order', true );
    $terms = $ordered_terms[ 'term_order' ];
    $term_ids = explode ( ",", $terms );
    
      for ( $i = 0; $i < count ( $term_ids ); $i ++ ) {
    
          $term = get_term( $term_ids[$i], $taxonomy, OBJECT)
          echo $term->name;
      }
    
  2. I’m sorry for bringing this question back to life after such a long time. This need is something that I came across recently and while searching for an answer bumped into this question.

    After some digging I found out that apparently WordPress has had for quite some time (ticket #5857) a term_order column in wp_term_relationships table to tackle this particular problem.

    To make use of this colum and term ordering you need to add

    'sort'      => true,
    'args' => array(
        'orderby' => 'term_order',
    ),
    

    to register_taxonomy $args, when registering a custom taxonomy, or to register_taxonomy_args, if modifying an existing taxonomy.

    Naturally this doesn’t give you nice drag’n’drop term ordering on the edit view, but just saves the terms in the order they are attached to the post. And you need to watch out for any greedy plugins you might have installed on your site, which might mess with this.

    I think this is the first time ever I noticed these arguments in the register_taxonomy documentation page as they are perhaps a little poorly noted there. But on the source file they are documented better, taxonomy.php, register_taxonomy()

    @type bool $sort
    Whether terms in this taxonomy should be sorted in the order 
    they are provided to wp_set_object_terms(). 
    Default null which equates to false.
    
    @type array $args
    Array of arguments to automatically use inside wp_get_object_terms() or this taxonomy.
    
  3. Below is a workaround that helped me a lot. I have tried to explore register_taxonomy and args at wp documentation, and finally made it.

    add_filter( 'register_taxonomy_args', 'wpdocs_my_taxonomy_team_args', 10, 2 );
    function wpdocs_my_taxonomy_team_args( $args, $taxonomy ) {
         
        if ( $taxonomy === "team" || $taxonomy === "post_tag") {
            $args['sort'] = true;
            $args['args'] = array('orderby' => 'term_order');
        }
         
        return $args;
    }