Случайные записи в wp_query
Я хочу показывать все записи в виде mywebsite.com/postname/1/
, mywebsite.com/postname/2/
...... mywebsite.com/postname/7/
..
Код ниже работает отлично, единственная проблема в том, что он показывает только первые записи mywebsite.com/postname/
...
Как это можно реализовать?
<?php
/**
* Template Name: Random Post
* Этот шаблон будет отображать только контент, который вы ввели в редакторе страниц
*/
?>
<html>
<head>
</head>
<body>
<?php
/*
Выбор случайной записи
Используйте на странице для перенаправления посетителя на случайную запись с возможностью модификации запроса
*/
// установка аргументов для WP_Query для получения 1 случайной опубликованной записи
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'orderby' => 'rand',
'order' => 'DESC',
// Использование date_query для фильтрации записей за последнюю неделю
'date_query' => array(
array(
'after' => '2 week ago'
)
)
);
// Время пришло! Идем в случайное место
$my_random_post = new WP_Query ( $args );
while ( $my_random_post->have_posts () ) {
$my_random_post->the_post ();
// перенаправление на случайную запись
wp_redirect ( get_permalink () );
exit;
}
?>
</body>
</html>
Делать это в PHP — ужасная идея:
- Эту страницу невозможно кэшировать
- Сортировка в случайном порядке крайне ресурсоемка для запроса, так как включает создание временных таблиц в базе данных, полное сканирование, поскольку необходимо скопировать всю таблицу постов, затем случайным образом переупорядочить записи, и только после этого выполнить фактический запрос к новой таблице перед ее уничтожением
- Это делает вас уязвимыми к атакам на исчерпание ресурсов
Например, эта команда будет многократно обращаться к вашему URL со случайным постом. Если выполнить ее достаточное количество раз на достаточном количестве компьютеров, это приведет к отказу базы данных:
for i in `seq 1 20000`; do curl http://mywebsite.com/postname; done
Если вы используете дешевый shared-хостинг, может быть достаточно открыть ваш URL в нескольких вкладках браузера одновременно, чтобы вызвать проблемы.
Не говоря уже о том, что ваш редирект отправляет HTTP-заголовки, но код выводит теги заранее, поэтому заголовки уже были отправлены, что приводит к ошибкам и предупреждениям PHP.
Супербыстрая и простая альтернатива
Вместо этого выполните обычный запрос, упорядоченный по дате, и выведите данные в формате JSON. Затем в JavaScript на стороне браузера случайным образом выберите один из этих постов и выполните редирект на клиентской стороне.
Таким образом, страница может кэшироваться, ваша база данных защищена, а вся тяжелая работа выполняется браузером. Запрос к базе данных будет выполняться гораздо быстрее.
Теперь ваша задача сводится к выводу данных в виде списка, случайному выбору элемента из этого списка в JS и использованию window.location= ...
для редиректа. Знания WP для этого не требуются.

+1 за клиентскую альтернативу. Но один вопрос: установка random в WP_Query
выберет случайный пост из, скажем, 15 000 постов. Но эффективно ли отправлять JSON-ответ, содержащий как минимум 15К строк в браузер?

Нет, отправка 15k постов неэффективна, как и запрос 15k постов. Любые операции с random и базами данных будут чрезмерно затратными. Вместо этого предоставьте более разумный выбор, например, 20 последних постов или 20 постов с наибольшим количеством комментариев. Существует так много альтернатив random при выводе постов, и random редко означает то, что люди под ним подразумевают. Если сайт вернет один и тот же пост 10 раз, это будет валидным результатом действительно случайного выбора, как и 5 постов подряд, но люди обычно имеют в виду 10 постов, равномерно распределенных по всему диапазону.

Итак, что вы предлагаете для блога с тысячами постов? Хорошая ли идея — установить случайное смещение (offset) и затем запрашивать 1 пост?

нет, мое предложение — не пытаться случайным образом выбирать из тысяч постов, так же как снятие £1 млн из банка одинаково затратно, независимо от того, делаете ли вы это через карту, чек или другими способами. Случайный выбор — плохая идея, по-настоящему случайный — еще хуже, вам придется идти на компромисс и создавать видимость случайности или ограничивать область выбора. Существует огромное количество альтернатив, которые выглядят так же, но получить по-настоящему случайное число из 15 тыс. постов, не зная их всех заранее, принципиально дорого

Интересно. Но если пользователь просматривает кэшированную версию страницы (кэширование страниц), это не имеет значения, верно? Очевидно, случайно выбранный пост останется тем же, пока кэш не будет сброшен. Если только, конечно, 'orderby' => 'rand'
не вызывает фрагментированное кэширование...

Да, вам придется кэшировать только эти части страницы, если только вы не сделаете это через JavaScript. В любом случае, случайный выбор — ужасная идея, и он никогда не означает то, что вы задумали

Привет, Том, я знаю, что этот пост старый, но не мог бы ты привести пример с использованием JSON? Я даже не знаю, с чего начать.

@rudtek любой REST API endpoint возвращает данные в формате JSON, а JSON — это то же самое, что объекты и массивы в JavaScript. Например, [1,2,3,4,5]
— это JSON, а также массив в JavaScript. Как случайно выбрать элемент из массива в JS — это вопрос для StackOverflow. Как декодировать JSON-ответ из AJAX-запроса — тоже вопрос для StackOverflow. Как включить массив элементов в HTML-страницу для использования в JS — аналогично. Для всего остального есть json_encode
, стандартный ванильный PHP. Честно говоря, тут очень мало связано с WordPress, если вообще есть.

Также есть промежуточный способ в PHP: shuffle($my_random_post->posts)
— если использовать его непосредственно перед циклом — создаст "случайный" порядок. Но учтите, что сортировка большого количества постов СЛУЧАЙНЫМ образом всегда будет очень затратной, и лучше обрабатывать это в браузере, как предлагает @TomJNowell!

Перемешивание также работает в PHP, основная нагрузка связана с тем, что случайность обрабатывается базой данных. Если вы можете перенести эту задачу в другое место — будь то PHP, браузер или предварительные вычисления — это даст значительный прирост производительности.

Только один пост отображается на вашей стороне, потому что вы используете exit()
в вашем цикле. В вашем коде, когда WP_Query начинает работу, цикл завершается после первого прохода.
Удалите exit()
и используйте этот код:
<?php
/*
* Выбор случайного поста
* Используйте на странице для перенаправления посетителя на случайный пост с возможностью модификации запроса
*/
// Устанавливаем аргументы для WP_Query, чтобы получить 1 случайный опубликованный пост
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'orderby' => 'rand',
'order' => 'DESC',
// Используем date_query для фильтрации постов за последние 2 недели
'date_query' => array(
array(
'after' => '2 week ago'
)
)
);
// Время действовать! Переходим на случайный пост
$my_random_post = new WP_Query ( $args );
if($my_random_post->have_posts()){
while ( $my_random_post->have_posts () ) {
$my_random_post->the_post ();
echo '<a href="'.get_the_permalink().'">'.get_the_title().'</a>';
}
}
?>

Я хочу, чтобы отображалась только одна запись, и чтобы происходил редирект на полный пост. Шаблон использует "mywebsite.com/random/,,,, когда посетитель использует mywebsite.com/random/, я хочу, чтобы пользователь перенаправлялся на полный пост, но случайным образом среди всех постов, как я объяснил "mywebsite.com/postname/15/..... и так далее, пост выглядит как галерея с кнопкой "следующий пост". Я хочу, чтобы посетитель перенаправлялся случайным образом на какой-то номер поста, а не просто на пост.

Используя мою случайную страницу, я перенаправляю пользователей на случайный пост. Тот же домен. mypage.com/random/ на mypage.com/somepost/ .... mypage.com/anotherpost/.... до этого момента все ок, я хочу больше... > mypage.com/somepost/2/..mypage.com/somepost/3

Извините, я не понял вас. Скажите сначала: эта ссылка на код не работает или вы хотите изменить постоянные ссылки записей? Пожалуйста, уточните вашу задачу.

@adikica если вы делаете редирект средствами PHP => вы должны сделать это ДО вывода любого HTML-кода ... а не в середине шаблона - вы можете использовать клиентский редирект на JavaScript или хук template_redirect

Вам не нужно перенаправлять пользователя на страницу со случайной записью, и вы даже не сможете этого сделать, если захотите, так как нельзя перенаправить одновременно в несколько мест.
Поэтому просто выводите случайные записи внутри вашего цикла:
while ( $my_random_post->have_posts () ) {
$my_random_post->the_post ();
echo '<a href="'.get_the_permalink().'">'.get_the_title().'</a>';
}
