Как создать виджет WordPress для случайного поста с миниатюрой
Введение
Многие блогеры ищут готовый виджет, который сделает ровно то, что им нужно. Если у вас есть базовые навыки программирования, часто проще написать свой собственный виджет. В этой статье я покажу, как создать виджет, который выбирает один случайный пост, получает его миниатюру (featured image) и отображает в сайдбаре — визуальный «посмотрите это» блок, который помогает читателям находить дополнительный контент на сайте.
Мы также разберём важные концепты WordPress — как работают запросы и The Loop — и добавим полезные практики: безопасность, производительность и тесты.
Ключевые понятия: запросы WordPress и Loop
Каждая страница блога — это запрос к базе данных записей (posts). В зависимости от страницы запрос меняется: домашняя страница может запрашивать «последние 10 записей», архив категории — «последние 20 записей для категории X, отсортированные по дате». Результат запроса затем проходит через шаблонный цикл — Loop — где каждая запись выводится в соответствии с шаблоном.
На одной странице может выполняться несколько запросов — вы можете создать собственные запросы в шаблонах или виджетах для вывода связанных материалов, трендов и т. п. Для нашего виджета нужно создать отдельный запрос, который возьмёт N случайных постов (в нашем случае — 1) и их миниатюры, а затем отобразит их в боковой колонке.
Важно: query_posts() перезаписывает главный запрос страницы и обычно не рекомендуется для вторичных запросов — лучше использовать WP_Query или get_posts(). Ниже приведены оба подхода: оригинальный пример с query_posts (как в классическом туториале) и современная версия с WP_Query.
Исходный базовый код виджета (как в источнике)
Создайте новый файл .php в папке wp-content/plugins. Можно писать локально и затем закачать через интерфейс, но удобнее отлаживать прямо в каталоге плагинов. В примере файл называется random-post-widget.php.
Ниже — исходный «скелет» виджета 그대로: вставьте в файл и сохраните. Не меняйте код сразу — сначала убедитесь, что плагин подключается и виджет появляется в списке.
'RandomPostWidget', 'description' => 'Displays a random post with thumbnail' );
$this->WP_Widget('RandomPostWidget', 'Random Post and Thumbnail', $widget_ops);
}
function form($instance)
{
$instance = wp_parse_args( (array) $instance, array( 'title' => '' ) );
$title = $instance['title'];
?>
This is my new widget!";
echo $after_widget;
}
}
add_action( 'widgets_init', create_function('', 'return register_widget("RandomPostWidget");') );?>
Как видно, плагин пока просто выводит заголовок «This is my new widget!». Далее мы заменим место «// WIDGET CODE GOES HERE» на рабочий запрос и вывод миниатюры.
Простой запрос и базовый Loop (оригинал)
Чтобы сделать запрос, в примере используется query_posts() и while Loop. Вставьте (как в источнике) такой код туда, где написано WIDGET CODE GOES HERE:
// WIDGET CODE GOES HERE
query_posts('');
if (have_posts()) :
while (have_posts()) : the_post();
the_title();
endwhile;
endif;
wp_reset_query(); Это самый простой пример: без параметров query_posts() вернёт записи по умолчанию (обычно 10 последних). Код просто выведет заголовки.
Чтобы добавить ссылку и HTML‑разметку, можно использовать get_the_permalink() и get_the_title():
query_posts('');
if (have_posts()) :
echo "";
while (have_posts()) : the_post();
echo "- ".get_the_title()."
";
endwhile;
echo "
";
endif;
wp_reset_query();Чтобы получить один случайный пост, добавьте параметры:
query_posts('posts_per_page=1&orderby=rand');А чтобы вывести миниатюру рядом с заголовком, используйте the_post_thumbnail():
query_posts('posts_per_page=1&orderby=rand');
if (have_posts()) :
echo "";
while (have_posts()) : the_post();
echo "- ".get_the_title();
echo the_post_thumbnail(array(220,200));
echo "
";
endwhile;
echo "
";
endif;
wp_reset_query();Этот код выведет одну случайную запись и её миниатюру. Результат можно увидеть на тестовом блоге автора.
Современная реализация: WP_Query и безопасный вывод
Рекомендуется не использовать query_posts() во вторичных запросах. Ниже — современная и безопасная версия для виджета, которая:
- использует WP_Query, не ломая главный запрос;
- кеширует результат во временной опции transient для снижения числа запросов к БД;
- экранирует вывод (esc_url, esc_attr, esc_html);
- использует безопасный регистр конструктора класса (PHP7+ совместимость).
Пример кода (фрагмент) для вставки в функцию widget():
// Пример: современный WP_Query и безопасный вывод
$cache_key = 'rpw_random_post_widget';
$cached = get_transient($cache_key);
if ($cached !== false) {
echo $cached; // уже безопасно закешировано и содержит HTML
} else {
$args = array(
'posts_per_page' => 1,
'orderby' => 'rand',
'post_status' => 'publish'
);
$q = new WP_Query($args);
ob_start();
if ($q->have_posts()) {
echo '';
}
wp_reset_postdata();
$html = ob_get_clean();
set_transient($cache_key, $html, HOUR_IN_SECONDS * 6); // кэш 6 часов
echo $html;
}Важно: пример использует set_transient/get_transient — кэширование снижает нагрузку БД, особенно на популярных сайтах. Подберите TTL (time to live) по нагрузке и обновляемости контента.
Советы по безопасности и качественному коду
- Экранируйте все данные при выводе: esc_html для текста, esc_attr для атрибутов, esc_url для URL.
- Используйте wp_reset_postdata() после WP_Query, чтобы вернуть глобальную $post в исходное состояние.
- Для опций виджета и форм в админке используйте sanitize_text_field и соответствующие функции в update().
- Ограничьте доступ к админским контролам по capability, если добавляете расширенные настройки.
Производительность и кэширование
- WP_Query с параметром orderby=rand может быть дорогим на больших таблицах posts. Для очень больших сайтов рассмотрите альтернативы (см. раздел «Альтернативные подходы»).
- Кэшируйте вывод (transients), особенно если набор случайных постов можно обновлять не чаще раза в несколько часов.
- Для мультисайтов и масштабируемых систем используйте объектный кеш (Redis, Memcached) если доступно.
Альтернативные подходы
- get_posts(): возвращает массив объектов WP_Post и удобен для простых задач.
- Крайне эффективный метод для больших баз: сначала получить случайный ID через SQL выборку ограниченного набора ID, затем запросить посты по ID (двухэтапный подход уменьшает дерево сортировки).
- REST API: для динамических одностраничных приложений можно получить случайный пост через REST и рендерить виджет на фронте.
- Плагин‑решения: если вы не хотите кодить, используйте готовые плагины, но будьте внимательны к безопасности и производительности.
Когда подход с миниатюрой не сработает (ограничения)
- У записи нет миниатюры: обработайте has_post_thumbnail() и выведите fallback‑картинку.
- Большая нагрузка базы данных при orderby=rand на миллионах записей.
- Кэширование на уровне CDN или прокси может показывать одинаковый пост разным посетителям — если хотите персонализацию, кэш нужно настраивать аккуратно.
Ментальные модели и эвристики
- «Не ломай главный запрос»: если вам нужен вторичный запрос — используйте WP_Query или get_posts.
- «Кэшируй горячее» — медленные операции (orderby=rand, тяжелые JOIN) лучше делать реже и кешировать результат.
- «Безопасный вывод» — любое внешнее или динамическое содержимое должно быть экранировано.
Чеклист по ролям
Разработчик:
- Использовать WP_Query или get_posts, не query_posts.
- Экранировать вывод (esc_*).
- Добавить transient‑кэширование.
- Добавить fallback для постов без миниатюр.
Контент‑менеджер:
- Убедиться, что важные посты имеют featured image.
- Настроить размеры миниатюр через add_image_size при необходимости.
Сайт‑админ:
- Включить объектный кэш (если доступно) для ускорения транзиентов.
- Проверить память PHP и время выполнения при больших выборках.
Критерии приёмки
- Виджет отображается в списке виджетов в админке.
- На передней части сайта виджет выводит одну ссылку на пост с миниатюрой.
- Вывод корректно экранирован (нет XSS при злонамеренных заголовках).
- Если у записи нет миниатюры, отображается заменяющая картинка или текст.
- Кэш работает: нагрузка БД снижается при повторных загрузках страницы.
Тесты и приёмочные сценарии
- Функциональность: при нескольких обновлениях страницы виджет показывает разные посты (в пределах кэша).
- Без миниатюры: запись без featured image корректно обрабатывается (fallback).
- Права доступа: только пользователи с нужными правами видят настройку в админке.
- Производительность: на тестовом стенде замерить время отклика до и после включения кэша.
Полезная шпаргалка: аргументы запроса
- posts_per_page — число записей; для виджета обычно 1.
- orderby — rand, date, title и т.д.
- post_status — publish (рекомендуется).
- category_name / cat — ограничение по категории.
- tag — ограничение по метке.
Применение и миграция
- Совместимость: код работает на современных версиях WP, но старый конструктор виджета (в примере) устарел. Для PHP7+ и новых WP используйте construct и parent::construct().
- При переносе на другой сайт проверьте размеры миниатюр и наличие нужных image sizes, регенерируйте миниатюры при необходимости.
Пример модернизированного класса виджета (скелет)
class Random_Post_Widget extends WP_Widget {
public function __construct() {
parent::__construct(
'random_post_widget',
__('Random Post and Thumbnail', 'text-domain'),
array('description' => __('Displays a random post with thumbnail', 'text-domain'))
);
}
public function widget($args, $instance) {
echo $args['before_widget'];
$title = apply_filters('widget_title', $instance['title'] ?? '');
if (!empty($title)) {
echo $args['before_title'] . esc_html($title) . $args['after_title'];
}
// Здесь можно вызвать безопасную функцию, которую мы показывали выше
echo $args['after_widget'];
}
public function form($instance) { /* форма настроек */ }
public function update($new_instance, $old_instance) { /* обновление */ }
}
add_action('widgets_init', function() { register_widget('Random_Post_Widget'); });Настройка размеров миниатюры и fallback
- Зарегистрируйте нужный размер через add_image_size(‘rpw-thumb’, 220, 200, true);
- При отсутствии миниатюры выводите статическое изображение из темы: get_template_directory_uri() . ‘/images/fallback-thumb.png’
Риски и способы их снижения
- Нагрузка на БД при orderby=rand — использовать кэш или альтернативные выборки.
- Уязвимости XSS — строго экранировать весь вывод.
- Конфликты имён — префиксируйте функции и классы уникальным префиксом (например, rpw_).
Краткое резюме
Создать собственный виджет для WordPress несложно: достаточно знаний базового PHP и понимания WP_Query/Loop. Лучше использовать WP_Query, кешировать результат и экранировать вывод. Для больших сайтов подход с orderby=rand требует аккуратности: применяйте кэш и альтернативные алгоритмы выборки.
Важные заметки
- Если вы используете старую реализацию виджета из примера, обновите конструктор до __construct и применяйте экранирование.
- Тестируйте на копии сайта перед выкладкой на прод.
Краткое итоговое резюме:
- Написать виджет можно в пару десятков строк; лучше использовать WP_Query и кэш.
- Экранируйте вывод и обрабатывайте посты без миниатюр.
- Для больших сайтов избегайте прямого использования orderby=rand без кэширования.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone