This question is almost what Im trying to achieve. Create a filter for posts based on a custom field.

My custom field is a date and I would like to create a date filter (monthly) based on this custom field for the post listing admin screen.

I'm not using the normal date as this is an historical website I'm creating and some posts are from many years ago.

  • In what format are you storing the dates? – Stephen Harris Jul 3 '12 at 18:17
  • It is being stored as YYYY-mm-dd. – stemie Jul 3 '12 at 18:22

Add a query variable to store the month

So first of all it'll be necessary to create a custom query variable - this will store the month we're after (in 2012/6) format. I'll call it custom_month, but its best practise to prefix it to avoid clashes with other plug-ins:

add_filter('query_vars', 'wpse57344_register_query_vars' );
function wpse57344_register_query_vars( $qvars ){
    //Add these query variables
    $qvars[] = 'custom_month';
    return $qvars;

Add the drop-down

The query variable will now be recognised and any given value stored by WordPress in the main query (global $wp_query).

Next we create the drop-down to go at the top of the post admin table (this can be changed to be any post type).

add_action( 'restrict_manage_posts', 'wpse57344_restrict_posts_by_metavalue' );
function wpse57344_restrict_posts_by_metavalue() {
    global $typenow;
    $months = wpse57344_get_months();
    if ($typenow == 'post') {
        $selected = get_query_var('custom_month');
        $output = "<select style='width:150px' name='custom_month' class='postform'>\n";
        $output .= '<option '.selected($selected,0,false).' value="">'.__('Show All','wpse57344_plugin').'</option>';
        if ( ! empty( $months ) ) {
            foreach ($months as $month):
                $value =esc_attr($month->year.'/'.$month->month);
                $month_dt = new DateTime($month->year.'-'.$month->month.'-01');
                $output .= "<option value='{$value}' ".selected($selected,$value,false).'>'.$month_dt->format('F Y').'</option>';
        $output .= "</select>\n";       
    echo $output;

The above gets an array of months a month object consists of:

year => //the year of the month e.g. 2012
month => //the number of the month, e.g. 6 for July
posts => //number of posts with the date meta value in this month

Obviously wpse57344_get_months() doesn't exist natively - we'll construct it later.

Assuming with an array of months, we create the drop-down with each option having the value of the form yyyy/mm. I've given the form the same name as the query variable we added.

Altering the query

When a month in the drop-down is selected, the month in yyyy/mm is posted as the value for custom_month. Because we've registered this variable name with WordPress we can access it through $query->get('custom_month').

So we check if its empty or not - if its not, then we restrict to posts where their meta value data is in that month. To do that we use a meta query and the BETWEEN operator.

add_action( 'pre_get_posts', 'wpse57351_pre_get_posts' );
function wpse57351_pre_get_posts( $query ) {

    //Only alter query if custom variable is set.
    $month_str = $query->get('custom_month');
    if( !empty($month_str) ){

            //For debugging, uncomment following line

        //Be careful not override any existing meta queries.
        $meta_query = $query->get('meta_query');
        if( empty($meta_query) )
            $meta_query = array();

        //Convert 2012/05 into a datetime object get the first and last days of that month in yyyy/mm/dd format
        $month = new DateTime($month_str.'/01');

        //Get posts with date between the first and last of given month
        $meta_query[] = array(
            'key' => 'customdate',
            'value' => array($month->format('Y/m/d'),$month->format('Y/m/t')),
            'compare' => 'BETWEEN',

            //For debugging, uncomment following line

Get the months

It remains then to define th function wpse57344_get_months(). We need to query the postmeta table and pick out distinct months from the dates in your posts' meta. In the following I assume that your date is stored with key 'customdate' and has format (as indicated in comments) yyyy/mm/dd.

function wpse57344_get_months(){
    global $wpdb;
        $months = wp_cache_get( 'wpse57344_months' );
        if ( false === $months ) {
            $query = "SELECT YEAR(meta_value) AS `year`, MONTH(meta_value) AS `month`, count(post_id) as posts 
                FROM $wpdb->postmeta WHERE meta_key ='customdate'           
                GROUP BY YEAR(meta_value), MONTH(meta_value) ORDER BY meta_value DESC";
            $months = $wpdb->get_results($query);
            wp_cache_set( 'wpse57344_months', $months );
        return $months;

This returns an array of month objects in the desired format.

  • Thanks Stephen, its almost working now except when I filter by the custom date non of the posts appear for that month. It is recognising the custom field though, as it appears with the correct years and months in the drop down box for custom date. – stemie Jul 4 '12 at 9:14
  • Thanks for editing :). In that case wpse57351_pre_get_posts is not doing its job - but this worked for me, so maybe you've not changed some the variable names? You can do var_dump($query) inside the conditional to debug (i.e. is custom_month set, if so to what?) and then again to see if the meta query is appropriately set. – Stephen Harris Jul 4 '12 at 9:29
  • I presume Im being silly. I changed meta_key to the one Im using ie 'historic-date'. I cant make sense of the debugging results link Not sure why wpse57351_pre_get_posts is not doing it job as there are no errors. – stemie Jul 5 '12 at 9:55
  • It's being queried fine, its trying to find dates between 1986/12/01 and 1986/12/31. The problem is probably that the you're storing the dates with -. Just change the / to - in my answer and it should work :) – Stephen Harris Jul 5 '12 at 10:02
  • Ah of course, it was indeed the dashes that solved it, thx bro you rock! – stemie Jul 5 '12 at 12:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.