Начнем с того что похожие посты не увеличивают трафик. Никак. Я проделал весь этот путь с Jetpack и после месяца использования среднее количество просмотров страниц в день не изменилось. Так что все эти рассказы и байки от маркетологов о похожих товарах постах не более чем мифы. В любом случае, похожие материалы в новостях ничего навязчивого и плохого из себя не представляют, поэтому почему бы не помочь посетителям проще находить похожие материалы?
Всю работу по настройке можно разделить на две части — генерация и вывод блока в нужном месте и стилизация, написание CSS-стилей.
Но ведь все и так работает
По умолчанию обе задачи решает сам Jetpack. Блок похожих публикаций добавляется в конец всех записей, используя фильтр the_content
. Этот прием используется многими плагинами для добавления чего угодно в конец статьи — от кнопок социальных сетей, заканчивая формочками подписок. Я не считаю такое поведение хорошим тоном, потому что с точки зрения HTML-разметки к тексту добавляется нечто, что к нему совсем не относится. Пользователи Safari с кнопкой Reader и поисковики этого точно не оценят.
Что же касается стилей — Jetpack добавляет свой дополнительный CSS-файл на страницы, в котором, можно сказать, все в порядке. Но отдельный файл… Я как перфекционист мечтающий о том дне, когда все JS и CSS файлы будут склеены в один, не хочу лишних файлов, когда этого можно избежать. И самое главное — иногда блок выглядит недостаточно симпатично, поэтому следует потратить некоторое время на приведение его к виду и стилистике схожей с вашей темой.
Вывод в правильном месте
Хорошие разработчики тем и плагинов пользуются фильтрами и экшенами, дающими невероятную гибкость. Покопавшись в исходном коде модуля можно убедиться в том, что блок с похожими постами добавляется в конец поста через фильтр.
protected function _action_frontend_init_page() { $this->_enqueue_assets( true, true ); $this->_setup_shortcode(); add_filter( 'the_content', array( $this, 'filter_add_target_to_dom' ), 40 ); } public function filter_add_target_to_dom( $content ) { if ( !$this->_found_shortcode ) { $content .= "\n" . $this->get_target_html(); } return $content; }
Для начала нужно сделать вызов remove_filter (...)
. Но с учетом того, что модули и Jetpack используют классы, а не просто функции стоит исследовать исходный код, чтобы понять, какие именно аргументы необходимо передать. В справочном материале на официальном сайте, кажется, есть решение для нашей задачи.
function jetpackme_remove_rp() { $jprp = Jetpack_RelatedPosts::init(); $callback = array( $jprp, 'filter_add_target_to_dom' ); remove_filter( 'the_content', $callback, 40 ); } add_filter( 'wp', 'jetpackme_remove_rp', 20 );
Отлично, Jetpack_RelatedPosts::init()
тот самый аргумент, который мы искали! Но не стоит торопиться вставлять код из примера к себе. Если составить цепочку происходящего, можно заметить, что мы обращаемся к методу класса, которого может и не быть, поэтому при отключенном модуле этот код приведет к фатальной ошибке в PHP. Чтобы найти правильное решение, стоит заглянуть в исходный код самого Jetpack, впрочем, как и всегда. Что именно искать? Нечто, что позволит определять активен ли Jetpack и модуль похожих записей и тут на помощь приходит практика. Например, в плагине bbPress есть экшен bbp_init
, к которому можно прицеплять свои функции и уже внутри смело использовать методы из bbPress, ведь bbp_init
срабатывает только если bbPress активирован.
В Jetpack есть именные экшены, срабатывающие сразу после загрузки модуля (см. Github).
require Jetpack::get_module_path( $module ); do_action( 'jetpack_module_loaded_' . $module );
Поэтому в случае с модулем похожих записей правильный код удаления фильтра для the_content
будет выглядеть следующим образом:
function selena_jetpack_remove_rp () { $jprp = Jetpack_RelatedPosts::init(); remove_filter ( 'the_content', array ($jprp, 'filter_add_target_to_dom'), 40 ); } function selena_jetpack_module_loaded_related_posts () { add_action ('wp', 'selena_jetpack_remove_rp', 20); } add_action ('jetpack_module_loaded_related-posts', 'selena_jetpack_module_loaded_related_posts');
Что здесь происходит? В момент срабатывания jetpack_module_loaded_related-posts
(сработает только в том случае, если модуль активен) происходит добавление функции, которая описана в примере с официального сайта. Таким образом мы сможем включать и отключать модуль в любое время без необходимости вечно переписывать код.
Вывод блока похожих записей
Теперь сделаем вывод блока в нужном месте. В теме Селена, с которой я вожусь уже больше года, есть специальное место для подобных вещей. Схематично разметка выглядит так:
<article class="entry"> <header class="entry-header"> <!-- Заголовок, имя автора, дата публикации и т. п. </header> <div class="entry-content"> <!-- Текст записи --> </div> <div class="entry-meta"> <!-- Соц. кнопки, похожие посты, список тегов и рубрик, ссылки на предыдущую и следующую запись и т. п. --> </div> </article>
Уже в другом файле, selena-network/themes/selena/jetpack/related-posts.php
, добавляем конструкцию, схожую с той, что удаляла фильтр:
function selena_network_themes_selena_related_posts ($content) { // Выводим только на страницах одиночных записей // Внимание, is_single() сработает и для других типов постов if (is_single ()) { // Получаем код блока из Jetpack $related = Jetpack_RelatedPosts::init()->get_target_html(); return $content . '<div class="entry-meta-item">' . $related . '</div>'; } return $content; } function selena_network_themes_selena_jetpack_module_loaded_related_posts () { // Мой фильтр внутри <div class="entry-meta"> add_filter ('selena_entire_entry_meta', 'selena_network_themes_selena_related_posts', 15); } add_action ('jetpack_module_loaded_related-posts', 'selena_network_themes_selena_jetpack_module_loaded_related_posts');
Ура!

Блок похожих записей внутри тега <footer>
Тонкая настройка
На скриншоте выше можно заметить странные надписи вроде «В “Музыка”». Это рубрика, в которой находится запись, но по-русски будет лучше, если написать «Рубрика “Музыка”». Что ж, в исходном коде модуля похожих записей, как и во всех других, есть еще несколько полезных фильтров. Давайте исправим это.
function selena_network_jetpack_relatedposts_post_category_context ($post_cat_context, $category) { return sprintf( __ ( 'Category „%s“', 'selena_network' ), $category->name ); } add_filter ('jetpack_relatedposts_post_category_context', 'selena_network_jetpack_relatedposts_post_category_context', 10, 2); function selena_network_jetpack_relatedposts_post_tag_context ($post_tag_context, $tag) { return sprintf( __ ( 'Tag „%s“', 'selena_network' ), $tag->name ); } add_filter ('jetpack_relatedposts_post_tag_context', 'selena_network_jetpack_relatedposts_post_tag_context', 10, 2);
С помощью фильтров jetpack_relatedposts_post_category_context
и jetpack_relatedposts_post_tag_context
мы заменяем надписи по умолчанию на свои, с правильными кавычками (в русском языке используются «елочки», а не „лапки“).
Еще можно убрать курсив у заголовка и добавить свои классы (я делаю это все в том же файле selena-network/themes/selena/jetpack/related-posts.php
):
function selena_network_themes_selena_related_posts_headline ($headline) { return '<h3 class="jp-relatedposts-headline h4">' . __ ('Related posts', 'selena_network') . '</h3>'; } add_filter ('jetpack_relatedposts_filter_headline', 'selena_network_themes_selena_related_posts_headline');
Напоследок не забудем отключить CSS-файл, который добавил Jetpack, ведь ниже мы напишем свои собственные стили внутри style.css
из темы.
// Remove Jetpack styles function selena_remove_jetpack_styles () { wp_dequeue_style ('jetpack_related-posts'); } add_action ('wp_print_styles', 'selena_remove_jetpack_styles'); add_filter ('jetpack_implode_frontend_css', '__return_false');
На этом мы заканчиваем с выводом блока и займемся его стилизацией.
Стили
PHP-функция, которая используется для вывода похожих постов, на самом деле выводит лишь контейнер с заголовком. Все остальное генерируется с помощью Java Script.
<div id='jp-relatedposts' class='jp-relatedposts' > <h3 class="jp-relatedposts-headline h4">Похожие посты</h3> </div>
Здесь я не буду давать каких-то конкретных рекомендаций — все зависит от ваших предпочтений и темы. Строчки с категориями или тегами можно скрыть (display: none;
), цвет ссылок или фона изменить, добавив фоновую картинку и т. д. В моей теме все стили для всех страниц содержатся в единственном файле для стилей — style.css
. Ниже пример моего макета. Я нашел подходящую фотографию, которая немного взаимодействует с контентом.

Блок становится более интересным, если рядом добавить небольшую иллюстрацию
Но об одной детали стоит упомянуть. Стили, которые добавляет Jetpack делают так, что ссылка на статью накладывается поверх контента с помощью position:absolute;
, поэтому контент выглядит словно на нем нет ссылки, но становится кликабельным.
Некоторые сложности возникают для :hover
-состояния. Контент находится вне ссылки, а значит селектор вроде a:hover .header
не сработает. Поэтому в самом Jetpack hover-стили указываются не у ссылки, а у <div>. Это накладывает некоторые ограничения на верстку. К примеру, сетка из Bootstrap использует отрицательные margin-ы, в комлекте с этим hover даст неприятный эффект. Наводя курсор на <div>, он превращается в pointer, а кликнуть на самом деле нельзя, потому что это div, а не ссылка. Я выкрутился, используя хитрые селекторы. Такая конструкция заработала в последнем Хроме и IE, но в Safari на Маке двойной плюсик почему-то не сработал.
/* * Сработает для .jp-relatedposts-post-title * расположенного сразу после .jp-relatedposts-post-aoverlay * при наведении на .jp-relatedposts-post-aoverlay */ .jp-relatedposts-post-aoverlay:hover+.jp-relatedposts-post-title .jp-relatedposts-post-a {} /* * Сработает для класса .jp-relatedposts-post-excerpt * при наведении курсора на .jp-relatedposts-post-aoverlay */ .jp-relatedposts-post-aoverlay:hover + .jp-relatedposts-post-title + .jp-relatedposts-post-excerpt
Для тех кто не очень понял что тут произошло рекомендую ознакомиться со списком селекторов. Если коротко, то символ +
в селекторах означает, что необходимо выбрать те элементы, которые располагаются сразу после того, что было указано перед плюсиком 🙂
Заключение
В результате мы получили вывод «служебной» информации в месте, где она и должна логически находиться. Сделали верстку лучше и отзывчевее. Уменьшили количество подключаемых CSS-файлов на странице. Разобрались с очередностью срабатывания экшенов в Jetpack и WordPress. Настроили работу модуля таким образом, что при его отключении сайт не будет ломаться и выдавать фатальные ошибки.
При подготовке иллюстрации использовалась фотография Люкаса Кёллера.
“Начнем с того что похожие посты не увеличивают трафик. Никак. Я проделал весь этот путь с Jetpack и после месяца использования среднее количество просмотров страниц в день не изменилось. Так что все эти рассказы и байки от маркетологов о похожих товарах постах не более чем мифы.”
Если пользоваться jetpack, который похожие записи выдаёт в js, то конечно никакого эффекта не будет, перелинковка-то больше для поисковиков нужна, а им подавай ссылки в html