В get_posts при случайной сортировке не работает пагинация если указать аргумент exclude
Решил я значит по тестить случайную сортировку в get_posts (просьба заказчика) и с удивлением обнаружил что при ajax загрузке происходит дублирование записей, немного покумекав прикинул что это вполне ожидаемое поведение, так как в запросе нет данных о том что уже выведено.
Далее нашел самый очевидный и банальный способ решения, передавать в запросе ID постов которые уже отрисованы и передавать их в параметр 'exclude', логично скажете вы и я соглашусь с вами но в этот момент опять же с удивлением я заметил что теперь в принципе сломалась пагинация, 1 страница отдает лимит вторая же вообще ничего не возвращает.
Причем что странно так это то что если уменьшить лимит для второй страницы (сделать меньше чем кол-во которое осталось), то есть если всего 20 и мы уже показали 12 то сделав лимит меньше 8 он начинает что то показывать, 20-12 это 8 и при лимите в 7 он отдает 1 запись,
В общем пока что не понял с чем это связано, ожидаемое поведение это то что он на 2 странице будет отдавать 8 записей которых нет на первой и всё, видимо пока что придется в ручную делать пагинацию, через offset
Вот пример кода если кому интересно
$exclude_ids = [1212, 616, 773, 3345] // Всего 12 элементов; $posts = get_posts([ 'paged' => 2, 'posts_per_page' => 12, 'post_status' => 'publish', 'post_type' => 'news', 'category' => 'media', 'order' => 'ASC', 'orderby' => 'rand', 'suppress_filters' => true, 'post__not_in' => $exclude_ids, ]);
UPD1: Кажется допер, у меня в голове как то по другому строился принцип фильтрации, мол то что мы сначала берем вторую страницу и из неё исключаем exclude а тут вон оно как, он же сначала фильтрует а потом берет 2 страницу, логично что в этом случае её уже нет так как мы исключили 12 записей и осталось 8, интересно как тогда решать проблему с задваиванием постов если через exclude не получается...
UPD2: Нашел ответ, нейронка посоветовала генерить уникальный seed при каждой загрузке страницы и отправлять его в запросе, что бы там сохранился тот же случайный порядок, интересно как бы я пришел к этому варианту не будь под рукой ИИ, в интересное время живем однако, без неё пришлось бы часами лазить по гуглу или ждать пока тут или на хабре ответят
Пример кода
// В functions.php или в файле плагина // 1. Генерируем случайный порядок один раз при первой загрузке function custom_random_pagination_seed() { if (!isset($_SESSION['random_seed'])) { $_SESSION['random_seed'] = rand(); } return $_SESSION['random_seed']; } // 2. Модифицируем запрос для использования фиксированного случайного порядка add_action('pre_get_posts', function($query) { if ($query->is_main_query() && $query->get('orderby') === 'rand') { $query->set('orderby', 'RAND(' . custom_random_pagination_seed() . ')'); } }); // 3. В AJAX-обработчике убедитесь, что передаете тот же seed add_action('wp_ajax_custom_ajax_pagination', 'custom_ajax_pagination'); add_action('wp_ajax_nopriv_custom_ajax_pagination', 'custom_ajax_pagination'); function custom_ajax_pagination() { // Получаем seed из сессии или из запроса $seed = isset($_POST['seed']) ? intval($_POST['seed']) : custom_random_pagination_seed(); $args = array( 'post_type' => 'post', 'orderby' => 'RAND(' . $seed . ')', 'paged' => $_POST['page'], 'post_status' => 'publish' ); $query = new WP_Query($args); // ... остальной код обработки AJAX wp_die(); }
Только чутка подредактирую, так как иначе $seed вообще не будет меняться до закрытия браузера, хотя я теперь задумался о том зачем вообще что-то хранить в сессии если после перезагрузки нужен новый $seed, по идее его можно так отправлять в ajax запросе
function custom_random_pagination_seed(): int { session_start(); $_SESSION['random_seed'] = (int)str_pad(rand(), 10, '0'); return $_SESSION['random_seed']; }
Спасибо за описание здесь интересного решение проблемы рандомной сортировки. Это в заметки можно вынести, позже может перенесу с небольшими правками.