0

I want to create a date archive were all the existing years containing posts are appearing in a list, together with another list containing the 12 months of the year.

Essentially it would be like this:

2020, 2019, 2018, 2017, 2016

January, February, March, April, May, June, July, August, September, October, November, December

If you click 2019 it will show you all the posts from 2019, and if you click on February it will show you only the February ones. But then you can click on June and show the posts from June 2019. Same with all the other ones.

I managed to show all years and months but I don't want the months to be duplicated for every year but make them work more like a filter.

Here my code so far:

function wp_custom_archive($args = '') {
    global $wpdb, $wp_locale;

    $defaults = array(
        'limit' => '',
        'format' => 'html', 'before' => '',
        'after' => '', 'show_post_count' => false,
        'echo' => 1
    );

    $r = wp_parse_args( $args, $defaults );
    extract( $r, EXTR_SKIP );

    if ( '' != $limit ) {
        $limit = absint($limit);
        $limit = ' LIMIT '.$limit;
    }

    // over-ride general date format ? 0 = no: use the date format set in Options, 1 = yes: over-ride
    $archive_date_format_over_ride = 0;

    // options for daily archive (only if you over-ride the general date format)
    $archive_day_date_format = 'Y/m/d';

    // options for weekly archive (only if you over-ride the general date format)
    $archive_week_start_date_format = 'Y/m/d';
    $archive_week_end_date_format   = 'Y/m/d';

    if ( !$archive_date_format_over_ride ) {
        $archive_day_date_format = get_option('date_format');
        $archive_week_start_date_format = get_option('date_format');
        $archive_week_end_date_format = get_option('date_format');
    }

    //filters
    $where = apply_filters('customarchives_where', "WHERE post_type = 'post' AND post_status = 'publish'", $r );
    $join = apply_filters('customarchives_join', "", $r);

    $output = '<ul>';

        $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date DESC $limit";
        $key = md5($query);
        $cache = wp_cache_get( 'wp_custom_archive' , 'general');
        if ( !isset( $cache[ $key ] ) ) {
            $arcresults = $wpdb->get_results($query);
            $cache[ $key ] = $arcresults;
            wp_cache_set( 'wp_custom_archive', $cache, 'general' );
        } else {
            $arcresults = $cache[ $key ];
        }
        if ( $arcresults ) {
            $afterafter = $after;
            foreach ( (array) $arcresults as $arcresult ) {
                $url = get_month_link( $arcresult->year, $arcresult->month );
                /* translators: 1: month name, 2: 4-digit year */
                $text = sprintf(__('%s'), $wp_locale->get_month($arcresult->month));
                $year_text = sprintf('<li>%d</li>', $arcresult->year);
                if ( $show_post_count )
                    $after = '&nbsp;('.$arcresult->posts.')' . $afterafter;
                $output .= ( $arcresult->year != $temp_year ) ? $year_text : '';
                $output .= get_archives_link($url, $text, $format, $before, $after);

                $temp_year = $arcresult->year;
            }
        }

    $output .= '</ul>';

    if ( $echo )
        echo $output;
    else
        return $output;
}

EDIT:

After managing to make the functionality work, the month filter is 'refreshing' and coming back to the most recent year although redirecting properly to the specific archive page. Would it be possible to get the selected year for the month?

1
  • You don't need custom SQL queries and date math to do this, WordPress already has built in date archives for years/months/days, and a selection of theme templates to render them ( date.php, year.php, etc, etc ), However, if you do this as a dropdown you'll be able to select months in years that do not have posts. – Tom J Nowell Nov 13 '20 at 17:28
1

That's trivial, we can exploit how WordPress URLs work and pass the query vars directly in the URL:

<form action="/" method="GET">
    <select name="year">
        <option value="2017>2017</option>
        ... etc ..
    </select>
    <select name="month">
        <option value="01>01</option>
        ... etc ..
    </select>

    <input type="submit" value="Filter"/>
</form>

WP will then see that it's a date archive, it may even 301 redirect to the appropriate date archive ( my site redirected ?year=2019&m=01 to /2019/?m=01 ).

We can similarly make hyperlink versions with get_year_link and get_month_link.

The important part though, is that if you want a single list of years, and a single list of months, then you must restrict the months to a single year. The user will need to click on the year, and then on that years archive, click on the month.

First, display the years:

wp_get_archives( [
    'type'            => 'yearly',
    'format'          => 'html',
    'show_post_count' => 0,
] );

Then display individual months by specifying the year:

wp_get_archives( [
    'type'            => 'monthly',
    'year'            => is_year() ? get_query_var( 'year' ) : date('Y'),
    'format'          => 'html',
    'show_post_count' => 0,
] );

Finally we can filter the months to just that year with this:

    add_filter( 'getarchives_where', 'wp_get_archives_month_filter', 10, 2 );
    function wp_get_archives_month_filter($where, $args){
        if ( 'monthly' !== $args['type'] ) {
            return $where;
        }
        $year = $args['year'];
        $start = date( 'Y-m-d', strtotime( $year . '-01-01' ) );
        $end = date( 'Y-m-d', strtotime( $year . '-12-31' ) );
        $where.= ' AND `post_date` BETWEEN "' . $start . '" AND "'. $end . '"';
        return $where;
    }
11
  • Thanks for the help. If I implement what you said it shows me duplicated months anyway. Any idea? – Anta Nov 17 '20 at 10:14
  • That should not be the case as there are only 2 calls to wp_get_archives it sounds like you've put these in a loop for some reason, don't do that. If you had followed my answer correctly then duplicate months should not be possible as the months are only displayed once, there is no opportunity for a second month to be displayed. Also note that I'm referring to the wp_get_archives that comes with WordPress, not your custom version – Tom J Nowell Nov 17 '20 at 11:32
  • I understand. I'm not including it in the loop then. But I'm not sure how can I integrate the wp_get_archives in the form so it filters dinamically – Anta Nov 17 '20 at 11:42
  • That's essentially what I want to achieve: alexadagmar.com/archives – Anta Nov 17 '20 at 11:44
  • It already does the filtering, notice how in the monthly archives I passed a year parameter so it knows which year to show the months for. My code is literally creating what that archive shows, it's a complete answer with no missing parts, just those 2 wp_get_archives calls are all that is needed – Tom J Nowell Nov 17 '20 at 11:44

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.