wp_get_archives( string|array $args =  ): void|string

Displays archive links based on type and format.

Description

See also

Parameters

$argsstring|arrayoptional
Default archive links arguments. Optional.
  • type string
    Type of archive to retrieve. Accepts 'daily', 'weekly', 'monthly', 'yearly', 'postbypost', or 'alpha'. Both 'postbypost' and 'alpha' display the same archive link list as well as post titles instead of displaying dates. The difference between the two is that 'alpha' will order by post title and 'postbypost' will order by post date.
    Default 'monthly'.
  • limit string|int
    Number of links to limit the query to. Default empty (no limit).
  • format string
    Format each link should take using the $before and $after args.
    Accepts 'link' (<link> tag), 'option' (<option> tag), 'html' (<li> tag), or a custom format, which generates a link anchor with $before preceding and $after succeeding. Default 'html'.
  • before string
    Markup to prepend to the beginning of each link.
  • after string
    Markup to append to the end of each link.
  • show_post_count bool
    Whether to display the post count alongside the link. Default false.
  • echo bool|int
    Whether to echo or return the links list. Default 1|true to echo.
  • order string
    Whether to use ascending or descending order. Accepts 'ASC', or 'DESC'.
    Default 'DESC'.
  • post_type string
    Post type. Default 'post'.
  • year string
    Year. Default current year.
  • monthnum string
    Month number. Default current month number.
  • day string
    Day. Default current day.
  • w string
    Week. Default current week.

Default:''

Return

void|string Void if 'echo' argument is true, archive links if 'echo' is false.

Source

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

	$defaults = array(
		'type'            => 'monthly',
		'limit'           => '',
		'format'          => 'html',
		'before'          => '',
		'after'           => '',
		'show_post_count' => false,
		'echo'            => 1,
		'order'           => 'DESC',
		'post_type'       => 'post',
		'year'            => get_query_var( 'year' ),
		'monthnum'        => get_query_var( 'monthnum' ),
		'day'             => get_query_var( 'day' ),
		'w'               => get_query_var( 'w' ),
	);

	$parsed_args = wp_parse_args( $args, $defaults );

	$post_type_object = get_post_type_object( $parsed_args['post_type'] );
	if ( ! is_post_type_viewable( $post_type_object ) ) {
		return;
	}

	$parsed_args['post_type'] = $post_type_object->name;

	if ( '' === $parsed_args['type'] ) {
		$parsed_args['type'] = 'monthly';
	}

	if ( ! empty( $parsed_args['limit'] ) ) {
		$parsed_args['limit'] = absint( $parsed_args['limit'] );
		$parsed_args['limit'] = ' LIMIT ' . $parsed_args['limit'];
	}

	$order = strtoupper( $parsed_args['order'] );
	if ( 'ASC' !== $order ) {
		$order = 'DESC';
	}

	// This is what will separate dates on weekly archive links.
	$archive_week_separator = '&#8211;';

	$sql_where = $wpdb->prepare( "WHERE post_type = %s AND post_status = 'publish'", $parsed_args['post_type'] );

	/**
	 * Filters the SQL WHERE clause for retrieving archives.
	 *
	 * @since 2.2.0
	 *
	 * @param string $sql_where   Portion of SQL query containing the WHERE clause.
	 * @param array  $parsed_args An array of default arguments.
	 */
	$where = apply_filters( 'getarchives_where', $sql_where, $parsed_args );

	/**
	 * Filters the SQL JOIN clause for retrieving archives.
	 *
	 * @since 2.2.0
	 *
	 * @param string $sql_join    Portion of SQL query containing JOIN clause.
	 * @param array  $parsed_args An array of default arguments.
	 */
	$join = apply_filters( 'getarchives_join', '', $parsed_args );

	$output = '';

	$last_changed = wp_cache_get_last_changed( 'posts' );

	$limit = $parsed_args['limit'];

	if ( 'monthly' === $parsed_args['type'] ) {
		$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 $order $limit";
		$key     = md5( $query );
		$key     = "wp_get_archives:$key:$last_changed";
		$results = wp_cache_get( $key, 'post-queries' );
		if ( ! $results ) {
			$results = $wpdb->get_results( $query );
			wp_cache_set( $key, $results, 'post-queries' );
		}
		if ( $results ) {
			$after = $parsed_args['after'];
			foreach ( (array) $results as $result ) {
				$url = get_month_link( $result->year, $result->month );
				if ( 'post' !== $parsed_args['post_type'] ) {
					$url = add_query_arg( 'post_type', $parsed_args['post_type'], $url );
				}
				/* translators: 1: Month name, 2: 4-digit year. */
				$text = sprintf( __( '%1$s %2$d' ), $wp_locale->get_month( $result->month ), $result->year );
				if ( $parsed_args['show_post_count'] ) {
					$parsed_args['after'] = '&nbsp;(' . $result->posts . ')' . $after;
				}
				$selected = is_archive() && (string) $parsed_args['year'] === $result->year && (string) $parsed_args['monthnum'] === $result->month;
				$output  .= get_archives_link( $url, $text, $parsed_args['format'], $parsed_args['before'], $parsed_args['after'], $selected );
			}
		}
	} elseif ( 'yearly' === $parsed_args['type'] ) {
		$query   = "SELECT YEAR(post_date) AS `year`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date) ORDER BY post_date $order $limit";
		$key     = md5( $query );
		$key     = "wp_get_archives:$key:$last_changed";
		$results = wp_cache_get( $key, 'post-queries' );
		if ( ! $results ) {
			$results = $wpdb->get_results( $query );
			wp_cache_set( $key, $results, 'post-queries' );
		}
		if ( $results ) {
			$after = $parsed_args['after'];
			foreach ( (array) $results as $result ) {
				$url = get_year_link( $result->year );
				if ( 'post' !== $parsed_args['post_type'] ) {
					$url = add_query_arg( 'post_type', $parsed_args['post_type'], $url );
				}
				$text = sprintf( '%d', $result->year );
				if ( $parsed_args['show_post_count'] ) {
					$parsed_args['after'] = '&nbsp;(' . $result->posts . ')' . $after;
				}
				$selected = is_archive() && (string) $parsed_args['year'] === $result->year;
				$output  .= get_archives_link( $url, $text, $parsed_args['format'], $parsed_args['before'], $parsed_args['after'], $selected );
			}
		}
	} elseif ( 'daily' === $parsed_args['type'] ) {
		$query   = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, DAYOFMONTH(post_date) AS `dayofmonth`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date), DAYOFMONTH(post_date) ORDER BY post_date $order $limit";
		$key     = md5( $query );
		$key     = "wp_get_archives:$key:$last_changed";
		$results = wp_cache_get( $key, 'post-queries' );
		if ( ! $results ) {
			$results = $wpdb->get_results( $query );
			wp_cache_set( $key, $results, 'post-queries' );
		}
		if ( $results ) {
			$after = $parsed_args['after'];
			foreach ( (array) $results as $result ) {
				$url = get_day_link( $result->year, $result->month, $result->dayofmonth );
				if ( 'post' !== $parsed_args['post_type'] ) {
					$url = add_query_arg( 'post_type', $parsed_args['post_type'], $url );
				}
				$date = sprintf( '%1$d-%2$02d-%3$02d 00:00:00', $result->year, $result->month, $result->dayofmonth );
				$text = mysql2date( get_option( 'date_format' ), $date );
				if ( $parsed_args['show_post_count'] ) {
					$parsed_args['after'] = '&nbsp;(' . $result->posts . ')' . $after;
				}
				$selected = is_archive() && (string) $parsed_args['year'] === $result->year && (string) $parsed_args['monthnum'] === $result->month && (string) $parsed_args['day'] === $result->dayofmonth;
				$output  .= get_archives_link( $url, $text, $parsed_args['format'], $parsed_args['before'], $parsed_args['after'], $selected );
			}
		}
	} elseif ( 'weekly' === $parsed_args['type'] ) {
		$week    = _wp_mysql_week( '`post_date`' );
		$query   = "SELECT DISTINCT $week AS `week`, YEAR( `post_date` ) AS `yr`, DATE_FORMAT( `post_date`, '%Y-%m-%d' ) AS `yyyymmdd`, count( `ID` ) AS `posts` FROM `$wpdb->posts` $join $where GROUP BY $week, YEAR( `post_date` ) ORDER BY `post_date` $order $limit";
		$key     = md5( $query );
		$key     = "wp_get_archives:$key:$last_changed";
		$results = wp_cache_get( $key, 'post-queries' );
		if ( ! $results ) {
			$results = $wpdb->get_results( $query );
			wp_cache_set( $key, $results, 'post-queries' );
		}
		$arc_w_last = '';
		if ( $results ) {
			$after = $parsed_args['after'];
			foreach ( (array) $results as $result ) {
				if ( $result->week != $arc_w_last ) {
					$arc_year       = $result->yr;
					$arc_w_last     = $result->week;
					$arc_week       = get_weekstartend( $result->yyyymmdd, get_option( 'start_of_week' ) );
					$arc_week_start = date_i18n( get_option( 'date_format' ), $arc_week['start'] );
					$arc_week_end   = date_i18n( get_option( 'date_format' ), $arc_week['end'] );
					$url            = add_query_arg(
						array(
							'm' => $arc_year,
							'w' => $result->week,
						),
						home_url( '/' )
					);
					if ( 'post' !== $parsed_args['post_type'] ) {
						$url = add_query_arg( 'post_type', $parsed_args['post_type'], $url );
					}
					$text = $arc_week_start . $archive_week_separator . $arc_week_end;
					if ( $parsed_args['show_post_count'] ) {
						$parsed_args['after'] = '&nbsp;(' . $result->posts . ')' . $after;
					}
					$selected = is_archive() && (string) $parsed_args['year'] === $result->yr && (string) $parsed_args['w'] === $result->week;
					$output  .= get_archives_link( $url, $text, $parsed_args['format'], $parsed_args['before'], $parsed_args['after'], $selected );
				}
			}
		}
	} elseif ( ( 'postbypost' === $parsed_args['type'] ) || ( 'alpha' === $parsed_args['type'] ) ) {
		$orderby = ( 'alpha' === $parsed_args['type'] ) ? 'post_title ASC ' : 'post_date DESC, ID DESC ';
		$query   = "SELECT * FROM $wpdb->posts $join $where ORDER BY $orderby $limit";
		$key     = md5( $query );
		$key     = "wp_get_archives:$key:$last_changed";
		$results = wp_cache_get( $key, 'post-queries' );
		if ( ! $results ) {
			$results = $wpdb->get_results( $query );
			wp_cache_set( $key, $results, 'post-queries' );
		}
		if ( $results ) {
			foreach ( (array) $results as $result ) {
				if ( '0000-00-00 00:00:00' !== $result->post_date ) {
					$url = get_permalink( $result );
					if ( $result->post_title ) {
						/** This filter is documented in wp-includes/post-template.php */
						$text = strip_tags( apply_filters( 'the_title', $result->post_title, $result->ID ) );
					} else {
						$text = $result->ID;
					}
					$selected = get_the_ID() === $result->ID;
					$output  .= get_archives_link( $url, $text, $parsed_args['format'], $parsed_args['before'], $parsed_args['after'], $selected );
				}
			}
		}
	}

	if ( $parsed_args['echo'] ) {
		echo $output;
	} else {
		return $output;
	}
}

Hooks

apply_filters( ‘getarchives_join’, string $sql_join, array $parsed_args )

Filters the SQL JOIN clause for retrieving archives.

apply_filters( ‘getarchives_where’, string $sql_where, array $parsed_args )

Filters the SQL WHERE clause for retrieving archives.

apply_filters( ‘the_title’, string $post_title, int $post_id )

Filters the post title.

Changelog

VersionDescription
5.2.0The $year, $monthnum, $day, and $w arguments were added.
4.4.0The $post_type argument was added.
1.2.0Introduced.

User Contributed Notes

  1. Skip to note 10 content

    To filter a specific category/term, we have a specific case to add filters. It works in case we wish to change archive link based on get_query_var(‘news_category’).

    For example, on same template we have:

    – News Category #1 has dropdown of 2019, 2018, 2017.
    – News Category #2 has dropdown of 2017.

    If the user access /news/news-category-2/2019, it will return no posts. We should disable an archive link 2018, 2019 if a current category is News Category.

    The below code work with Taxonomy “news_category” and custom post type “news”. To work with default post, you should change back to Taxonomy “category” and default post “post”.

    In templates/custom-archive-template.php

      add_filter( 'getarchives_where', 'custom_archive_by_category_where' );
      add_filter( 'getarchives_join', 'custom_archive_by_category_join' );
    
      $args = array();
    
      wp_get_archives(
        array(
          'type'            => 'yearly',
          'format'          => 'option',
          'post_type'       => 'news',
        )
      );
    
      remove_filter( 'getarchives_where', 'custom_archive_by_category_where' );
      remove_filter( 'getarchives_join', 'custom_archive_by_category_join' );

    In functions.php:

    function custom_archive_by_category_join( $x ) {
        global $wpdb;
        return $x . " INNER JOIN $wpdb->term_relationships ON ($wpdb->posts.ID = $wpdb->term_relationships.object_id) INNER JOIN $wpdb->term_taxonomy ON ($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)";
    }
    
    function custom_archive_by_category_where($x) {
        global $wpdb;
        $current_term_slug = get_query_var( 'news_category' );
    
        if (!empty($current_term_slug)) {
            $current_term = get_term_by('slug', $current_term_slug, 'news_category');
    
            if (is_wp_error($current_term) ) {
                return $x;
            }
    
            $current_term_id = $current_term->term_id;
    
            return $x . " AND $wpdb->term_taxonomy.taxonomy = 'news_category' AND $wpdb->term_taxonomy.term_id IN ($current_term_id)";
    
        }
    
        return $x;
    }

    Read more about https://developer.wordpress.org/reference/hooks/getarchives_where/ and https://developer.wordpress.org/reference/hooks/getarchives_join/

  2. Skip to note 12 content

    Dropdown Box
    Displays a dropdown menu of monthly archives, in option tags, with the post count (uses JavaScript to navigate to the selected archive page).

    <select name="archive-dropdown" onchange="document.location.href=this.options[this.selectedIndex].value;">
    	<option value=""><?php esc_attr( _e( 'Select Month', 'textdomain' ) ); ?></option> 
    	<?php wp_get_archives( array( 'type' => 'monthly', 'format' => 'option', 'show_post_count' => 1 ) ); ?>
    </select>
  3. Skip to note 13 content

    To return an array of years with name and value only like:

    array(
       array(
          'name' => 'January 2020',
          'value' => 'http://demo.com/2020/01/'
       ),
       array(
          'name' => 'February 2020',
          'value' => 'http://demo.com/2020/02/'
       )
    )

    We must capture using echo=false&format=custom:

    function theme_name_get_year_archive_array() {
      $years = array();
      $years_args = array(
        'type' => 'monthly',
        'format' => 'custom', // My advise: WordPress Core shoud be add support "format=array" to keep it easy to catch for many custom cases
        'before' => '',
        'after' => '|',
        'echo' => false,
        'post_type' => 'news',
        'order' => 'ASC'
      );
    
      // Get Years
      $years_content = wp_get_archives($years_args);
      if (!empty($years_content) ) {
        $years_arr = explode('|', $years_content);
        $years_arr = array_filter($years_arr, function($item) {
          return trim($item) !== '';
        }); // Remove empty whitespace item from array
    
        foreach($years_arr as $year_item) {
          $year_row = trim($year_item);
          preg_match('/href=["\']?([^"\'>]+)["\']>(.+)<\/a>/', $year_row, $year_vars);
    
          if (!empty($year_vars)) {
            $years[] = array(
              'name' => $year_vars[2], // Ex: January 2020
              'value' => $year_vars[1] // Ex: http://demo.com/2020/01/
            );
          }
        }
      }
    
      return $years;
    }
  4. Skip to note 16 content

    Display the titles of the last 20 posts
    Uses custom as the value for the format argument and specifies before and after values (so, in this example, prints the titles within span tags and separated by commas):

    <?php wp_get_archives( array( 'type' => 'postbypost', 'limit' => 20, 'format' => 'custom', 'before' => '<span class="my-post-title">', 'after' => '</span>, ' ) ); ?>
  5. Skip to note 18 content

    An example using custom wrappers on each link item in the archive. Helpful if you are doing special styling that require a more complicated structure.

    <?php
    	$args = array(
    		'type'            => 'monthly',
    		'limit'           => '',
    		'format'          => 'custom',
    		'before'          => '<div class="sub-item">',
    		'after'           => '</div>',
    		'show_post_count' => false,
    		'echo'            => 1,
    		'order'           => 'DESC'
    	);
    	wp_get_archives( $args );
    ?>

You must log in before being able to contribute a note or feedback.