Create an Alphabetical Glossary of Posts in WordPress

Once up a time i did a client project where i had to have archives organized alphabetically.   I ended up accomplishing by adding a query variable and targeting the posts_where filter.  However, in answering a recent question at WordPress Stack Exchange I decided that it might be neater to create a hidden taxonomy instead.  I don’t know if there is any performance benefit, but the code was a little more elegant and you get prettier permalinks: site.com/glossary/a instead of site.com/?glossary=a right off the bat without the need to do any complicated htaccess rewrite rules, since everyone knows that mod-rewrite is straight up voodoo.

Creating a Hidden Taxonomy

Pretty standard function for registering a taxonomy. Just going to accept a lot of the defaults, and not worry about labels since we’re going to hide it from the back-end by setting the ‘show_ui’ parameter to false. For this example we’re going to call the taxonomy “glossary” and we’re going to assign it to posts, but we could well have done it for any custom post type.

Automatically Setting Terms for each Post on Save/Update

It’d be a pain if we had to actively remember to sort each post by its letter each time we wrote a post. I have enough difficulty just writing a post in the first place. But with some code we can automatically pop off the first letter of the post title and assign that letter as the term in our “glossary” taxonomy. The following is the pretty standard function for saving information from a metabox, which works perfectly for our case even though we don’t have a visible metabox.

Auto-Assigning Terms to existing posts

If you’ve had a blog for a while and have a lot of posts the idea of going back and manually saving the posts again sounds worse than pulling our your toenails.  So you can run a little function to do it automatically.  Ideally, if this were a plugin, it’d run on the plugin’s activation hook.  Since it’s not you can just drop it into your functions.php and then reload your site. Thanks to the transient check you can leave it in there and it won’t run again. Basically what it will do it grab all your posts, loop through them all, pop off the first letter of the title and assign it to the “glossary” taxonomy.

Finally, Creating the Alphabet “Menu”

Now that we have assigned a term for each existing post and for every post to come in our custom taxonomy, we should display an alphabet menu…. or some way to access the different Letter archives.  This was the one place were the code was not 10x more elegant than in my first iteration because there was no easy way to test whether a term had a post in it.  Usually, yes, it would… but if you changed a post name and there were no other posts under a certain letter you could come up with an empty section.  So what I did was get all the terms in the taxonomy with get_terms which hides empty terms by default, loop through that data and store it in an array, then check each letter of the alphabet against that array.  To avoid running through that every single page load, I used the transient API to store the resulting array of alphabet terms.  This array refreshes whenever a post is updated (this actually happens in an earlier code block for the kia_save_first_letter() function. For my original project I wanted to have the letters with posts have an active link and the letters without posts in that section just have the letter.  Something like: A B C D** E** …. and onwards, so I have re-created that effect.

Future Improvements

One thing I’d like to improve upon would be ensuring that the first letter I’m popping off with the substr PHP function is actually a letter… or maybe a number, but not a character.  I’d also like to get it to skip words like The, An, and other pointless words that don’t really reflect on the subject.  But then this isn’t a solution for everyone… if you want to make sure that “A Post about Bacon” and “The Bacon Post” show up in the same archive, well you should tag them in the Bacon tag and not hope that they show up alphabetically in the right place.  Still it has its uses.  Let me know of any improvements you make!

Posted in

66 Comments

  1. Damanjit Singh on February 15, 2012 at 8:19 am

    Hi Kathy, Today i saw your website (kathyawesome). Your website provides really useful stuff but why are you using this simple theme (twentyeleven). Use a new theme, add some social follow icons and also add “About US” and “Contact us” page in navigation bar.

    I Hope you like my suggestion.
    Best Regards,
    Damanjit Singh (TechWallz.COM)

    • kathy on March 8, 2012 at 5:09 am

      @Damanjit Thank you for your suggestions. You know what they say about designing your own site? You have a pain in the ass for a client. It’s a work in progress, but there was some information I wanted to publish and didn’t want to wait on a custom theme.

  2. Gino on March 27, 2012 at 10:09 pm

    Hi Kathy, you are pretty awesome. I’ve (and many other likely) been struggling with this some time now.

    I’m trying the get the first letter of the second word. When setting the term to the first letter of post title. Just can’t figure out the right way.

    posts}.post_title))), 0, 1)), $taxonomy );

    ?>

    Lookin’ forward to your coming posts…

    • Gino on March 27, 2012 at 10:10 pm

      Here what I have, but doesn’t work for me:

      wp_set_post_terms( $post_id, strtolower(substr(LOCATE(‘ ‘,LTRIM({$wpdb->posts}.post_title))), 0, 1)), $taxonomy );

      • kathy on March 27, 2012 at 10:23 pm

        gino, thanks for the compliment. as you can tell, i haven’t bothered to theme the site yet, so code in comments is a little shaky. i have to wonder what you are doing with

        $wpdb->posts}.post_title

        b/c that doesn’t really resemble my code at all… and indicates you are trying to do something with the database maybe?

        i don’t really have time to solve your question, so the best i can tell you is to revisit what i have for wp_set_post_terms and play around with the php string matching. i’d suggest you do something on the front-end to just experiment with a static string until you get the right PHP combo.

  3. Gino on March 28, 2012 at 7:42 pm

    Hi Kathy, I add explode(‘ ‘,x) to take first letter of the second word in the title.

    $name_array = explode(‘ ‘, $_POST[‘post_title’]);
    echo $name_array[1];

    wp_set_post_terms( $post_id, strtolower(substr($name_array[1], 0, 1)), $taxonomy );

    • kathy on March 28, 2012 at 8:34 pm

      nice! i might be able to use that if i ever update this to skip “an”, “the”, “a” and other short, meaningless words.

  4. AzzePis on November 15, 2012 at 8:28 pm

    Great tutorial, thank you very much, it is exactly I need for one of the movie database website

    • kathy on November 15, 2012 at 9:59 pm

      Glad it helped you! A movie database is a great application. Though the more I think about it the more it probably could be done without a taxonomy…. and definitely needs to consider how to handle a movie title like “The Sting”. My code will probably file that under “T” when it should be “S” for “Sting, The”. Aw well. Keep learning.

      • AzzePis on November 16, 2012 at 6:20 am

        I added the taxonomy-box to editor screen, so author can check and change the letter. I now, it’s not the automatical way, but :-)

        • kathy on November 16, 2012 at 1:39 pm

          @AzzePis – Nicely done. I think this could be done totally with rewrite rules and custom queries, but whatever get the job done.

  5. David Alexander on December 18, 2012 at 8:01 pm

    Hey Kathy, great work, trying to implement this with a custom post type created via the types plugin. When you make a custom post type you have the plural name and the singular, which should be referenced in your code instead of “post” I was guessing singular, but the code didn’t seem to work with my custom post type.

    How many changes need to be made to your code? I changed two instances of “post” to “listing” but no joy. I noticed your final function to check current posts and update their alphabetical taxonomy entry doesn’t bare a reference to the post type? Is it because it is inherited from the code before it?

    Thanks

    • kathy on December 18, 2012 at 8:27 pm

      Thanks David. When you register the post type… that’s where you set the name of the post type that you will need for this code. register_post_type( 'bacon', $args ); So you’d refer to that type as “bacon”. I honestly don’t know how much needs changing, but I wouldn’t think much. All the code that references a post by its ID will work on any post type. I would also double check that your taxonomy is registered to your custom post type. Hope that helps.

  6. David Alexander on December 18, 2012 at 8:31 pm

    Hey Kathy, yeah, the issue I have with not knowing is because the post type is not manually registered, I use the types plugin for custom post types in some cases where there is a lot going on such as lots of different custom fields attributed to the new post type etc, and I am also working on the Thesis framework, I will have a look and see if the plugin gives more info as to which term is used as the global post type name for these purposes, I am pretty sure it is the plural with “Types”.

    Thanks for your speedy reply, I admire your work!

  7. kathy on December 18, 2012 at 9:14 pm

    You happened to catch me when I was procrastinating from what I should be doing. Maybe post a question to the Types people or in WordPress Answers. I am not actually sure that the base would be. Sorry I couldn’t be more helpful. You’ve caught me at a bit of a frazzled time.

  8. Jon Nixon on April 2, 2013 at 11:19 pm

    Hi Kathy,

    Is there anything special I need to do here to make /glossary/ work as a virtual directory (other than what you’ve provided)? I’m getting a 404 error when I attempt /glossary/v

    Any thoughts?

    • kathy on April 3, 2013 at 8:00 am

      Hi Jon. I don’t know what you mean by “virtual directory” but be sure to re-save your permalinks after initializing a custom taxonomy. Sorry, but I can’t provide much support on this post.

  9. Dennis Martin on May 24, 2013 at 6:29 pm

    Hi Kathy,

    You’re so clever! This is most likely what I’m after for a Woocommerce site I’ve wound up building, but one question, would this work for the product,not sure if they are handled the same way as posts? Fairly new to wordpress as you can see!

    Many thanks for a great article.

    Dennis

    • Dennis Martin on May 24, 2013 at 6:31 pm

      To elaborate a little, it’s a second hand book site, so being able to list by author, title is kind of essential, and each more or less unique book is listed as a product.

      Cheers

      Dennis

    • kathy on May 24, 2013 at 8:58 pm

      Dennis,

      This concept should work just fine for products. You’ll just have to adapt your taxonomy and some of my code from the ‘post’ post type to the ‘product’ post type. Regarding your ‘books’ you might also want an author taxonomy.

  10. lee on July 17, 2013 at 1:50 pm

    Hi Kathy,
    I love your alphabetical post taxonomy article and was wondering how do I get it to just index posts that belongs in a specific category and not all posts?

    • kathy on July 17, 2013 at 2:58 pm

      Hi Lee, Thanks for stopping by. I am sure this is possible, but it isn’t exactly easy either, so I couldn’t tell you off the top of my head. Instead of a specific category, I think it’d probably be easier to switch to a custom post type.

  11. Mr. Cee on August 14, 2013 at 4:16 am

    Hi Kathy. Awesome tutorial for real! I just wanted to know how would get it to work for custom post type? Would I simply change the following line register_taxonomy('glossary',array('post')), to register_taxonomy('glossary',array('custom-post-type')), and then change any reference to ‘post’ to ‘custom-post-type’ where it appears in your example above? Thanks.

    • kathy on August 14, 2013 at 6:09 am

      It has been a long while since I’ve looked at this code, but yes, I assume that you’d change any reference to “post” to your CPT.

  12. Mr. Cee on August 14, 2013 at 4:41 am

    Lastly, where does the code for “Creating the Alphabet Menu” go? In functions.php or in the template using php?

    • kathy on August 14, 2013 at 6:10 am

      As written it goes in the template where you want to display it. Nothing stopping you from wrapping it in a function and adding it to a hook if your parent theme has appropriate hooks and you are making a child theme.

  13. Scott on October 22, 2013 at 7:24 pm

    Kathy,
    Thanks for this post! It was the best post I found on this and very well explained. One question though. Could I modify this to use a range such as A-D, E-H and so on? Thanks again.

    • kathy on October 22, 2013 at 8:21 pm

      Hi Scott, I’m glad you found it useful. Almost assuredly, the code could be altered to use ranges. You’d have to change the menu and how the tax data is saved…. to save terms such as “a-e” instead of simply “e”, but it is definitely doable.

      • Scott on October 23, 2013 at 1:41 pm

        So I would need to change this section of the function? wp_set_post_terms( $post_id, strtolower(substr($\_POST['post\_title'], 0, 1)), $taxonomy ); and this in the template. foreach(range('a', 'z') as $i) : Apologize for my ignorance, but first time attempting something like this.

  14. haripriya on October 25, 2013 at 6:26 pm

    Hi Kathy,
    I need posts of one of custom posts types {medical glossary} as Alphabetical filter

    A B C D….etc (or) search form like dictionary if we type A/a the it shows list of posts under A name.. like that
    A
    Apost 1
    B
    Bpost 2

    Please give me suggestions how to display those things.
    I have created custom posts types as medical glossary which post-type= disease
    so , i need to show all these things and one more challenge is to display these things in a page.. {i.e tempalte}.

    Please help me kathy.
    I am eager for your reply..

    Thanks in advance..
    TC

  15. Naama on May 25, 2014 at 11:43 am

    Has anyone managed to get this working with custom post types? I am trying but I cant work it out.

    Can anyone post their code?

    Thanks for your help!

    • kathy on May 25, 2014 at 1:49 pm

      To use for a particular post type you should only need to
      1. register the taxonomy for that post type
      1. change the save routine’s $limitPostTypes array
      1. change the get_posts args in the kia_run_once() (only needed if you have existing posts)

      But I’m not going to test it, so your mileage may vary. Good luck.

      • Naama on May 26, 2014 at 7:13 am

        Thank you for your quick and useful reply! I made the changes you said and ran the code. It generated the alphabet but without any links. So I understand there must be an issue with populating the $alphabet array. I change the code there to accommodate CPT to

        $args = array( "numberposts" => -1, "post_type" => "directory-listing" );
        $posts = get_posts($args);

        could you point me in the direction of where the issue may be? Thanks so much

        • kathy on May 26, 2014 at 4:02 pm

          I can’t really look into it, but I think you’re on the right path. I would delete the $alphabet transient and maybe disable the set_transient() part until you figure it out.

        • kathy on May 27, 2014 at 2:17 am

          It also occurred to me while I was out for a walk that get_terms() only gets terms that have at least one post with that term assigned (unless you change the arguments). As such, if you don’t have any posts yet in a particular term, you won’t get a link. Maybe that helps and maybe it doesn’t.

  16. Damien on June 9, 2014 at 3:56 pm

    Hi Kathy This is exactly what I’ve been looking for (and great site too btw!). The problem I’m having though is that instead of using the first letter of the post title I would like to use the first letter of a custom field. I’m using this for a bibliography and so want to order my posts by the ‘author_surname’ custom field for a custom post type called ‘publications’. Here’s the code I’m trying to use. Can you see where I might be going wrong? Would appreciate any help you could provide. Many thanks D

    • kathy on June 9, 2014 at 7:03 pm

      Hi Damien. Thanks for stopping by. I can’t really provide free customization support in the comments and as I’m in training camp right now I can’t provide paid support at the moment either. Do remember that get_posts() only gets posts by default and always make sure things work before setting transients. Good luck.

      • Damien on June 10, 2014 at 8:26 am

        Hi Kathy
        No problem. I’ll figure it out I’m sure!
        Thanks

        D

  17. Diana on June 15, 2014 at 3:08 am

    Thanks a lot Kathy!

    Simple and works nicely.

  18. Andy on June 23, 2014 at 6:28 pm

    This is really great! Thanks so much for sharing Kathy!!

  19. Thanasis on September 10, 2014 at 4:49 pm

    Hey Kathy i ve got a problem using this with greek posts! I need to use the greek alphabet not the english one but i cant make it happen. It shows me character codes like 208 210 etc instead of letters. For instance if i use it with English alphabet i get exactly the result i want but when i use it with the greek posts i have instead of Α, Β, Γ, Δ , Ε Ζ … => 208, 209 ,210 what can i do?

    • kathy on September 10, 2014 at 8:20 pm

      Hi Thanasis,

      I have to admit to being a little weak on internationalization stuff. Do you mean your menu doesn’t look right? It could be that foreach(range('a', 'z') as $i) : needs to be adapted to something that loops through the greek alphabet instead. If it isn’t saving correctly, I’m at more of a loss since it ought to save characters in your site’s language.

      • thanasis on September 10, 2014 at 9:43 pm

        Yes it is exactly like that…and if i change the foreach(range(‘a’, ‘z’) as $i) to foreach($alphabet as $i) i get the codes i was telling you!anyway thank you for your help and your great post!!

        • kathy on September 11, 2014 at 9:12 am

          You’re welcome!

          Well $alphabet is not the actual alphabet, but rather a list of all the terms in the alphabet taxonomy (so all the first letters that have posts). If the greek first letter isn’t able to be saved as a term name, well I’m stumped. (Though maybe it has something to do with sanitize_title()?)

          range() does not yet support unicode (PHP6.0 supposedly so who knows!), so you can’t use that to create the list. You could however, write your own array and use that. for example:

          • thanasis on September 11, 2014 at 10:51 am

            Ok i ll do that. Thank you so much Kathy



          • Bahruz on February 12, 2015 at 8:01 am

            First, Thaks you for this code. It’s very good idea. But I have problem like thanasis. I have instead of Ə, Ü, Ç, Ş , Ö, Ğ characters my posts. And create massive:

            $symbols = array('a', 'b', 'c', 'ç', 'd', 'e', 'ə', 'f', 'g', 'h', 'x', 'İ', 'i', 'j', 'k', 'q', 'l', 'm', 'n', 'o', 'ö', 'p', 'r', 's', 'ş', 't', 'u', 'ü', 'v', 'y', 'z');

            Latin characters(a-z) get link, but my special characters not have link.



          • kathy on February 13, 2015 at 5:56 pm

            Hi Bahruz,

            Unfortunately, I don’t know the answer. I’m not well-versed in non-English characters. If they show up unlinked, it is possible that the first letter isn’t saving as a character? If you figure it out please do post back so I can update the post.

            -k



  20. Muluneh Awoke on November 20, 2014 at 12:13 pm

    A very nifty approach. Awesome indeed!!!

  21. Brendon on December 8, 2014 at 1:46 am

    Hi,

    Thank you for posting this tutorial. It is super useful. It worked great, except the letters were not live links. Where could I have gone wrong?

    Thank you,
    Brendon

    • kathy on December 8, 2014 at 8:18 am

      It has been ages since I wrote this, but if I had to guess you don’t have actual links because those letters don’t have any posts in their archive. Have you written any posts yet? There’s also the transient I was using to save the terms so if you do have posts, try re-saving a post to clear the transient. And make sure you ran the “run once” code.

      • Brendon on January 10, 2015 at 4:44 pm

        KATHY IS AWESOME! Thank you.

  22. Miguel Morera on March 20, 2015 at 4:14 pm

    Hi Kathy!
    First of all, thank you for this post! It’s awesome!
    I get the alphabet list but my problem is when I make a click on a letter I go to the first post of the letter list instead of the archive page listing the posts with that letter.
    What can I do? Thank you in advance!

    • kathy on March 20, 2015 at 4:48 pm

      You’re welcome Miguel. I don’t know what you mean exactly, but

      printf( '<li class="az-char %s"><a href="%s">%s</a></li>', $current, get_term_link( $i, $taxonomy ), strtoupper($i) );

      should be printing a link like : www.yoursite.com/glossary/a

      If that isn’t resolving to an archive you might try re-saving your permalinks settings.

  23. Stephen Smith on September 18, 2015 at 11:02 pm

    Hey Kathy! Thanks for this awesome bit of code. I made a few changes for my use case in which the customer wanted ranges of the alphabet, so I made a small function that takes a string and returns strings like “a-f”. I then integrated it with existing filtering code by hiding the “glossary” select (with visibility hidden and position absolute) and added code to fetch all the terms and display links that triggered value changes in the hidden select before using jquery to submit the form again.

    Thanks again for the useful post!

    • kathy on October 19, 2015 at 2:22 pm

      Hi Stephen, that sounds really cool. Thanks for letting me know what you’ve done with my code.

  24. Orkhan Hasanli on November 14, 2015 at 11:20 pm

    Hi Kathy
    I want to thank you for the useful article that helped me build my new site. I want to ask you how you can create an array with 0-9, while maintaining that Article, any nubmer, it shows on links 0-9.
    For example, 0-9, A, B, C, D, …
    Article name: 5-NOK must be shown in 0-9

    Thanks in advance)))

  25. Ismaël on November 16, 2015 at 2:26 pm

    Hi I have some questions:
    I didn’t correctly get how do we use your code? Where do we use these functions? and do we have to use all the aformentioned functions or only some of them?

    thanks,

    • kathy on November 16, 2015 at 5:09 pm

      You need all the functions as they all work together to handle various aspects of the solution. I’d like to say they should all go into a plugin, but the menu definitely needs to go in your theme somewhere (on the archive template for whatever post type you are using this one… the example is for posts), so it would probably be easiest to put everything else in your theme’s functions.php.

      • Ismaël on November 16, 2015 at 8:27 pm

        Hi Kathy! thanks for your answer!

        Well I’m trying your stuff for posts so it’s fortunate…

        So to be clear, I need to use it all in the functions.php file then ??

        • kathy on November 16, 2015 at 8:43 pm

          No, everything goes in functions.php except the last code block which contains the menu. That needs to go in the appropriate template…. could be index.php could be archive.php. I can’t know for sure because everything theme is different. If you aren’t already you should be using a child theme to make these types of modifications.

  26. Ismaël on November 16, 2015 at 9:38 pm

    Hi Kathy!

    Oh ok! got it now! I think I know what was my mistake using your code…I’ll try it more properly….

    thanks a lot for your clarifications ;)

  27. nolageek on February 16, 2016 at 5:54 am

    Love this – but I have an odd issue – first of all I can’t save values for posts via quick edit. I select multiple items and when I update the alpha field and save – they do not get saved.

    Secondly, I accidentally cleared out all values of alpha for 435 posts – when I try to run the kia_run_once function again (to reassign all of the posts to the alpha taxonomy) nothing happens. I’ve tried modifying it so that it’s a different function, with a different set_transient value, etc.. but nothing. Any ideas?

    • kathy on February 17, 2016 at 2:07 pm

      Hi there, I’m glad you like this post. Unfortunately, it’s pretty old so I’m no longer super familiar with it. It doesn’t have quick edit support, someone would need to write that. As for the run once function you should be able to simply delete the transient delete_transient( 'kia_run_once' ); and on the next load it would run.

  28. Andrea on June 7, 2016 at 9:26 am

    Hi, Thanks for your precious article, I have a big problem to solve.
    In my site there are different type of archive pages.
    and the archive.php is not the right one.
    I need to redirect the plug in to a specific archive.php file. How can I do that?

    Thank you so much

    • Andrea on June 7, 2016 at 11:03 am

      Nevermind I just solved the problem creating a taxonomy-[taxonomy-slug].php file
      :)

Leave a Comment