Quantcast
Channel: Tutorial Archives - Kathy is Awesome
Viewing all articles
Browse latest Browse all 14

Create an Alphabetical Glossary of Posts in WordPress

$
0
0

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.

// Add new taxonomy, NOT hierarchical (like tags)
function kia_create_glossary_taxonomy(){
    if(!taxonomy_exists('glossary')){
        register_taxonomy('glossary',array('post'),array(
        'show_ui' => false
      ));
     }
}
add_action('init','kia_create_glossary_taxonomy');

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.

/* When the post is saved, saves our custom data */
function kia_save_first_letter( $post_id ) {
// verify if this is an auto save routine.
// If it is our form has not been submitted, so we dont want to do anything
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return;

//check location (only run for posts)
$limitPostTypes = array('post');
if (!in_array($_POST['post_type'], $limitPostTypes)) return;

// Check permissions
if ( !current_user_can( 'edit_post', $post_id ) )
return;

// OK, we're authenticated: we need to find and save the data
$taxonomy = 'glossary';

//set term as first letter of post title, lower case
wp_set_post_terms( $post_id, strtolower(substr($_POST['post_title'], 0, 1)), $taxonomy );

//delete the transient that is storing the alphabet letters
delete_transient( 'kia_archive_alphabet');
}
add_action( 'save_post', 'kia_save_first_letter' );

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, reload your site (probably 2x for good measure) and then delete the function.  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.

//create array from existing posts
function kia_run_once(){
$taxonomy = 'glossary';

$alphabet = array();
$posts = get_posts(array('numberposts' => -1) );

foreach($posts as $p) :
//set term as first letter of post title, lower case
wp_set_post_terms( $p->ID, strtolower(substr($p->post_title, 0, 1)), $taxonomy );
endforeach;
}
add_action('init','kia_run_once');

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.

$taxonomy = 'glossary';

// save the terms that have posts in an array as a transient
if ( false === ( $alphabet = get_transient( 'kia_archive_alphabet' ) ) ) {
    // It wasn't there, so regenerate the data and save the transient
    $terms = get_terms($taxonomy);

    $alphabet = array();
    if($terms){
        foreach ($terms as $term){
            $alphabet[] = $term->slug;
        }
    }
     set_transient( 'kia_archive_alphabet', $alphabet );
}

?>

<div id="archive-menu" class="menu">

	<ul id="alphabet-menu">

	<?php 
	
	foreach(range('a', 'z') as $i) :              

		$current = ($i == get_query_var($taxonomy)) ? "current-menu-item" : "menu-item";              

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

	endforeach; 
	
	?>
	</ul>

</div>

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 improvememts you make!


Viewing all articles
Browse latest Browse all 14

Trending Articles