How to order categories in WordPress?

I use wp_list_categories() to get the list of all the categories and generate the navigation bar. Is there a way to order these categories in a particular order other than alphabetical ordering.

eg: Connect, News & Views, Q&A, Hello Startup, Startup 101…

Related posts

Leave a Reply

7 comments

  1. Most themes don’t use the description of the category for anything. Easy workaround I did was to use numbers in description. The top post here currently has some jQuery hack from here, it’s unneeded.

    You can add custom order fields I suppose as well.

    Just

    $categories = get_categories( array(
        'orderby' => 'description',
        'order'   => 'ASC'
    ) );
    
  2. Technical approach

    The problem in wordpress core is that the table wp_terms has no term_order column. That means, standard wordpress does not support the custom term order. If you look at this WP database structure you can find the table wp_term_relationships. This table is responsible for the relationships between posts and the taxonomy (your categories) AND this table has a term_order column.

    Now, with a simple SQL statement ALTER TABLE wp_terms ADD term_order INT(11) NOT NULL DEFAULT 0 (not forget the $wpdb->wp_terms variable) you can add a column to the table, which is responsible for your custom category order. Then you can put your custom category order in this two columns of wp_term_relationships and wp_terms. When all is finished, you can hook into the filter of get_terms_args and change the orderby to term_order.

    Here a list of all relevant links for the technical approach:

    A plugin can do the job for you

    Check my plugin to solve this: WordPress Real Categories Management. WP RCM creates an extra field term_order on the wp terms table. It also brings a lot of other useful features as you can see in the screenshot below. It allows you to organize your wordpress categories in a nice way. It is easy to use, just drag&drop your categories and move it to a specific order. The plugin works in all Custom post types.

    Drag & Drop categories order

    From the product description i can quote. If you want to try the plugin, there is also a demo on the plugin page.

    There are a lot of free plugins

    This can be solved with a lot of free plugins available within the wordpress.org plugin repository. Simply search for “category order” in your WordPress Dashboard > Plugins > Install.

  3. I did it generating several term lists. I call it later by my own order. I’m a PHP beginner.

    First, I store, in a different variable, the ID for each category term:

    $terms = get_terms('my_taxonomy', 'hide_empty=0');
            foreach ( $terms as $term ) {
               ${$term->slug} = get_term_by('slug', $term->slug, 'product_cat');
               ${$term->slug.'_array'} = (array)${$term->slug};
               ${$term->slug.'_array_id'} =${$term->slug.'_array'}['term_id'];
          };
    

    Then, I create several args for each wp_list_categories() excluding, with this variable the terms I want to:

          $args = array(
            'taxonomy'     => 'my_taxonomy',
            'orderby'      => 'name',
            'show_count'   => true,
            'pad_counts'   => false,
            'hierarchical' => true,
            'title_li'     => '',
            'hide_empty'   => 0,
            'show_option_all' => 'Show all',
            'exclude'    => array( $term1_array_id, $term2_array_id )
          );
    
          $args_1 = array(
            'taxonomy'     => 'my_taxonomy',
            'orderby'      => 'name',
            'show_count'   => true,
            'pad_counts'   => false,
            'hierarchical' => true,
            'title_li'     => '',
            'hide_empty'   => 0,
            'exclude'    => array( $term3_array_id, $term4_array_id, $term1_array_id )
          );
    
          $args_2 = array(
            'taxonomy'     => 'my_taxonomy',
            'orderby'      => 'name',
            'show_count'   => true,
            'pad_counts'   => false,
            'hierarchical' => true,
            'title_li'     => '',
            'hide_empty'   => 0,
            'exclude'    => array( $term1_array_id, $term4_array_id, $term5_array_id )
          );
    

    Finally, I can call separately each term list:

    <ul>
        <?php wp_list_categories( $args ); ?>
        <?php wp_list_categories( $args_1 ); ?>
        <?php wp_list_categories( $args_2 ); ?>
    </ul>
    
  4. I didn’t find anything so I constructed my own method. I abstracted it away in an abstract class for my plugin, hence the extra code, but you can pull the methods.

    The main method to look at is format_hierarchy()

    // The parent class
    abstract class Taxonomy {
        protected bool $_terms_loaded;
        
        protected array $terms;
        protected array $formatted_terms;
        
        public function __get( $property ) {
            if ( $property === 'formatted_terms' ) {
                if ( !isset( $this->formatted_terms ) ) $this->format_hierarchy();
                return $this->formatted_terms;
            }
            
            if ( substr( $property, 0, 1 ) === '_' ) die( 'Cannot get private properties' );
    
            if ( property_exists( $this, $property ) )
                return $this->$property;
        }
    
        /**
         * Formats the taxonomy's terms into a hierarchy of term_blocks and saves the value as a property to the class
         * 
         * @return              array       an array of `[term_taxonomy_id:number] => term_block` like:
         *                                      array(
         *                                          array( // parent $term_block
         *                                              'term' => WP_Term,
         *                                              'children' => array( $term_blocks… )
         *                                          ),
         *                                          …$term_blocks…
         *                                      )
         */
        public function format_hierarchy():array {
            if ( !$this->_load_terms() ) return [];
    
            // Holds a reference to every category, parents and children
            $term_blocks = [];
    
            // Holds a reference to every top most level category
            $parents = [];
    
            foreach ( $this->terms as $term ) {
                // Add itself to the list of all categories
                $term_block = [
                    'children' => [],
                    'term' => $term
                ];
                
                // Add itself to the array of all categories
                if ( !isset( $term_blocks[ $term->term_taxonomy_id ] ) )
                    $term_blocks[ $term->term_taxonomy_id ] =& $term_block;
                
                // If it's a child category…
                if ( $term->parent !== 0 ) {
                    // If the parent hasn't been created yet, create it
                    if ( !isset( $term_blocks[ $term->parent ] ) )
                        $term_blocks[ $term->parent ] = [
                            'children' => [],
                            'term' => null
                        ];
                        
                    $term_blocks[ $term->parent ][ 'children' ][] =& $term_block;
                } else
                    // Otherwise it's a parent
                    $parents[ $term->term_taxonomy_id ] =& $term_blocks[ $term->term_taxonomy_id ];
    
                // set the term block's WP_Term property
                $term_blocks[ $term->term_taxonomy_id ][ 'term' ] =& $term;
                // This is needed so that the loop doesn't readd the same reference over and over again
                unset( $term ); unset( $term_block );
            }
            
            return $this->formatted_terms = $parents;
        }
    
        /**
         * Given a WP_Term property value, and a property key, recursively searches through all of the terms for it
         * 
         * @property    $term_val       mixed       The property value to find
         * @property    $prop           string      The property key for the value
         * @property    $with_parent    ?boolean    Whether to return the top level parent as well
         * @property    $term_blocks    ?array      Array of term blocks
         * @return                      array       If $with_parent is true, returns an [ $found_term_block, $top_level_parent ]
         *                                          Otherwise returns only the found term block
         */
        public function find_term_by(
            $term_val,
            string $prop,
            bool $with_parent = false,
            $term_blocks = null
        ):?array {
            if ( is_null( $term_blocks ) ) $term_blocks = $this->formatted_terms;
            foreach ( $term_blocks as $term_block ) {
                if ( $term_block[ 'term' ]->{$prop} === $term_val ) return $term_block;
                if ( count( $term_block[ 'children' ] ) &&
                    ( $found = $this->find_term_by( $term_val, $prop, false, $term_block[ 'children' ] ) )
                ) return $with_parent ? [ $found, $term_block ] : $found;
            }
            return null;
        }
    
        /**
         * Loads the taxonomy terms once from the DB
         */
        protected function _load_terms():bool {
            if ( isset( $this->_terms_loaded ) ) return $this->_terms_loaded;
            
            $this->terms = get_terms(
                array(static::$taxonomy),
                array(
                        'hide_empty'    => false,
                    )
            );
    
            if ( !$this->terms ) {
                ClassErrorHandler::handle_exception(
                    new WP_Error( 500, 'Failed to load category terms: '' . static::$taxonomy . ''' )
                );
            }
    
            return $this->_terms_loaded = !!$this->terms;
        }
    }
    
    // The Implementation
    class TaxonomyProductService extends Taxonomy {
        public static string $taxonomy;
        public static string $slug;
    
        /**
         * To be called upon taxonomy registration long before any instance is required
         */
        public static function define_taxonomy( string $slug, string $taxonomy ) {
            static::$slug = $slug;
            static::$taxonomy = $taxonomy;
        }
    }
    

    Right after registering the custom taxonomy I call
    TaxonomyProductService::define_taxonomy( ‘url-slug’, ‘product-service’ );

    And finally how it’s used

    $tax = new TaxonomyProductService();
    $terms = $tax->formatted_terms;
    // search for a term whose slug === `my-term`, and return the parent category
    list( $current_term, $parent_term ) = $tax->find_term_by( 'my-term', 'slug', true );