Come risolvere la paginazione per i loop personalizzati?

28 ott 2013, 21:00:53
Visualizzazioni: 136K
Voti: 144

Ho aggiunto una query personalizzata/secondaria a un file template/modello di pagina personalizzato; come posso fare in modo che WordPress utilizzi la mia query personalizzata per la paginazione, invece di utilizzare la paginazione del loop della query principale?

Nota Aggiuntiva

Ho modificato la query del loop principale tramite query_posts(). Perché la paginazione non funziona e come posso risolvere?

1
Commenti

Leggi anche questo tutorial: https://devnote.in/wordpress-paginate_links-how-to-use-it Sarà molto più utile.

Fefar Ravi Fefar Ravi
5 ott 2021 19:34:53
Tutte le risposte alla domanda 5
11
247

Il Problema

Per impostazione predefinita, in qualsiasi contesto, WordPress utilizza la query principale per determinare l'impaginazione. L'oggetto della query principale è memorizzato nella variabile globale $wp_query, che viene utilizzata anche per generare il loop della query principale:

if ( have_posts() ) : while ( have_posts() ) : the_post();

Quando si utilizza una query personalizzata, si crea un oggetto query completamente separato:

$custom_query = new WP_Query( $custom_query_args );

E quella query viene generata tramite un loop completamente separato:

if ( $custom_query->have_posts() ) : 
    while ( $custom_query->have_posts() ) : 
        $custom_query->the_post();

Ma i tag template per l'impaginazione, inclusi previous_posts_link(), next_posts_link(), posts_nav_link() e paginate_links(), basano il loro output sull'oggetto query principale, $wp_query. Quella query principale potrebbe essere impaginata o meno. Se il contesto corrente è un template di pagina personalizzato, ad esempio, l'oggetto principale $wp_query consisterà in un singolo post - quello dell'ID della pagina a cui è assegnato il template personalizzato.

Se il contesto corrente è un indice di archivio di qualche tipo, la query principale $wp_query potrebbe contenere abbastanza post da causare l'impaginazione, il che porta alla parte successiva del problema: per l'oggetto query principale $wp_query, WordPress passerà un parametro paged alla query, basato sulla variabile di query URL paged. Quando la query viene recuperata, quel parametro paged verrà utilizzato per determinare quale insieme di post impaginati restituire. Se viene cliccato un link di impaginazione visualizzato e viene caricata la pagina successiva, la tua query personalizzata non avrà alcun modo di sapere che l'impaginazione è cambiata.

La Soluzione

Passare il Parametro Paged Corretto alla Query Personalizzata

Supponendo che la query personalizzata utilizzi un array di argomenti:

$custom_query_args = array(
    // Parametri della query personalizzata vanno qui
);

Sarà necessario passare il parametro paged corretto all'array. Puoi farlo recuperando la variabile di query URL utilizzata per determinare la pagina corrente, tramite get_query_var():

get_query_var( 'paged' );

Puoi quindi aggiungere quel parametro al tuo array di argomenti della query personalizzata:

$custom_query_args['paged'] = get_query_var( 'paged' ) 
    ? get_query_var( 'paged' ) 
    : 1;

Nota: Se la tua pagina è una pagina frontale statica, assicurati di utilizzare page invece di paged poiché una pagina frontale statica utilizza page e non paged. Questo è ciò che dovresti avere per una pagina frontale statica

$custom_query_args['paged'] = get_query_var( 'page' ) 
    ? get_query_var( 'page' ) 
    : 1;

Ora, quando la query personalizzata viene recuperata, verrà restituito il set corretto di post impaginati.

Utilizzare l'Oggetto Query Personalizzata per le Funzioni di Impaginazione

Affinché le funzioni di impaginazione producano l'output corretto - cioè i link precedenti/successivi/pagina relativi alla query personalizzata - WordPress deve essere forzato a riconoscere la query personalizzata. Ciò richiede un piccolo "hack": sostituire l'oggetto query principale $wp_query con l'oggetto query personalizzato, $custom_query:

Modificare l'oggetto query principale

  1. Eseguire il backup dell'oggetto query principale: $temp_query = $wp_query
  2. Annullare l'oggetto query principale: $wp_query = NULL;
  3. Sostituire la query personalizzata nell'oggetto query principale: $wp_query = $custom_query;

    $temp_query = $wp_query;
    $wp_query   = NULL;
    $wp_query   = $custom_query;
    

Questo "hack" deve essere fatto prima di chiamare qualsiasi funzione di impaginazione

Ripristinare l'oggetto query principale

Una volta che le funzioni di impaginazione sono state generate, ripristinare l'oggetto query principale:

$wp_query = NULL;
$wp_query = $temp_query;

Correzioni alle Funzioni di Impaginazione

La funzione previous_posts_link() funzionerà normalmente, indipendentemente dall'impaginazione. Determina semplicemente la pagina corrente e poi genera il link per pagina - 1. Tuttavia, è necessaria una correzione per next_posts_link() per generare correttamente l'output. Questo perché next_posts_link() utilizza il parametro max_num_pages:

<?php next_posts_link( $label , $max_pages ); ?>

Come per altri parametri di query, per impostazione predefinita la funzione utilizzerà max_num_pages per l'oggetto query principale $wp_query. Per forzare next_posts_link() a considerare l'oggetto $custom_query, sarà necessario passare max_num_pages alla funzione. Puoi recuperare questo valore dall'oggetto $custom_query: $custom_query->max_num_pages:

<?php next_posts_link( 'Post più vecchi' , $custom_query->max_num_pages ); ?>

Mettendo tutto insieme

Quello che segue è un costrutto di base di un loop di query personalizzato con funzioni di impaginazione che funzionano correttamente:

// Definire i parametri della query personalizzata
$custom_query_args = array( /* Parametri vanno qui */ );

// Ottenere la pagina corrente e aggiungerla all'array dei parametri della query personalizzata
$custom_query_args['paged'] = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1;

// Istanzare la query personalizzata
$custom_query = new WP_Query( $custom_query_args );

// Correzione dell'impaginazione
$temp_query = $wp_query;
$wp_query   = NULL;
$wp_query   = $custom_query;

// Generare il loop della query personalizzata
if ( $custom_query->have_posts() ) :
    while ( $custom_query->have_posts() ) :
        $custom_query->the_post();
        // Output del loop va qui
    endwhile;
endif;
// Ripristinare i dati del post
wp_reset_postdata();

// Impaginazione del loop della query personalizzata
previous_posts_link( 'Post più vecchi' );
next_posts_link( 'Post più recenti', $custom_query->max_num_pages );

// Ripristinare l'oggetto query principale
$wp_query = NULL;
$wp_query = $temp_query;

Addendum: E query_posts()?

query_posts() per Loop Secondari

Se stai utilizzando query_posts() per generare un loop personalizzato, anziché istanziare un oggetto separato per la query personalizzata tramite WP_Query(), allora stai _doing_it_wrong() e incorrerai in diversi problemi (non il minimo dei quali saranno problemi di impaginazione). Il primo passo per risolvere questi problemi sarà convertire l'uso improprio di query_posts() in una chiamata corretta a WP_Query().

Utilizzare query_posts() per Modificare il Loop Principale

Se vuoi semplicemente modificare i parametri per la query del loop principale - come cambiare il numero di post per pagina o escludere una categoria - potresti essere tentato di utilizzare query_posts(). Ma non dovresti comunque. Quando utilizzi query_posts(), forzi WordPress a sostituire l'oggetto query principale. (WordPress in realtà effettua una seconda query e sovrascrive $wp_query.) Il problema, però, è che fa questa sostituzione troppo tardi nel processo per aggiornare l'impaginazione.

La soluzione è filtrare la query principale prima che i post vengano recuperati, tramite l'hook pre_get_posts.

Invece di aggiungere questo al file del template della categoria (category.php):

query_posts( array(
    'posts_per_page' => 5
) );

Aggiungi quanto segue a functions.php:

function wpse120407_pre_get_posts( $query ) {
    // Test per l'indice dell'archivio della categoria
    // e assicurarsi che la query sia la query principale
    // e non una query secondaria (come l'output di un menu di navigazione
    // o di un widget di post recenti, ecc.
    if ( is_category() && $query->is_main_query() ) {
        // Modificare i post per pagina
        $query->set( 'posts_per_page', 5 ); 
    }
}
add_action( 'pre_get_posts', 'wpse120407_pre_get_posts' );

Invece di aggiungere questo al file del template dell'indice dei post del blog (home.php):

query_posts( array(
    'cat' => '-5'
) );

Aggiungi quanto segue a functions.php:

function wpse120407_pre_get_posts( $query ) {
    // Test per l'indice principale dei post del blog
    // e assicurarsi che la query sia la query principale
    // e non una query secondaria (come l'output di un menu di navigazione
    // o di un widget di post recenti, ecc.
    if ( is_home() && $query->is_main_query() ) {
        // Escludere la categoria ID 5
        $query->set( 'category__not_in', array( 5 ) ); 
    }
}
add_action( 'pre_get_posts', 'wpse120407_pre_get_posts' );

In questo modo, WordPress utilizzerà l'oggetto $wp_query già modificato quando determina l'impaginazione, senza necessità di modifiche al template.

Quando utilizzare quale funzione

Studia questa domanda e risposta e questa domanda e risposta per capire come e quando utilizzare WP_Query, pre_get_posts e query_posts().

28 ott 2013 21:00:53
Commenti

Vorrei che le pagine del codex potessero essere così complete.

Pieter Goosen Pieter Goosen
3 apr 2014 15:41:19

Chip, mi hai fatto la giornata!

tepkenvannkorn tepkenvannkorn
8 set 2014 11:57:18

Chip, risparmi sempre così tanto tempo! Se solo Google classificasse le tue risposte più in alto (un appello per i googler) prima che io impazzissi a cercare ;) grazie.

Sagive Sagive
14 set 2014 18:56:33

Usando il tuo esempio non sono riuscito a far funzionare l'impaginazione finché non ho utilizzato un blocco if-else come quello trovato a metà pagina (invece del condizionale ? :) su questa pagina: http://themeforest.net/forums/thread/pagination-on-wordpress-static-page-set-to-front-page/28120, molto strano. A parte questo, questa risposta mi ha insegnato molto.

P a u l P a u l
8 dic 2014 08:52:43

Ottima risposta - 1 cosa, stavo avendo problemi nell'eseguire la funzione next/previous post link in una chiamata ajax - semplicemente non funzionava - dopo una rapida ricerca ho scoperto che la variabile globale paged non veniva aggiornata (ovviamente qualcosa legato all'ambiente admin-ajax.php) quindi ho aggiunto questo: global $paged; $paged = $custom_query_args['paged']; e ha funzionato :)

rmorse rmorse
20 dic 2014 18:00:12

...Non uso la parola 'eroe' con leggerezza, ma tu sei il più grande eroe nella storia americana. - Lionel Hutz

dgo dgo
5 gen 2015 20:05:26

Ora ho la paginazione per il mio loop personalizzato, su una pagina principale, che mostra le pagine figlie. Quando provo ad accedere alla seconda pagina tramite http:example.com/main_page/page/2, viene semplicemente reindirizzato a http://example.com/main_page. Qualcuno sa perché?

Mr Pablo Mr Pablo
6 lug 2016 13:39:40

E ricorda di ripulire la struttura dei permalink (risalva le impostazioni della struttura dei permalink)

Dan. Dan.
28 nov 2016 21:31:54

Questa soluzione funziona ancora in WordPress 4.5+? Quando seguo queste istruzioni il risultato mostra un "post più recenti" nella pagina 1, cliccandoci si va alla pagina 2, ma i risultati della sottoquery sono ancora i primi 10 post.

Slam Slam
19 lug 2018 03:16:04

Ho lottato con la mia paginazione per così tanto tempo, poi ho letto questa piccola pepita: Nota: Se la tua pagina è una pagina frontale statica, assicurati di usare page invece di paged poiché una pagina frontale statica utilizza page e non paged. Questo è ciò che dovresti avere per una pagina frontale statica

user1676224 user1676224
25 giu 2019 16:06:34

assolutamente perfetto, funziona alla grande. una delle poche soluzioni a un problema che ho visto spiegato in modo così chiaro e che funziona davvero :D

djack109 djack109
1 giu 2020 14:50:58
Mostra i restanti 6 commenti
0
22

Utilizzo questo codice per un loop personalizzato con paginazione:

<?php
if ( get_query_var('paged') ) {
    $paged = get_query_var('paged');
} elseif ( get_query_var('page') ) { // 'page' viene usato invece di 'paged' nella Pagina Frontale Static
    $paged = get_query_var('page');
} else {
    $paged = 1;
}

$custom_query_args = array(
    'post_type' => 'post', 
    'posts_per_page' => get_option('posts_per_page'),
    'paged' => $paged,
    'post_status' => 'publish',
    'ignore_sticky_posts' => true,
    //'category_name' => 'custom-cat',
    'order' => 'DESC', // 'ASC'
    'orderby' => 'date' // modified | title | name | ID | rand
);
$custom_query = new WP_Query( $custom_query_args );

if ( $custom_query->have_posts() ) :
    while( $custom_query->have_posts() ) : $custom_query->the_post(); ?>

        <article <?php post_class(); ?>>
            <h3><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
            <small><?php the_time('j F Y') ?> di <?php the_author_posts_link() ?></small>
            <div><?php the_excerpt(); ?></div>
        </article>

    <?php
    endwhile;
    ?>

    <?php if ($custom_query->max_num_pages > 1) : // paginazione personalizzata  ?>
        <?php
        $orig_query = $wp_query; // fix per far funzionare la paginazione
        $wp_query = $custom_query;
        ?>
        <nav class="prev-next-posts">
            <div class="prev-posts-link">
                <?php echo get_next_posts_link( 'Post più vecchi', $custom_query->max_num_pages ); ?>
            </div>
            <div class="next-posts-link">
                <?php echo get_previous_posts_link( 'Post più recenti' ); ?>
            </div>
        </nav>
        <?php
        $wp_query = $orig_query; // fix per far funzionare la paginazione
        ?>
    <?php endif; ?>

<?php
    wp_reset_postdata(); // resetta la query 
else:
    echo '<p>'.__('Spiacenti, nessun post corrisponde ai tuoi criteri.').'</p>';
endif;
?>

Fonte:

9 nov 2015 18:27:40
1

Fantastico come sempre Chip. Come aggiunta a questo, considera la situazione in cui stai utilizzando un template di pagina globale associato a una Pagina per del "testo introduttivo" seguito da una subquery che desideri sia impaginata.

Utilizzando paginate_links() come menzioni sopra, con per lo più impostazioni predefinite, (e assumendo che tu abbia i permalink accattivanti attivati) i tuoi link di impaginazione saranno predefiniti come mysite.ca/page-slug/page/# che è carino ma genererà errori 404 perché WordPress non conosce quella particolare struttura di URL e cercherà effettivamente una pagina figlia di "page" che è figlia di "page-slug".

Il trucco qui è inserire una regola di riscrittura astuta che si applica solo a quel particolare "slug di pagina pseudo-archivio" che accetta la struttura /page/#/ e la riscrive in una stringa di query che WordPress PUÒ comprendere, ovvero mysite.ca/?pagename=page-slug&paged=#. Nota pagename e paged non name e page (che mi ha causato letteralmente ORE di problemi, motivando questa risposta qui!).

Ecco la regola di reindirizzamento:

add_rewrite_rule( "page-slug/page/([0-9]{1,})/?$", 'index.php?pagename=page-slug&paged=$matches[1]', "top" );

Come sempre, quando modifichi le regole di riscrittura, ricorda di svuotare i tuoi permalink visitando Impostazioni > Permalink nel pannello di amministrazione.

Se hai più pagine che si comporteranno in questo modo (ad esempio, quando hai a che fare con più tipi di post personalizzati), potresti voler evitare di creare una nuova regola di riscrittura per ogni slug di pagina. Possiamo scrivere un'espressione regolare più generica che funzioni per qualsiasi slug di pagina che identifichi.

Un approccio è il seguente:

function wpse_120407_pseudo_archive_rewrite(){
    // Aggiungi gli slug delle pagine che utilizzano un Template Globale per simulare di essere una pagina "archivio"
    $pseudo_archive_pages = array(
        "all-movies",
        "all-actors"
    );

    $slug_clause = implode( "|", $pseudo_archive_pages );
    add_rewrite_rule( "($slug_clause)/page/([0-9]{1,})/?$", 'index.php?pagename=$matches[1]&paged=$matches[2]', "top" );
}
add_action( 'init', 'wpse_120407_pseudo_archive_rewrite' );

Svantaggi / Avvertenze

Uno svantaggio di questo approccio che mi fa venire un po' la nausea è la codifica fissa dello slug della Pagina. Se un amministratore cambia mai lo slug della pagina di quel pseudo-archivio, sei fritto - la regola di riscrittura non corrisponderà più e otterrai il temuto 404.

Non sono sicuro di poter pensare a una soluzione alternativa per questo metodo, ma sarebbe bello se fosse il template di pagina globale a innescare in qualche modo la regola di riscrittura. Un giorno potrei rivisitare questa risposta se nessun altro avrà risolto questo particolare problema.

17 dic 2016 02:49:08
Commenti

Potresti agganciarti al salvataggio del post, verificare se la pagina ha il tuo template di archivio sotto la meta key _wp_page_template, poi aggiungere un altro rewrite e svuotare le regole.

Milo Milo
17 dic 2016 02:54:39
0

Ho modificato la query principale del loop con query_posts(). Perché la paginazione non funziona e come posso risolvere?

La grande risposta creata da Chip necessita di una modifica oggi.
Da qualche tempo abbiamo la variabile $wp_the_query che dovrebbe essere uguale alla globale $wp_query subito dopo l'esecuzione della query principale.

Questo è il motivo per cui la parte della risposta di Chip:

Modifica l'oggetto della query principale

non è più necessaria. Possiamo dimenticarci di questa parte con la creazione della variabile temporanea.

// Correzione paginazione
$temp_query = $wp_query;
$wp_query   = NULL;
$wp_query   = $custom_query;

Quindi ora possiamo chiamare:

$wp_query   = $wp_the_query;

o ancora meglio possiamo chiamare:

wp_reset_query();

Tutto il resto che Chip ha delineato rimane valido. Dopo quella parte di reset della query puoi chiamare le funzioni di paginazione che sono f($wp_query), — dipendono dalla globale $wp_query.


Per migliorare ulteriormente la meccanica della paginazione e dare più libertà alla funzione query_posts ho creato questo possibile miglioramento:

https://core.trac.wordpress.org/ticket/39483

13 gen 2017 23:21:24
0
global $wp_query;
        $paged = get_query_var('paged', 1);

    $args = array( 
        'post_type' => '{your_post_type_name}',
        'meta_query' => array('{add your meta query argument if need}'),  
        'orderby' => 'modified',
        'order' => 'DESC',
        'posts_per_page' => 20,
        'paged' => $paged 
    );
    $query = new WP_Query($args);

    if($query->have_posts()):
        while ($query->have_posts()) : $query->the_post();
            //aggiungi il tuo codice qui
        endwhile;
        wp_reset_query();

        //gestisci la paginazione basata sulla Query personalizzata.
        $GLOBALS['wp_query']->max_num_pages = $query->max_num_pages;
        the_posts_pagination(array(
            'mid_size' => 1,
            'prev_text' => __('Pagina precedente', 'patelextensions'),
            'next_text' => __('Pagina successiva', 'patelextensions'),
            'before_page_number' => '<span class="meta-nav screen-reader-text">' . __('Pagina', 'patelextensions') . ' </span>',
        ));
    else:
    ?>
        <div class="container text-center"><?php echo _d('Nessun risultato trovato','30'); ?></div>
    <?php
        endif;
    ?>
6 mar 2018 11:39:57