Sorting post according to post tag in WordPress

It's been quite a while since i write new post on Hungred Dot Com as i got stuck with my Malay language examination (sigh..) and other project that i'm working on. And recently i was trying to sort one of my WordPress theme according to its post tag and found that there is no easy way to sort your post tag in prioritize or customization ways. Although i manage to make it work on the WordPress theme. It still required additional query to the database although i tried to minimized it. Well, i guess you will have to pay the price for things that WordPress doesn't support i guess. Nonetheless, this is the optimized version of how i get it done on my WordPress theme.

Getting Started

Before i start, let me explain what i am sorting here. Basically, i have only two post tag which are 'sold out' and 'available' and i'm sorting it according to this sequence.

  1. Sticky post
  2. Available tag
  3. Sold out tag
  4. Others

The objective is pretty simple but working on the code require a bit more work than i though. Let's see how do i simplify the explanation so that all of us can understand. I'll just split them in 4 section as shown on my sorting sequence. Let's pray that we all get it. (hahaha..)

Sorting sticky post

The starting sort which is also the one who will initialize all variables (so that we don't enquire the database twice) is located on the first sorting sequence.

<?php
global $wp_query;
$tmp = $wp_query->query;	// backup the original query
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;	//determine the current page number
$sticky = get_option('sticky_posts');	//get all sticky post from db
$num2display = get_option('posts_per_page');	//get post per page from db
$numHadDisplay = 0;		//display post counter
$maxnumofpost = 0;		//maximum post counter
$args = array(
	'post__in'  => $sticky, // we only want to see sticky post
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => $paged,
	'showposts' => ($num2display),
	'offset' => (($paged-1)*$num2display)	//we need to tell our query the offset so that it will display correctly on subsequence page
);
 query_posts(array_merge($args,$wp_query->query));	//merge my query with the original one (don't have to do this, its up to you)
 $numHadDisplay += $wp_query->post_count;	//increment the post that has displayed
 $maxnumofpost += $wp_query->found_posts;	//increment the maximum post on this section

I have commented above so that you guys can figure out what the heck am i doing. I'm basically initializing all the things i need on the top to avoid redundant call to the database. Once you have finish this, the next bit of code is to print them out on the screen. This bits of code are the same for all print out after the logic has been applied.

	<?php if (have_posts()) : ?>
		<?php while (have_posts()) : the_post(); ?>
			<div class="post" id="post-<?php the_ID(); ?>">
				<div class="commentP">
					<div class="commentC">
					<?php comments_popup_link('0', '1', '%'); ?>
					</div>
				</div>

				<h2 class="home_title"><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
				<p class="byline"><small>
					by <?php the_author() ?>, 				<?php the_time('F jS') ?> • 				<?php the_category(' •  ') ?> • 				<?php edit_post_link('Edit', '', '  •  '); ?>
				</small></p>
				<div class="entry">
					<?php the_content('<div id="moretag">MORE »</div>');?>
				</div>
				<p class="tags"><?php the_tags('Item Status: ', ', ', '<br />'); ?></p>
			</div>
		<?php endwhile; ?>
	<?php endif; ?>

It's just a loop to print out everything. It should be different from your theme. (looping should be the same though)

Sorting available post tag

NOW, i'm hoping that the first sorting part doesn't confuse you. If it does, then you should stop reading and start thinking of your own logic. But it should be alright for most of you. The next page should get a bit more challenging.

<?php
if($numHadDisplay < $num2display){	//check whether the previous tag post has enough to print (too little, we will need to help to fill up the gap
if($maxnumofpost%$num2display == 0)	//we will need to have decimal to determine the correct page
$maxnumofpost++;
$maxpage = ceil($maxnumofpost/$num2display);	//find the maximum page the previous tag post sections can get
$offset = 0;
if(($paged-$maxpage) == 0)			//although we are at n page where n can be any number, this section might just run the first time. Hence, first page.
$offset = 0;
else if($paged >= 1)		//subsequence page we will calculate the number of post we have printed to fill up the grap on the previous page PLUS the number that had already display to get the starting index
$offset = ($num2display-($maxnumofpost)%$num2display) + ((($paged-$maxpage) -1 )*$num2display);

$args = array(
	'tag'=> "available",
	'post__not_in'  => $sticky,
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => ($paged-$maxpage),	// this is the starting page (we do not follow the current page because each section start page is different)
	'showposts' => ($num2display - $numHadDisplay),	//show this amount of post
	'caller_get_posts'=>1,	//do not show any sticky post
	'offset' => $offset		//get the record after this offset
);
 query_posts($args);
 $numHadDisplay += $wp_query->post_count;
 $maxnumofpost += $wp_query->found_posts;
?>

The above is pretty confusing but this is all the logic we need for all subsequence section. In here, you must understand that we are trying to prioritize each tag to display first. Hence, each level in your priority list will initialize at different time. Therefore, we need to calculate what is the starting offset of that part, number of post to display and the correct page to display at that time.

After the above logic has been applied, you will perform another print out on the screen as shown previously.

Sorting sold out post tag

Like i mention on the previous section. That is all the logic we need. Hence, the code for this section differ only at the query level.

<?php
if($numHadDisplay < $num2display){
if($maxnumofpost%$num2display == 0)
$maxnumofpost++;
$maxpage = ceil($maxnumofpost/$num2display);
global $wp_query;
$offset = 0;
if(($paged-$maxpage) == 0)
$offset = 0;
else if($paged >= 1)
$offset = ($num2display-($maxnumofpost)%$num2display) + ((($paged-$maxpage) -1 )*$num2display);

$args = array(
	'tag'=> "sold-out", // it's sold out instead
	'post__not_in'  => $sticky,
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => ($paged-$maxpage),
	'showposts' => ($num2display - $numHadDisplay),
	'caller_get_posts'=>1,
	'offset' => $offset
);
 query_posts($args);
 $numHadDisplay += $wp_query->post_count;
 $maxnumofpost += $wp_query->found_posts;
?>

Then, we will just output all the things out as usual as shown on the sticky section.

Sorting other post

Once, we display all the tag we want, other not so important post will be thrown at the back.

<?php
if($numHadDisplay < $num2display){
if($maxnumofpost%$num2display == 0)
$maxnumofpost++;
global $wp_query;
$maxpage = ceil($maxnumofpost/$num2display);
$soldout = get_term_by('slug','sold-out','post_tag');
$available = get_term_by('slug','available','post_tag');
$offset = 0;
if(($paged-$maxpage) == 0)
$offset = 0;
else if($paged >= 1)
$offset = ($num2display-($maxnumofpost)%$num2display) + ((($paged-$maxpage) -1 )*$num2display);

// echo "max page = ". $maxpage . "<br/>";
// echo "previous printed = " . ($num2display-($maxnumofpost)%$num2display) . "<br/>";
// echo "added to printed = ". ((($paged-$maxpage) -1 )*$num2display) . "<br/>";
// echo "max num of post = ".$maxnumofpost . "<br/>";
// echo "page = ". ($paged-$maxpage) . "<br/>";
// echo "offset = ". ($offset) . "<br/>";
// echo "showposts = ".  ($num2display - $numHadDisplay) . "<br/>";

$args = array(
	'post__not_in'  => $sticky, // do not display sticky post
	'tag__not_in'=> array($available->term_id, $soldout->term_id), // do not display sold out and available tag post
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => ($paged-$maxpage),
	'showposts' => ($num2display - $numHadDisplay),
	'caller_get_posts'=>1,
	'offset' => $offset
);
 query_posts(array_merge($args));
 $numHadDisplay += $wp_query->post_count;
 $maxnumofpost += $wp_query->found_posts;
?>

As usual, print them out once the above has initialize.

Fix pagination

Once you have done all that, you will notice that your WordPress theme page is wrong as it takes the last query total page. Hence, you will need to call the backup query that we did previous to display the proper maximum page our site have.

<?php  query_posts($tmp); ?>

That's it!

Full Sorting code

In case you can't follow without the full code. Here is the one i have 🙂

<?php get_header(); ?>
<!-- start #content -->
<div id="page">
	<div id="content">
<?php
global $wp_query;
$tmp = $wp_query->query;	// backup the original query
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;	//determine the current page number
$sticky = get_option('sticky_posts');	//get all sticky post from db
$num2display = get_option('posts_per_page');	//get post per page from db
$numHadDisplay = 0;		//display post counter
$maxnumofpost = 0;		//maximum post counter
$args = array(
	'post__in'  => $sticky,
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => $paged,
	'showposts' => ($num2display),
	'offset' => (($paged-1)*$num2display)	//start query
);
 query_posts(array_merge($args,$wp_query->query));	//merge my query with the original one (don't have to do this, its up to you)
 $numHadDisplay += $wp_query->post_count;	//increment the post that has displayed
 $maxnumofpost += $wp_query->found_posts;	//increment the maximum post on this section
?>
	<?php if (have_posts()) : ?>
		<?php while (have_posts()) : the_post(); ?>
			<div class="post" id="post-<?php the_ID(); ?>">
				<div class="commentP">
					<div class="commentC">
					<?php comments_popup_link('0', '1', '%'); ?>
					</div>
				</div>

				<h2 class="home_title"><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
				<p class="byline"><small>
					by <?php the_author() ?>, 				<?php the_time('F jS') ?> • 				<?php the_category(' •  ') ?> • 				<?php edit_post_link('Edit', '', '  •  '); ?>
				</small></p>
				<div class="entry">
					<?php the_content('<div id="moretag">MORE »</div>');?>
				</div>
				<p class="tags"><?php the_tags('Item Status: ', ', ', '<br />'); ?></p>
			</div>
		<?php endwhile; ?>
	<?php endif; ?>
<?php
if($numHadDisplay < $num2display){	//check whether the previous tag post has enough to print (too little, we will need to help to fill up the gap
if($maxnumofpost%$num2display == 0)	//we will need to have decimal to determine the correct page
$maxnumofpost++;
$maxpage = ceil($maxnumofpost/$num2display);	//find the maximum page the previous tag post sections can get
$offset = 0;
if(($paged-$maxpage) == 0)			//although we are at n page where n can be any number, this section might just run the first time. Hence, first page.
$offset = 0;
else if($paged >= 1)		//subsequence page we will calculate the number of post we have printed to fill up the grap on the previous page PLUS the number that had already display to get the starting index
$offset = ($num2display-($maxnumofpost)%$num2display) + ((($paged-$maxpage) -1 )*$num2display);

$args = array(
	'tag'=> "available",
	'post__not_in'  => $sticky,
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => ($paged-$maxpage),	// this is the starting page (we do not follow the current page because each section start page is different)
	'showposts' => ($num2display - $numHadDisplay),	//show this amount of post
	'caller_get_posts'=>1,	//do not show any sticky post
	'offset' => $offset		//get the record after this offset
);
 query_posts($args);
 $numHadDisplay += $wp_query->post_count;
 $maxnumofpost += $wp_query->found_posts;
?>
	<?php if (have_posts()) : ?>
		<?php while (have_posts()) : the_post(); ?>
			<div class="post" id="post-<?php the_ID(); ?>">
				<div class="commentP">
					<div class="commentC">
					<?php comments_popup_link('0', '1', '%'); ?>
					</div>
				</div>

				<h2 class="home_title"><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
				<p class="byline"><small>
					by <?php the_author() ?>, 				<?php the_time('F jS') ?> • 				<?php the_category(' •  ') ?> • 				<?php edit_post_link('Edit', '', '  •  '); ?>
				</small></p>
				<div class="entry">
					<?php the_content('<div id="moretag">MORE »</div>');?>
				</div>
				<p class="tags"><?php the_tags('Item Status: ', ', ', '<br />'); ?></p>
			</div>
		<?php endwhile; ?>
	<?php endif; ?>
<?php
}
if($numHadDisplay < $num2display){
if($maxnumofpost%$num2display == 0)
$maxnumofpost++;
$maxpage = ceil($maxnumofpost/$num2display);
global $wp_query;
$offset = 0;
if(($paged-$maxpage) == 0)
$offset = 0;
else if($paged >= 1)
$offset = ($num2display-($maxnumofpost)%$num2display) + ((($paged-$maxpage) -1 )*$num2display);

$args = array(
	'tag'=> "sold-out",
	'post__not_in'  => $sticky,
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => ($paged-$maxpage),
	'showposts' => ($num2display - $numHadDisplay),
	'caller_get_posts'=>1,
	'offset' => $offset
);
 query_posts($args);
 $numHadDisplay += $wp_query->post_count;
 $maxnumofpost += $wp_query->found_posts;
?>
	<?php if (have_posts()) : ?>
		<?php while (have_posts()) : the_post(); ?>
			<div class="post" id="post-<?php the_ID(); ?>">
				<div class="commentP">
					<div class="commentC">
					<?php comments_popup_link('0', '1', '%'); ?>
					</div>
				</div>

				<h2 class="home_title"><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
				<p class="byline"><small>
					by <?php the_author() ?>, 				<?php the_time('F jS') ?> • 				<?php the_category(' •  ') ?> • 				<?php edit_post_link('Edit', '', '  •  '); ?>
				</small></p>
				<div class="entry">
					<?php the_content('<div id="moretag">MORE »</div>');?>
				</div>
				<p class="tags"><?php the_tags('Item Status: ', ', ', '<br />'); ?></p>
			</div>
		<?php endwhile; ?>
	<?php endif; ?>
<?php
}
if($numHadDisplay < $num2display){
if($maxnumofpost%$num2display == 0)
$maxnumofpost++;
global $wp_query;
$maxpage = ceil($maxnumofpost/$num2display);
$soldout = get_term_by('slug','sold-out','post_tag');
$available = get_term_by('slug','available','post_tag');
$offset = 0;
if(($paged-$maxpage) == 0)
$offset = 0;
else if($paged >= 1)
$offset = ($num2display-($maxnumofpost)%$num2display) + ((($paged-$maxpage) -1 )*$num2display);

// echo "max page = ". $maxpage . "<br/>";
// echo "previous printed = " . ($num2display-($maxnumofpost)%$num2display) . "<br/>";
// echo "added to printed = ". ((($paged-$maxpage) -1 )*$num2display) . "<br/>";
// echo "max num of post = ".$maxnumofpost . "<br/>";
// echo "page = ". ($paged-$maxpage) . "<br/>";
// echo "offset = ". ($offset) . "<br/>";
// echo "showposts = ".  ($num2display - $numHadDisplay) . "<br/>";

$args = array(
	'post__not_in'  => $sticky,
	'tag__not_in'=> array($available->term_id, $soldout->term_id),
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => ($paged-$maxpage),
	'showposts' => ($num2display - $numHadDisplay),
	'caller_get_posts'=>1,
	'offset' => $offset
);
 query_posts($args);
 $numHadDisplay += $wp_query->post_count;
 $maxnumofpost += $wp_query->found_posts;
?>
	<?php if (have_posts()) : ?>
		<?php while (have_posts()) : the_post(); ?>
			<div class="post" id="post-<?php the_ID(); ?>">
				<div class="commentP">
					<div class="commentC">
					<?php comments_popup_link('0', '1', '%'); ?>
					</div>
				</div>

				<h2 class="home_title"><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
				<p class="byline"><small>
					by <?php the_author() ?>, 				<?php the_time('F jS') ?> • 				<?php the_category(' •  ') ?> • 				<?php edit_post_link('Edit', '', '  •  '); ?>
				</small></p>
				<div class="entry">
					<?php the_content('<div id="moretag">MORE »</div>');?>
				</div>
				<p class="tags"><?php the_tags('Item Status: ', ', ', '<br />'); ?></p>
			</div>
		<?php endwhile; ?>

	<?php else : ?>
		<div class="post">
			<h2 class="center">Not Found</h2>
			<p class="center">Sorry, we can't find the thing you are looking for. Please try to search for it.</p>
		</div>
		<?php include (TEMPLATEPATH . "/searchform.php"); ?>
	<?php endif; ?>
<?php } ?>
<?php  query_posts($tmp); ?>
		<div class="navigation post">
			<div class="alignleft"><?php if(function_exists('wp_pagenavi')) { wp_pagenavi(); } ?>  </div>
		</div>
	</div>
<?php get_sidebar(); ?>
</div>
<!-- end #content -->
<?php get_menu(); ?>
<?php get_footer(); ?>

And if you still not convince that it work, you can have a look at this store.looliwun.com.

Summary

The challenging part on sorting post tag is that you will have to take care of each sequence logic. In this logic that i have, i have only added around 5 WordPress query to get what i want. (let's don't calculate what WordPress did inside, hahaha..) But it depends on what you want, you might try other ways to achieve the same result. Enjoy 🙂

P.S: you can further optimize this by blocking access to each section after there is no post to be displayed for some of the top logic. Example, sticky has 1 post, available have 2 post, sold out have 100 post. In this case, after 2nd page, sticky and available post should have ended but we still enquire the database to check. This can be optimized. 🙂