I'm looking to echo the number of posts per month since the blog began, and for months where there was no posts echo '0'.
This is the output I want:
January 1, February 3, March 8, April 3, ...
Any help would be great. Dave
So basically what you need to do is paste the following code in your theme’s sidebar.php file or any other file where you want to display custom WordPress archives.
<?php
global $wpdb;
$limit = 0;
$year_prev = null;
$months = $wpdb->get_results("SELECT DISTINCT MONTH( post_date ) AS month,YEAR( post_date ) AS year, COUNT( id ) as post_count FROM $wpdb->posts WHERE post_status = 'publish' and post_date <= now( ) and post_type = 'post' GROUP BY month , year ORDER BY post_date DESC");
foreach($months as $month) :
$year_current = $month->year;
if ($year_current != $year_prev){
if ($year_prev != null){
?>
<?php } ?>
<li class="archive-year"><a href="<?php bloginfo('url') ?>/<?php echo $month->year; ?>/"><?php echo $month->year; ?></a></li>
<?php } ?>
<li><a href="<?php bloginfo('url') ?>/<?php echo $month->year; ?>/<?php echo date("m", mktime(0, 0, 0, $month->month, 1, $month->year)) ?>"><span class="archive-month"><?php echo date("F", mktime(0, 0, 0, $month->month, 1, $month->year)) ?></span></a></li>
<?php $year_prev = $year_current;
if(++$limit >= 18) { break; }
endforeach; ?>
Note: If you want to change the number of months displayed, then you need to change line 19 where the current $limit value is set to 18.
Our CSS looked a bit like this:
.widget-archive{padding: 0 0 40px 0; float: left; width: 235px;}
.widget-archive ul {margin: 0;}
.widget-archive li {margin: 0; padding: 0;}
.widget-archive li a{ border-left: 1px solid #d6d7d7; padding: 5px 0 3px 10px; margin: 0 0 0 55px; display: block;}
li.archive-year{float: left; font-family: Helvetica, Arial, san-serif; padding: 5px 0 3px 10px; color:#ed1a1c;}
li.archive-year a{color:#ed1a1c; margin: 0; border: 0px; padding: 0;}
Now if you want to show the count of posts in each month, then you would need to add this bit of code anywhere in between line 12 – 16 of the above code:
<?php echo $month->post_count; ?>
Your best bet is probably to use $wpdb
directly. You can use COUNT
and GROUP BY
to make things easier.
A query might look something like this:
<?php
global $wpdb;
$res = $wpdb->get_results("SELECT MONTH(post_date) as post_month, count(ID) as post_count from {$wpdb->posts} WHERE post_status = 'publish' GROUP BY post_month", OBJECT_K);
That gets you most of the way there. Be sure to have a look at the generic results section of the wpdb
docs.
To get you the rest of the way there, you'll likely want to loop through a range of 1-12, creating month names and checking to see if the results include that month.
Here's an example implemented as a shortcode:
<?php
add_action('init', 'wpse60859_register_shortcode');
/**
* Registers the shortcode
*
* @uses add_shortcode
*/
function wpse60859_register_shortcode()
{
add_shortcode(
'posts_per_month',
'wpse60859_shortcode_cb'
);
}
/**
* The shortcode callback function.
*
* Usage:
* [posts_per_month year="2012"]
*
* @uses date_i18n
* @uses shortcode_atts
*/
function wpse60859_shortcode_cb($args)
{
global $wpdb;
$args = shortcode_atts(array(
'year' => false
), $args);
$year = absint($args['year']);
// year is a no go? bail.
if(!$year)
return '';
$res = $wpdb->get_results($wpdb->prepare(
"SELECT MONTH(post_date) AS post_month, count(ID) AS post_count from " .
"{$wpdb->posts} WHERE post_status = 'publish' AND YEAR(post_date) = %d " .
"GROUP BY post_month;", $year
), OBJECT_K);
// We didn't get any results. Something might be wrong?
if(!$res)
return '';
// build the display
$out = '<ul>';
foreach(range(1, 12) as $m)
{
$month = date_i18n('F', mktime(0, 0, 0, $m, 1));
$out .= sprintf(
'<li>%s %d</li>',
$month,
isset($res[$m]) ? $res[$m]->post_count : 0
);
}
$out .= '</ul>';
return $out;
}
The foreach
loop at the end is the one to pay attention to. Loop through a 1-12 range, create a proper month name for each, and see if the post count exists. If it does use that number, or else print 0.
That shortcode as a plugin.
EDIT Display counts for the last 12 months.
This one requires a bit more complex query, but the concept is the same: get post counts, group by month. This time, order by post date ascending. From there we just need to make an array of month numbers based on the current date.
Example (again as a shortcode)
<?php
add_action('init', 'wpse60859_register_shortcode');
/**
* Registers the shortcode
*
* @uses add_shortcode
*/
function wpse60859_register_shortcode()
{
add_shortcode(
'posts_per_month_last',
'wpse60859_shortcode_alt_cb'
);
}
/**
* Callback for displaying the last twelve months of posts
*
* @uses $wpdb
*/
function wpse60859_shortcode_alt_cb()
{
global $wpdb;
$res = $wpdb->get_results(
"SELECT MONTH(post_date) as post_month, COUNT(ID) as post_count " .
"FROM {$wpdb->posts} " .
"WHERE post_date BETWEEN DATE_SUB(NOW(), INTERVAL 12 MONTH) AND NOW() " .
"AND post_status = 'publish' " .
"GROUP BY post_month ORDER BY post_date ASC", OBJECT_K
);
$cur = absint(date('n'));
if($cur > 1)
{
$looper = array_merge(range($cur, 12), range(1, $cur-1));
}
else
{
$looper = range(1, 12);
}
$out = '<ul>';
foreach($looper as $m)
{
$month = date_i18n('F', mktime(0, 0, 0, $m, 1));
$out .= sprintf(
'<li>%s %d</li>',
$month,
isset($res[$m]) ? $res[$m]->post_count : 0
);
}
$out .= '</ul>';
return $out;
}
Thanks for your help, Chris and Varun. I seem to have done it by mostly using Chris's example, and taking a little code from Varuns too.
Here's what I ended up with. I'm not sure if it's the most efficient way of doing this, its more a proof of concept for me but if anyone has a way to do it any cleaner then please let me know.
Thanks all.
global $wpdb;
$res = $wpdb->get_results(
"SELECT MONTH(post_date) as post_month, COUNT(ID) as post_count " .
"FROM {$wpdb->posts} " .
"WHERE post_date BETWEEN DATE_SUB(NOW(), INTERVAL 12 MONTH) AND NOW() AND post_type = 'post' " .
"AND post_status = 'publish' " .
"GROUP BY post_month ORDER BY post_date DESC", OBJECT_K
);
$postCount= 0;
$len = count($looper);
$cur = absint(date('n'));
if($cur > 1)
{
$looper = array_merge(range($cur+1, 12), range(1, $cur));
}
else
{
$looper = range(1, 12);
}
$out = '0,';
$postCount= '0';
$len = count($looper);
foreach($looper as $m)
{
$month = date_i18n('F', mktime(0, 0, 0, $m, 1));
$out .= sprintf(
'%s %d',
$month,
//'',
isset($res[$m]) ? $res[$m]->post_count : 0
);
if ($postCount!= $len-1) {
$out .= ',';
}
$postCount++;
}
//$out .= '</ul>';
echo $out;