Блок похожих записей предлагает читателю другие статьи по теме. В WordPress его можно собрать без плагина: найти записи с общими рубриками или метками и вывести ссылки после текста статьи.
Сначала напишем запрос через WP_Query, затем добавим кеширование и вывод в single.php. Примеры рассчитаны на классическую тему.
1. Поиск похожих записей по рубрикам
Функция возвращает массив ID записей из тех же рубрик, без текущей. Добавь в functions.php:
function mytheme_get_related_post_ids( $post_id, $count = 3 ) {
$categories = wp_get_post_categories( $post_id );
if ( ! $categories ) {
return array();
}
$query = new WP_Query( array(
'post_type' => 'post',
'post_status' => 'publish',
'category__in' => $categories,
'post__not_in' => array( $post_id ),
'posts_per_page' => $count,
'ignore_sticky_posts' => true,
'no_found_rows' => true,
'fields' => 'ids',
) );
return $query->posts;
}
Параметры, которые часто забывают:
ignore_sticky_posts— отключает приоритет закреплённых записей. Он не исключает их из результата: запись всё ещё может попасть в список, если подходит под условия запроса.no_found_rows— WordPress не будет считать общее число найденных записей. Для блока без пагинации этот подсчёт не нужен.fields => 'ids'— запрос вернёт только ID, их удобно кешировать.
Сортировка по умолчанию — от новых к старым. 'orderby' => 'rand' лучше не использовать: на большой базе ORDER BY RAND() заметно медленнее, а с кешем случайность всё равно пропадёт.
Как выбирать записи по меткам
Метки подходят, если они точнее описывают тему статьи, чем рубрики. В первой функции замени wp_get_post_categories() на wp_get_post_tags(), проверку ! $categories — на ! $tags, а category__in — на tag__in. Фрагмент замены:
$tags = wp_get_post_tags( $post_id, array( 'fields' => 'ids' ) );
// ...
'tag__in' => $tags,
2. Кеширование списка через Transients API
Чтобы не выполнять один и тот же запрос при каждом просмотре, сохрани массив ID через Transients API. Срок хранения в примере — не больше суток:
function mytheme_get_related_posts_cached( $post_id, $count = 3 ) {
$key = 'mytheme_related_' . $post_id . '_' . $count;
$ids = get_transient( $key );
if ( false === $ids ) {
$ids = mytheme_get_related_post_ids( $post_id, $count );
set_transient( $key, $ids, DAY_IN_SECONDS );
}
return $ids;
}
Пустой массив тоже сохраняется в кеше. Значение false означает, что кеш отсутствует или уже истёк; WordPress может удалить transient и раньше указанного срока.
После новой публикации список пересчитается при первом запросе без действующего кеша. Для немедленного обновления нужно очистить связанные ключи при сохранении записи, например на хуке save_post. Учитывай все затронутые списки, а не только кеш изменённой статьи.
3. Вывод похожих статей в single.php
Вставь в single.php после контента записи:
<?php
$related_ids = mytheme_get_related_posts_cached( get_the_ID() );
if ( $related_ids ) : ?>
<section class="related" aria-labelledby="related-title">
<h2 id="related-title">Похожие статьи</h2>
<ul class="related__list">
<?php foreach ( $related_ids as $related_id ) : ?>
<li class="related__item">
<a href="<?php echo esc_url( get_permalink( $related_id ) ); ?>">
<?php echo esc_html( get_the_title( $related_id ) ); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</section>
<?php endif; ?>
Функции получают ID явно, поэтому глобальная $post не меняется и не нужен wp_reset_postdata(). Если в карточке нужна картинка, добавь get_the_post_thumbnail( $related_id, 'card' ) — размеры и вывод миниатюр разобраны в статье о миниатюрах записей.
Если похожих записей не хватает
В маленьком блоге в рубрике может быть одна-две записи. Дополни список свежими записями из любых рубрик:
$ids = mytheme_get_related_post_ids( $post_id, $count );
if ( count( $ids ) < $count ) {
$extra = get_posts( array(
'post__not_in' => array_merge( array( $post_id ), $ids ),
'posts_per_page' => $count - count( $ids ),
'ignore_sticky_posts' => true,
'fields' => 'ids',
) );
$ids = array_merge( $ids, $extra );
}
Внутри условия if ( false === $ids ) замени строку $ids = mytheme_get_related_post_ids( $post_id, $count ); этим фрагментом. Вызов set_transient() оставь после него: в кеш должен попасть дополненный список.
Свежие записи из других рубрик могут быть не связаны с темой. Если используешь такое дополнение, назови блок «Читайте также».