Случайный порядок в wp_query с разбиением на страницы

#php #wordpress #custom-post-type

Вопрос:

У меня есть шорткод, который запрашивает некоторые пользовательские сообщения, а также добавляет разбиение на страницы. Все работает, но я хочу рандомизировать порядок. Однако, когда я устанавливаю порядок «rand» в своих аргументах, каждая страница в моей разбивке на страницы полностью случайна — это означает, что одно и то же сообщение может появляться несколько раз на разных страницах. Как я могу рандомизировать порядок всех моих сообщений, но не на уровне каждой страницы? Вот мой код:

 function slaProductsArchive( $atts ){
    global $paged;
    $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
    $numposts = intval($atts['num']);
    $cat = $atts['cat'];
     $args = array(  
        'post_type' => 'product',
        'post_status' => 'publish',
        'posts_per_page' => $numposts, 
        'orderby' => 'rand',
        'product-category' => $cat,
        'paged' => $paged,
    );

    $loop = new WP_Query( $args ); 
    while ( $loop->have_posts() ) : $loop->the_post(); 

// Display my posts

    endwhile;
    $output .= '</div>';
    $output .= '<div class="slaPagination"><span class="prev-posts-links">' . get_previous_posts_link('<i class="fas fa-arrow-left"></i> Previous') . '</span> ';
    $output .= '<span class="next-posts-links">' . get_next_posts_link('Next <i class="fas fa-arrow-right"></i>', $loop->max_num_pages) . '</span></div>';
    
    wp_reset_postdata(); 
    return $output;
}
add_shortcode('slaProductsArchive', 'slaProductsArchive');
 

Комментарии:

1. Имейте в виду, что rand не предназначен для архивирования.

Ответ №1:

Есть несколько вещей, которые вы можете сделать, в зависимости от того, как вы хотели бы с этим справиться. Несколько идей:

  1. Посеять random порядок чем-то, что меняется
  2. Сохраните порядок «уже показанных» сообщений и исключите их
  3. Возьмите все сообщения и перемешайте их с семенем

Конечно, есть и другие способы, и числа 2 и 3 не особенно изящны вне очень специфических применений. Итак, давайте сосредоточимся на номере 1, посеве random .

posts_orderby Фильтр-это тот, который вы ищете. Это позволяет вам, ну… отфильтруйте, по чему упорядочены записи.

 add_filter( 'posts_orderby', 'so_69214414_edit_orderby' );
function so_69214414_edit_orderby( $orderby ){
    $seed = ''; // We need to figure out what to seed with
    return " RAND({$seed}) ";
}
 

Выяснил, что определение $seed » как » действительно зависит от вас, и как часто вы хотите, чтобы случайный порядок менялся.

Как это работает, так это то, что если вы RAND(1) — он будет заполнять случайное значение 1, так что до тех пор, пока сообщения не изменятся, они всегда будут в таком порядке. RAND(2) приведет к совершенно иному порядку, чем RAND(1) — но каждый раз, когда вы используете RAND(2) его, он один и тот же. Например:

 RAND(1) : [1, 3, 6, 2, 4, 5]
RAND(2) : [3, 5, 2, 1, 4, 6]
RAND(2) : [3, 5, 2, 1, 4, 6]
RAND(2) : [3, 5, 2, 1, 4, 6]
RAND(1) : [1, 3, 6, 2, 4, 5]
RAND(3) : [5, 1, 4, 3, 6, 2]
 

То, что я использую для одного из своих WP-сайтов, — это семена в день: $seed = date('M d, Y') . Таким образом, сообщения всегда «рандомизируются» в полночь каждую ночь, но страница 1, страница 2, страница 3 и т. Д. Всегда будут в порядке. Например:

 Day 1: p1[1, 3] p2[6, 2] p3[5, 4]
Day 2: p1[3, 5] p2[4, 1] p3[2, 6]
 

Если это не сработает для вашего usecase, вы можете использовать PHP-сессиями и хранения случайных чисел, или вы можете хранить случайных чисел в файле cookie и обновления, каждый раз, когда пользователь посещает «Страница 1» Итак, Страница 1 всегда является случайным, но страницы 2, 3, 4 всегда тянут с этого cookied-семя, и т. д.

Просто выясните, как часто вы хотите, чтобы семя менялось, а затем добавьте это в posts_orderby фильтр, и все будет готово.

Просто убедитесь , что вы используете его add_filter перед тем, как позвонить new WP_Query() , а remove_filter затем, если вы не хотите, чтобы он применялся в другом месте!

Комментарии:

1. Для этого не нужно использовать фильтр, так как они напрямую задают свои параметры запроса и создают свой собственный WP_Query, при этом не используется основной. Поэтому RAND(seedval) может быть помещен непосредственно в массив аргументов, wordpress.stackexchange.com/a/274163

2. Ах, спасибо за информацию! Я думаю, у меня есть несколько запросов, из которых я могу удалить этот фильтр!

Ответ №2:

Для этого я использовал сеанс WC, поскольку вы используете продукты, я предполагаю, что вы используете WooCommerce. Каждая страница хранится в переменной сеанса, поэтому, если вы вернетесь на предыдущую страницу, она будет такой же, как и раньше. Он также записывает отдельный массив сообщений, которые были сделаны, чтобы вы могли их использовать post__not_in .

Таким образом, хотя это создает случайный порядок, как только страница создается, это порядок при обновлении.

Я также обновил ваше использование shortcode_atts и поставил условие на случай, если не будет задана категория продукта.

 function slaProductsArchive( $atts ){
    $atts = shortcode_atts(
        array(
            'num' => 10,
            'cat' => ''
            ),
        $atts, 'slaProductsArchive' );

    // Retrieve Previous WC Session Vars
    $done = WC()->session->get('viewed_randoms', array());
    $my_page = WC()->session->get('my_page', array());


    global $paged;
    $paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
    if (!array_key_exists($paged, $my_page)) {
        if ( !empty( $atts['cat'] ) ) {
            $args = array(
                'post_type'      => 'product',
                'post_status'    => 'publish',
                'posts_per_page' => absint( $atts['num'] ),
                'orderby'        => 'rand',
                'paged'          => $paged,
                'product_cat'    => $atts['cat'],
                'post__not_in'   => $done
            );
        } else {
            $args = array(
                'post_type'      => 'product',
                'post_status'    => 'publish',
                'posts_per_page' => absint( $atts['num'] ),
                'orderby'        => 'rand',
                'paged'          => $paged,
                'post__not_in'   => $done
            );
        }
    } else {
        $args = array(
            'post_type'      => 'product',
            'post_status'    => 'publish',
            'post__in' => $my_page[$paged]
        );
    }
    // Initialize $output ** This wasn't here before ** 
    $output = '<div>';
    // A variable to store the POST ID's of this loop
    $this_loop = array();
    
    $loop = new WP_Query( $args );
    while ( $loop->have_posts() ) : $loop->the_post();
    
    // just to show something from the loop
    // You can remove this and put your own loop here 
    $output .= '<p>' . get_the_title() . ' ' . get_the_ID(). '</p>';

    // Set two array of the POST ID's
    $done[] = get_the_ID();
    $this_loop[] = get_the_ID();
    
    endwhile;
    $output .= '</div>';
    $output .= '<div class="slaPagination"><span class="prev-posts-links">' . get_previous_posts_link( '<i class="fas fa-arrow-left"></i> Previous' ) . '</span> ';
    $output .= '<span class="next-posts-links">' . get_next_posts_link( 'Next <i class="fas fa-arrow-right"></i>', $loop->max_num_pages ) . '</span></div>';

    // Set My Page Key =  Page Number - Value = each post ID
    $my_page[$paged] = $this_loop;
    // Store WC Session Var
    WC()->session->set('viewed_randoms', $done);
    WC()->session->set('my_page', $my_page);
    wp_reset_postdata();
    return $output;
}
add_shortcode( 'slaProductsArchive', 'slaProductsArchive' );
 

Альтернативный Метод Без WooCommerce

В этом методе я инициализирую сеанс PHP и использую файл cookie идентификатора сеанса для хранения переходного процесса, так как все остальное выполняется после отправки заголовков. Таким образом, сеанс предназначен только для идентификатора сеанса. Вы также можете просто установить файл cookie с любым именем, которое вы хотите, и использовать его для составленного сеанса, исключительно для использования переходных процессов в качестве этого сеанса.

 function dd_register_session(){
    if(!session_id() amp;amp; !headers_sent()) {
        session_start();
        session_write_close();
    }
}
add_action('init','dd_register_session', 1);

add_action('wp_logout', 'end_session');
add_action('wp_login', 'end_session');
add_action('end_session_action', 'end_session');

function end_session() {
    session_destroy ();
}

function slaProductsArchive( $atts ){
    $atts = shortcode_atts(
        array(
            'num' => 10,
            'cat' => ''
        ),
        $atts, 'slaProductsArchive' );

    // Retrieve Previous Session Data
    $done = $my_page = array();
    if (isset($_COOKIE['PHPSESSID'])) {
        if ( false !== ( $session_data = get_transient( $_COOKIE['PHPSESSID'] ) ) ) {
            $done = $session_data['done'];
            $my_page = $session_data['my_page'];
        }
    }
    global $paged;
    $paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
    if (!array_key_exists($paged, $my_page)) {
        if ( !empty( $atts['cat'] ) ) {
            $args = array(
                'post_type'      => 'product',
                'post_status'    => 'publish',
                'posts_per_page' => absint( $atts['num'] ),
                'orderby'        => 'rand',
                'paged'          => $paged,
                'product_cat'    => $atts['cat'],
                'post__not_in'   => $done
            );
        } else {
            $args = array(
                'post_type'      => 'product',
                'post_status'    => 'publish',
                'posts_per_page' => absint( $atts['num'] ),
                'orderby'        => 'rand',
                'paged'          => $paged,
                'post__not_in'   => $done
            );
        }
    } else {
        $args = array(
            'post_type'   => 'product',
            'post_status' => 'publish',
            'post__in'    => $my_page[$paged],
            'orderby'     => 'post__in'
        );
    }
    // Initialize $output ** This wasn't here before **
    $output = '<div>';
    // A variable to store the POST ID's of this loop
    $this_loop = array();

    $loop = new WP_Query( $args );
    while ( $loop->have_posts() ) : $loop->the_post();

        // just to show something from the loop
        // You can remove this and put your own loop here
        $output .= '<p>' . get_the_title() . ' ' . get_the_ID(). '</p>';

        // Set two array of the POST ID's
        $done[] = get_the_ID();
        $this_loop[] = get_the_ID();

    endwhile;
    $output .= '</div>';
    $output .= '<div class="slaPagination"><span class="prev-posts-links">' . get_previous_posts_link( '<i class="fas fa-arrow-left"></i> Previous' ) . '</span> ';
    $output .= '<span class="next-posts-links">' . get_next_posts_link( 'Next <i class="fas fa-arrow-right"></i>', $loop->max_num_pages ) . '</span></div>';

    // Set My Page Key =  Page Number - Value = each post ID
    $my_page[$paged] = $this_loop;
    if (isset($_COOKIE['PHPSESSID'])) {
        set_transient( $_COOKIE['PHPSESSID'] , array('done' => $done, 'my_page' => $my_page), 10 * MINUTE_IN_SECONDS );
    }
    wp_reset_postdata();
    return $output;
}
add_shortcode( 'slaProductsArchive', 'slaProductsArchive' );
 

Комментарии:

1. Эй, Говард, спасибо за ответ — к сожалению, я не пользуюсь туалетом. Это просто cpt с типом продукта.

2. Я обновил ответ, чтобы использовать идентификатор сеанса php в качестве файла cookie и переходные процессы WP для хранения порядка вывода.