get_adjacent_post( bool $in_same_term = false, int[]|string $excluded_terms = '', bool $previous = true, string $taxonomy = 'category' ): WP_Post|null|string

Retrieves the adjacent post.

Description

Can either be next or previous post.

Parameters

$in_same_termbooloptional
Whether post should be in the same taxonomy term.

Default:false

$excluded_termsint[]|stringoptional
Array or comma-separated list of excluded term IDs.

Default:''

$previousbooloptional
Whether to retrieve previous post.

Default:true

$taxonomystringoptional
Taxonomy, if $in_same_term is true. Default 'category'.

Default:'category'

Return

WP_Post|null|string Post object if successful. Null if global $post is not set.
Empty string if no corresponding post exists.

Source

function get_adjacent_post( $in_same_term = false, $excluded_terms = '', $previous = true, $taxonomy = 'category' ) {	global $wpdb;	$post = get_post();	if ( ! $post || ! taxonomy_exists( $taxonomy ) ) {	return null;	}	$current_post_date = $post->post_date;	$join = '';	$where = '';	$adjacent = $previous ? 'previous' : 'next';	if ( ! empty( $excluded_terms ) && ! is_array( $excluded_terms ) ) {	// Back-compat, $excluded_terms used to be $excluded_categories with IDs separated by " and ".	if ( str_contains( $excluded_terms, ' and ' ) ) {	_deprecated_argument(	__FUNCTION__,	'3.3.0',	sprintf(	/* translators: %s: The word 'and'. */	__( 'Use commas instead of %s to separate excluded terms.' ),	"'and'"	)	);	$excluded_terms = explode( ' and ', $excluded_terms );	} else {	$excluded_terms = explode( ',', $excluded_terms );	}	$excluded_terms = array_map( 'intval', $excluded_terms );	}	/** * Filters the IDs of terms excluded from adjacent post queries. * * The dynamic portion of the hook name, `$adjacent`, refers to the type * of adjacency, 'next' or 'previous'. * * Possible hook names include: * * - `get_next_post_excluded_terms` * - `get_previous_post_excluded_terms` * * @since 4.4.0 * * @param int[]|string $excluded_terms Array of excluded term IDs. Empty string if none were provided. */	$excluded_terms = apply_filters( "get_{$adjacent}_post_excluded_terms", $excluded_terms );	if ( $in_same_term || ! empty( $excluded_terms ) ) {	if ( $in_same_term ) {	$join .= " INNER JOIN $wpdb->term_relationships AS tr ON p.ID = tr.object_id INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id";	$where .= $wpdb->prepare( 'AND tt.taxonomy = %s', $taxonomy );	if ( ! is_object_in_taxonomy( $post->post_type, $taxonomy ) ) {	return '';	}	$term_array = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'ids' ) );	// Remove any exclusions from the term array to include.	$term_array = array_diff( $term_array, (array) $excluded_terms );	$term_array = array_map( 'intval', $term_array );	if ( ! $term_array || is_wp_error( $term_array ) ) {	return '';	}	$where .= ' AND tt.term_id IN (' . implode( ',', $term_array ) . ')';	}	if ( ! empty( $excluded_terms ) ) {	$where .= " AND p.ID NOT IN ( SELECT tr.object_id FROM $wpdb->term_relationships tr LEFT JOIN $wpdb->term_taxonomy tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id) WHERE tt.term_id IN (" . implode( ',', array_map( 'intval', $excluded_terms ) ) . ') )';	}	}	// 'post_status' clause depends on the current user.	if ( is_user_logged_in() ) {	$user_id = get_current_user_id();	$post_type_object = get_post_type_object( $post->post_type );	if ( empty( $post_type_object ) ) {	$post_type_cap = $post->post_type;	$read_private_cap = 'read_private_' . $post_type_cap . 's';	} else {	$read_private_cap = $post_type_object->cap->read_private_posts;	}	/* * Results should include private posts belonging to the current user, or private posts where the * current user has the 'read_private_posts' cap. */	$private_states = get_post_stati( array( 'private' => true ) );	$where .= " AND ( p.post_status = 'publish'";	foreach ( $private_states as $state ) {	if ( current_user_can( $read_private_cap ) ) {	$where .= $wpdb->prepare( ' OR p.post_status = %s', $state );	} else {	$where .= $wpdb->prepare( ' OR (p.post_author = %d AND p.post_status = %s)', $user_id, $state );	}	}	$where .= ' )';	} else {	$where .= " AND p.post_status = 'publish'";	}	$op = $previous ? '<' : '>';	$order = $previous ? 'DESC' : 'ASC';	/** * Filters the JOIN clause in the SQL for an adjacent post query. * * The dynamic portion of the hook name, `$adjacent`, refers to the type * of adjacency, 'next' or 'previous'. * * Possible hook names include: * * - `get_next_post_join` * - `get_previous_post_join` * * @since 2.5.0 * @since 4.4.0 Added the `$taxonomy` and `$post` parameters. * * @param string $join The JOIN clause in the SQL. * @param bool $in_same_term Whether post should be in the same taxonomy term. * @param int[]|string $excluded_terms Array of excluded term IDs. Empty string if none were provided. * @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true. * @param WP_Post $post WP_Post object. */	$join = apply_filters( "get_{$adjacent}_post_join", $join, $in_same_term, $excluded_terms, $taxonomy, $post );	/** * Filters the WHERE clause in the SQL for an adjacent post query. * * The dynamic portion of the hook name, `$adjacent`, refers to the type * of adjacency, 'next' or 'previous'. * * Possible hook names include: * * - `get_next_post_where` * - `get_previous_post_where` * * @since 2.5.0 * @since 4.4.0 Added the `$taxonomy` and `$post` parameters. * * @param string $where The `WHERE` clause in the SQL. * @param bool $in_same_term Whether post should be in the same taxonomy term. * @param int[]|string $excluded_terms Array of excluded term IDs. Empty string if none were provided. * @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true. * @param WP_Post $post WP_Post object. */	$where = apply_filters( "get_{$adjacent}_post_where", $wpdb->prepare( "WHERE p.post_date $op %s AND p.post_type = %s $where", $current_post_date, $post->post_type ), $in_same_term, $excluded_terms, $taxonomy, $post );	/** * Filters the ORDER BY clause in the SQL for an adjacent post query. * * The dynamic portion of the hook name, `$adjacent`, refers to the type * of adjacency, 'next' or 'previous'. * * Possible hook names include: * * - `get_next_post_sort` * - `get_previous_post_sort` * * @since 2.5.0 * @since 4.4.0 Added the `$post` parameter. * @since 4.9.0 Added the `$order` parameter. * * @param string $order_by The `ORDER BY` clause in the SQL. * @param WP_Post $post WP_Post object. * @param string $order Sort order. 'DESC' for previous post, 'ASC' for next. */	$sort = apply_filters( "get_{$adjacent}_post_sort", "ORDER BY p.post_date $order LIMIT 1", $post, $order );	$query = "SELECT p.ID FROM $wpdb->posts AS p $join $where $sort";	$key = md5( $query );	$last_changed = wp_cache_get_last_changed( 'posts' );	if ( $in_same_term || ! empty( $excluded_terms ) ) {	$last_changed .= wp_cache_get_last_changed( 'terms' );	}	$cache_key = "adjacent_post:$key:$last_changed";	$result = wp_cache_get( $cache_key, 'post-queries' );	if ( false !== $result ) {	if ( $result ) {	$result = get_post( $result );	}	return $result;	}	$result = $wpdb->get_var( $query );	if ( null === $result ) {	$result = '';	}	wp_cache_set( $cache_key, $result, 'post-queries' );	if ( $result ) {	$result = get_post( $result );	}	return $result; } 

Hooks

apply_filters( “get_{$adjacent}_post_excluded_terms”, int[]|string $excluded_terms )

Filters the IDs of terms excluded from adjacent post queries.

apply_filters( “get_{$adjacent}_post_join”, string $join, bool $in_same_term, int[]|string $excluded_terms, string $taxonomy, WP_Post $post )

Filters the JOIN clause in the SQL for an adjacent post query.

apply_filters( “get_{$adjacent}_post_sort”, string $order_by, WP_Post $post, string $order )

Filters the ORDER BY clause in the SQL for an adjacent post query.

apply_filters( “get_{$adjacent}_post_where”, string $where, bool $in_same_term, int[]|string $excluded_terms, string $taxonomy, WP_Post $post )

Filters the WHERE clause in the SQL for an adjacent post query.

Changelog

VersionDescription
2.5.0Introduced.

User Contributed Notes

  1. Skip to note 3 content

    Examples
    Link to previous post in the same taxonomy with Post Title for text.

     <?php $prev_post = get_adjacent_post( true, '', true, 'taxonomy_slug' ); ?> <?php if ( is_a( $prev_post, 'WP_Post' ) ) { ?>	<a href="<?php echo get_permalink( $prev_post->ID ); ?>"><?php echo get_the_title( $prev_post->ID ); ?></a> <?php } ?>

    Link to next post in the same taxonomy with Post Title for text.

     <?php $next_post = get_adjacent_post( true, '', false, 'taxonomy_slug' ); ?> <?php if ( is_a( $next_post, 'WP_Post' ) ) { ?>	<a href="<?php echo get_permalink( $next_post->ID ); ?>"><?php echo get_the_title( $next_post->ID ); ?></a> <?php } ?>
  2. Skip to note 4 content

    I am not sure if there’s a function to get the next post of a post that maybe was retrieved from a custom loop or that isn’t the current global $post;. But sometimes, for design purposes, I have found the need to get the next post but in two levels. I searched for a function that works like this function but passing a custom $post as parameter.

    For example: get_adjacent_post( $my_post, true, '', false, 'taxonomy_slug' );. Where $my_post is a custom post where I would like to get the next post of that specific post.

    I am not sure if currently exists that function, but could be great to have it accessible.

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