Перелинковка статей в WordPress (предыдущие записи из категории). Функция 2

Хочу поделиться доработкой своей функции для кольцевой перелинковки статей в WordPress. Первый вариант функции в общем ни чем не уступает этому, разница лишь в том, что с помощью этого варианта функции, вы сможете указать с какой таксономией работать и какой тип записи вас интересует. Прошлая функция работает лишь со стандартными категориями WordPress и постами, т.е. с таксономией category и типом записей post.

Смотрите также вопрос: https://wp-kama.ru/question/perelinkovka-postov

Эта функция полностью заменяет прошлую. Толчком для её создания стала просьба одного из читателей этого блога, сделать функцию для кольцевой перелинковки, для кастомных (произвольных) страниц, которые были предусмотренны на сайте. Таксономия так же была созданна новая. Не долго думая я переделал прошлую функцию, изменив там принцип получения таксономии: если раньше категория(и), в которой находится запись, получалась стандартными функциями WordPress, то теперь таксономия к которой относится запись получается прям из Базы Данных, такой подход вроде более логичный и быстрый (не проверял).

Тот кто просил эту функцию даже отблагодарил меня 10-ю условными единицами на мой кошелек WebMoney, что конечно приятно, спс ему, надеюсь он не против, что я выкладываю эту функцию в паблик.

Устанавливается функция как обычно: копируем код в файл темы functions.php а затем в шаблоне, там где нужно вывести ссылки, вызываем функцию с указанием необходимых параметров.

GitHub
/**
 * Предыдущие записи из рубрики (относительно текущей записи) +
 * кольцевая перелинковка (можно указывать таксономию и тип записи).
 *
 * Кэширует результат в объектный кэш, если он включен.
 *
 * Вызываем функцию так:
 *
 *     echo kama_previous_posts_from_tax_lis( [
 *         'post_num' => 5,
 *         'format' => '{date:j.M.Y} - {a}{title}{/a}',
 *     ] );
 *
 * @param array|string $args {
 *     Parameters passed as array or query string.
 *
 *     @type int          $post_num      Количество ссылок.
 *     @type string       $format        {thumb} {date:j.M.Y} - {a}{title}{/a} ({comments})
 *     @type string       $list_tag      Тег-обертка каждой ссылки.
 *     @type string       $tax           Таксономия. Пр. category.
 *     @type string|array $exclude_terms Элементы таксономии посты из которых нужно исключить (через запятую или в массиве).
 *                                       Указываем term_id или term_slug.
 *     @type string       $post_type     Тип записи. пр. post.
 * }
 *
 * @version 1.3
 */
function kama_previous_posts_from_tax_lis( $args = array() ){
	global $post, $wpdb;

	$rg = (object) wp_parse_args( $args, [
		'post_num'      => 5,
		'format'        => '',
		'list_tag'      => 'li',
		'tax'           => 'category',
		'post_type'     => 'post',
		'exclude_terms' => '',
	] );

	if( wp_using_ext_object_cache() ){
		$cache_key = md5( __FUNCTION__ . $post->ID );
		$cache_flag = __FUNCTION__;

		if( $cache_out = wp_cache_get( $cache_key, $cache_flag ) ){
			return $cache_out;
		}
	}

	// wp_parse_list() analog to work with WP < 5.1
	if ( is_string( $rg->exclude_terms ) ) {
		$rg->exclude_terms = preg_split( '/[\s,]+/', $rg->exclude_terms, -1, PREG_SPLIT_NO_EMPTY );
	}
	if( 'exclude_terms' ){
		$exclude_terms = $rg->exclude_terms;
		foreach( $exclude_terms as & $term_id ){
			if( ! is_numeric( $term_id ) ){
				$term = get_term_by( 'slug', $term_id, $rg->tax );
				$term_id = $term ? $term->term_id : 0;
			}
			else {
				$term_id = (int) $term_id;
			}
		}
		unset( $term_id );
		$exclude_terms = array_map( 'intval', array_filter( $exclude_terms ) );

		$AND_NOT_IN = $exclude_terms ? sprintf( ' AND term_id NOT IN (%s)', implode( ',', $exclude_terms ) ) : '';
	}

	$sub_query_term_id = $wpdb->prepare(
		"(SELECT term_id FROM $wpdb->term_relationships rl
			LEFT JOIN $wpdb->term_taxonomy tx ON (rl.term_taxonomy_id = tx.term_taxonomy_id)
			WHERE object_id = %d AND tx.taxonomy = %s $AND_NOT_IN
			LIMIT 1)",
		$post->ID, $rg->tax
	);

	$WHERE_arr = [
		"p.post_status = 'publish'",
		"tax.term_id = $sub_query_term_id",
		$wpdb->prepare( 'p.post_date < %s', $post->post_date ),
		$wpdb->prepare( 'tax.taxonomy = %s', $rg->tax ),
		$wpdb->prepare( 'p.post_type = %s', $rg->post_type ),
	];

	$SELECT = "SELECT ID, post_title, post_date, comment_count, guid
		FROM $wpdb->posts p
		LEFT JOIN $wpdb->term_relationships rel ON (p.ID = rel.object_id)
		LEFT JOIN $wpdb->term_taxonomy tax ON (rel.term_taxonomy_id = tax.term_taxonomy_id)";

	$WHERE = implode( ' AND ', $WHERE_arr );
	$ORDER_BY = 'ORDER BY p.post_date DESC';

	$sql = "$SELECT WHERE $WHERE $ORDER_BY LIMIT " . (int) $rg->post_num;

	$posts_data = $wpdb->get_results( $sql );

	$posts_count = count( $posts_data );

	// если количество меньше нужного, делаем 2-й запрос (кольцевая перелинковка)
	if( ! $posts_data || $posts_count < $rg->post_num ){
		$NOT_IN = $post->ID;
		foreach( $posts_data as $pdata ){
			$NOT_IN .= ",$pdata->ID";
		}

		$WHERE_arr[] = "p.ID NOT IN ($NOT_IN)";
		$WHERE = implode( ' AND ', $WHERE_arr );

		$sql = "$SELECT WHERE $WHERE $ORDER_BY LIMIT " . (int) ( $rg->post_num - $posts_count );

		$res2 = $wpdb->get_results( $sql );

		$posts_data = array_merge( $posts_data, $res2 );
	}

	if( ! $posts_data ){
		return '';
	}

	// вывод
	$date_match = false;
	if( $rg->format ){
		preg_match( '!{date:(.*?)}!', $rg->format, $date_match );
	}

	$add_thumb = false !== strpos( $rg->format, '{thumb}' );

	$out = '';
	foreach( $posts_data as $pdata ){
		$x = ( ( $x ?? '' ) === 'li1' ) ? 'li2' : 'li1';

		$a = sprintf( '<a href="%s" title="%s">', get_permalink( $pdata->ID ), esc_attr( $pdata->post_title ) );

		if( $rg->format ){
			$formated = strtr( $rg->format, [
				'{title}'    => esc_html( $pdata->post_title ),
				'{a}'        => $a,
				'{/a}'       => '</a>',
				'{comments}' => $pdata->comment_count ?: '',
			] );

			if( $add_thumb ){
				$formated = str_replace( '{thumb}', get_the_post_thumbnail( $pdata->ID, 'thumbnail' ), $formated );
			}

			// есть дата
			if( $date_match ){
				$formated = str_replace( $date_match[0], apply_filters( 'the_time', mysql2date( $date_match[1], $pdata->post_date ) ), $formated );
			}
		}
		else{
			$formated = $a . esc_html( $pdata->post_title ) . '</a>';
		}

		$out .= "\t<li class=\"$x\">$formated</li>\n";
	}

	if( wp_using_ext_object_cache() ){
		wp_cache_add( $cache_key, $out, $cache_flag );
	}

	return $out;
}

Параметры, передаваемые функции

  1. post_num - количество ссылок, которые будут выведены. По умолчанию 5.

  2. format - формат выводимых ссылок. По умолчанию "{a}{title}{/a}".

  3. tax - таксономия к которой относится запись. По умолчанию category

  4. post_type - тип записей для который будет происходить перелинковка. По умолчанию post.

Несколько примеров использования

#1. Выведем 10 предыдущих ссылок из категории, для обычных записей WordPress:

<?php echo kama_previous_posts_from_tax('post_num=10'); ?>

#2. Пример передачи параметров и указания формата

Выведем 6 предыдущих ссылок, для записей из таксономии movies, тип записи которых равен movie. Так же в формате вывода добавим дату публикации записи и миниатюру:

<?php
echo kama_previous_posts_from_tax( array(
	'post_num'  => 6,
	'format'    => '{thumb} {a}{title}{/a} - {date:j.M.Y}',
	'tax'       => 'movies',
	'post_type' => 'movie',
);
?>

Весь список параметров и шорткоды для параметра format смотрите в коде функции (в самом начале).