Wie behebt man die Paginierung für benutzerdefinierte Schleifen?

Ich habe eine benutzerdefinierte / sekundäre Abfrage zu einer Vorlagendatei / benutzerdefinierten Seitenvorlage hinzugefügt. Wie kann ich WordPress dazu bringen, meine benutzerdefinierte Abfrage für die Paginierung zu verwenden, anstatt die Paginierung der Hauptabfrageschleife zu verwenden?

Nachtrag

Ich habe die Abfrage der Hauptschleife über query_posts() geändert. Warum funktioniert die Paginierung nicht und wie behebe ich sie?

Solutions Collecting From Web of "Wie behebt man die Paginierung für benutzerdefinierte Schleifen?"

Das Problem

Standardmäßig verwendet WordPress in jedem beliebigen Kontext die Hauptabfrage, um die Seitennummerierung zu bestimmen. Das Hauptabfrageobjekt wird im $wp_query global gespeichert, das auch für die Ausgabe der Hauptabfrage-Schleife verwendet wird:

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

Wenn Sie eine benutzerdefinierte Abfrage verwenden , erstellen Sie ein vollständig separates Abfrageobjekt:

 $custom_query = new WP_Query( $custom_query_args ); 

Und diese Abfrage wird über eine ganz eigene Schleife ausgegeben:

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

next_posts_link() Tags, einschließlich previous_posts_link() , next_posts_link() , posts_nav_link() und next_posts_link() , posts_nav_link() ihre Ausgabe jedoch auf dem Hauptabfrageobjekt $wp_query . Diese Hauptabfrage kann paginiert sein oder nicht. Wenn der aktuelle Kontext beispielsweise eine benutzerdefinierte Seitenvorlage ist, besteht das $wp_query aus nur einem einzigen Post – dem der ID der Seite, der die benutzerdefinierte Seitenvorlage zugewiesen wurde.

Wenn der aktuelle Kontext ein Archivindex irgendeiner Art ist, kann die Haupt $wp_query aus genügend Posts bestehen, um Paginierung zu verursachen, was zum nächsten Teil des Problems führt: für das Haupt $wp_query Objekt $wp_query WordPress einen paged Parameter an die Abfrage basierend auf der paged URL-Abfragevariablen. Wenn die Abfrage abgerufen wird, wird dieser paged Parameter verwendet, um zu bestimmen, welcher Satz von paginierten Posts zurückgegeben werden soll. Wenn auf einen angezeigten Seitenumbruch-Link geklickt wird und die nächste Seite geladen wird, kann Ihre benutzerdefinierte Abfrage nicht wissen, dass sich die Seitenumbruch geändert hat .

Die Lösung

Übergeben des korrekten paged-Parameters an die benutzerdefinierte Abfrage

Angenommen, die benutzerdefinierte Abfrage verwendet ein args-Array:

 $custom_query_args = array( // Custom query parameters go here ); 

Sie müssen den richtigen paged Parameter an das Array übergeben. Sie können dies tun, indem Sie die URL- get_query_var() die zum Ermitteln der aktuellen Seite verwendet wird, über get_query_var() :

 get_query_var( 'paged' ); 

Sie können diesen Parameter dann an Ihren benutzerdefinierten Abfrageargumentarray anhängen:

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

Hinweis: Wenn es sich bei Ihrer Seite um eine statische Titelseite handelt , verwenden Sie die page statt der Seite , da eine statische Titelseite die page und nicht paged . Dies ist, was Sie für eine statische Titelseite haben sollten

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

Jetzt, wenn die benutzerdefinierte Abfrage abgerufen wird, wird der korrekte Satz paginierter Posts zurückgegeben.

Benutzerdefiniertes Abfrageobjekt für Seitenumbruchfunktionen verwenden

Damit Seitenumbruchfunktionen die richtige Ausgabe liefern – dh vorherige / nächste / Seitenverweise relativ zur benutzerdefinierten Abfrage – muss WordPress gezwungen werden, die benutzerdefinierte Abfrage zu erkennen. Dies erfordert ein bisschen “Hack”: Ersetzen des $wp_query durch das benutzerdefinierte Abfrageobjekt $custom_query :

Hack das Hauptabfrageobjekt

  1. Sichern Sie das Hauptabfrageobjekt: $temp_query = $wp_query
  2. Null das Hauptabfrageobjekt: $wp_query = NULL;
  3. Tauschen Sie die benutzerdefinierte Abfrage in das Hauptabfrageobjekt aus: $wp_query = $custom_query;

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

Dieser “Hack” muss vor dem Aufruf von Paginierungsfunktionen durchgeführt werden

Setzen Sie das Hauptabfrageobjekt zurück

Nachdem Paginierungsfunktionen ausgegeben wurden, setzen Sie das Hauptabfrageobjekt zurück:

 $wp_query = NULL; $wp_query = $temp_query; 

Paginierung function korrigiert

Die function previous_posts_link() funktioniert unabhängig von der Paginierung normal. Es bestimmt lediglich die aktuelle Seite und gibt dann den Link für page - 1 . Es ist jedoch eine Korrektur erforderlich, damit next_posts_link() korrekt ausgegeben wird. Dies liegt daran, dass next_posts_link() den Parameter max_num_pages :

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

Wie bei anderen Abfrageparametern verwendet die function standardmäßig max_num_pages für das $wp_query . Um zu erzwingen, dass next_posts_link() das $custom_query Objekt berücksichtigt, müssen Sie die max_num_pages an die function übergeben. Sie können diesen Wert vom $custom_query Objekt $custom_query : $custom_query->max_num_pages :

 < ?php next_posts_link( 'Older Posts' , $custom_query->max_num_pages ); ?> 

Alles zusammensetzen

Das folgende ist ein grundlegendes Konstrukt einer benutzerdefinierten Abfrage-Schleife mit ordnungsgemäß funktionierenden Paginierungsfunktionen:

 // Define custom query parameters $custom_query_args = array( /* Parameters go here */ ); // Get current page and append to custom query parameters array $custom_query_args['paged'] = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1; // Instantiate custom query $custom_query = new WP_Query( $custom_query_args ); // Pagination fix $temp_query = $wp_query; $wp_query = NULL; $wp_query = $custom_query; // Output custom query loop if ( $custom_query->have_posts() ) : while ( $custom_query->have_posts() ) : $custom_query->the_post(); // Loop output goes here endwhile; endif; // Reset postdata wp_reset_postdata(); // Custom query loop pagination previous_posts_link( 'Older Posts' ); next_posts_link( 'Newer Posts', $custom_query->max_num_pages ); // Reset main query object $wp_query = NULL; $wp_query = $temp_query; 

Nachtrag: Was ist mit query_posts() ?

query_posts() für sekundäre Schleifen

Wenn Sie query_posts() um eine benutzerdefinierte Schleife auszugeben, anstatt ein separates Objekt für die benutzerdefinierte Abfrage über WP_Query() , dann werden Sie _doing_it_wrong() , und es werden mehrere Probleme auftreten (nicht zuletzt Paginierungsprobleme sein). Der erste Schritt zum Beheben dieser Probleme besteht darin, die unsachgemäße Verwendung von query_posts() in einen ordnungsgemäßen WP_Query() Aufruf zu konvertieren.

Verwenden von query_posts() zum Ändern der Hauptschleife

Wenn Sie lediglich die Parameter für die Abfrage der Hauptschleife ändern möchten (z. B. Ändern der Beiträge pro Seite oder Ausschließen einer Kategorie), könnten Sie versucht sein, query_posts() . Aber du solltest es immer noch nicht. Wenn Sie query_posts() , erzwingen Sie, dass WordPress das Hauptabfrageobjekt ersetzt. (WordPress erstellt tatsächlich eine zweite Abfrage und überschreibt $wp_query .) Das Problem besteht jedoch darin, dass dieser Austausch zu spät durchgeführt wird, um die Seitennumerierung zu aktualisieren.

Die Lösung besteht darin , die Hauptabfrage vor dem pre_get_posts Posts über den pre_get_posts Hook zu pre_get_posts .

Anstatt dies der Kategorievorlagendatei ( category.php ) hinzuzufügen:

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

Fügen Sie functions.php Folgendes hinzu:

 function wpse120407_pre_get_posts( $query ) { // Test for category archive index // and ensure that the query is the main query // and not a secondary query (such as a nav menu // or recent posts widget output, etc. if ( is_category() && $query->is_main_query() ) { // Modify posts per page $query->set( 'posts_per_page', 5 ); } } add_action( 'pre_get_posts', 'wpse120407_pre_get_posts' ); 

Anstatt dies der Blog-Post- home.php ( home.php ) home.php :

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

Fügen Sie functions.php Folgendes hinzu:

 function wpse120407_pre_get_posts( $query ) { // Test for main blog posts index // and ensure that the query is the main query // and not a secondary query (such as a nav menu // or recent posts widget output, etc. if ( is_home() && $query->is_main_query() ) { // Exclude category ID 5 $query->set( 'category__not_in', array( 5 ) ); } } add_action( 'pre_get_posts', 'wpse120407_pre_get_posts' ); 

Auf diese Weise verwendet WordPress das bereits geänderte $wp_query Objekt bei der Bestimmung der $wp_query , ohne dass eine Template-Änderung erforderlich ist.

Wann soll welche function verwendet werden?

Recherchieren Sie diese Frage und beantworten Sie diese Frage und beantworten Sie, um zu verstehen, wie und wann WP_Query , pre_get_posts und query_posts() .

Ich verwende diesen Code für benutzerdefinierte Schleife mit Seitenumbruch:

 < ?php if ( get_query_var('paged') ) { $paged = get_query_var('paged'); } elseif ( get_query_var('page') ) { // 'page' is used instead of 'paged' on Static Front Page $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(); ?> 
>

< ?php the_title(); ?>

< ?php the_time('F jS, Y') ?> by < ?php the_author_posts_link() ?>
< ?php the_excerpt(); ?>
< ?php endwhile; ?> < ?php if ($custom_query->max_num_pages > 1) : // custom pagination ?> < ?php $orig_query = $wp_query; // fix for pagination to work $wp_query = $custom_query; ?> < ?php $wp_query = $orig_query; // fix for pagination to work ?> < ?php endif; ?> < ?php wp_reset_postdata(); // reset the query else: echo '

'.__('Sorry, no posts matched your criteria.').''; endif; ?>

Quelle:

  • Benutzerdefinierte WordPress-Schleife mit Seitenumbruch

Genial wie immer Chip. Beachten Sie als Nachtrag dazu die Situation, in der Sie eine globale Seitenvorlage verwenden, die an eine Seite für einen “Einführungstext” angehängt ist, und darauf eine Unterabfrage folgt, die Sie paginieren möchten.

Mit paginate_links (), wie Sie oben erwähnt haben, mit den meisten Standardeinstellungen (und vorausgesetzt, Sie haben schöne Permalinks aktiviert), werden Ihre Paginierungslinks standardmäßig auf mysite.ca/page-slug/page/# was sehr mysite.ca/page-slug/page/# ist, aber 404 Fehler wegen WordPress mysite.ca/page-slug/page/# wird weiß nicht über diese bestimmte URL-Struktur und wird tatsächlich nach einer untergeordneten Seite von “page” suchen, die ein Kind von “page-slug” ist.

Der Trick hier ist, eine raffinierte Rewrite-Regel einzufügen, die nur für den bestimmten Seiten-Slug der “Pseudo-Archiv-Seite” gilt, der die /page/#/ -Struktur akzeptiert und sie in einen Abfrage-String mysite.ca/?pagename=page-slug&paged=# , den WordPress verstehen kann, nämlich mysite.ca/?pagename=page-slug&paged=# . Notiz pagename und paged nicht name und page (was mich buchstäblich HOURS der Trauer verursacht, motivierend diese Antwort hier!).

Hier ist die Umleitungsregel:

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

Wie immer sollten Sie beim Ändern von Umschreibungsregeln Ihre Permalinks löschen, indem Sie im Admin-Back-End Einstellungen> Permalinks aufrufen.

Wenn Sie mehrere Seiten haben, die sich auf diese Weise verhalten (z. B. wenn Sie mit mehreren benutzerdefinierten Post-Typen arbeiten), sollten Sie möglicherweise vermeiden, für jeden Seiten-Slug eine neue Rewrite-Regel zu erstellen. Wir können einen allgemeineren regulären Ausdruck schreiben, der für jeden von Ihnen identifizierten Seitenblock funktioniert.

Ein Ansatz ist unten:

 function wpse_120407_pseudo_archive_rewrite(){ // Add the slugs of the pages that are using a Global Template to simulate being an "archive" page $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' ); 

Nachteile / Vorbehalte

Ein Nachteil dieses Ansatzes, der mich ein wenig in den Mund kotzen lässt, ist die Hartcodierung des Page Slugs. Wenn ein Admin den Seiten-Slug dieser Pseudo-Archiv-Seite ändert, bist du Toast – die Rewrite-Regel passt nicht mehr und du bekommst den gefürchteten 404.

Ich bin mir nicht sicher, ob ich mir einen Workaround für diese Methode vorstellen kann, aber es wäre schön, wenn es die globale Seitenvorlage wäre, die die Rewrite-Regel ausgetriggers hat. Eines Tages werde ich diese Antwort noch einmal durchgehen, wenn niemand sonst diese bestimmte Nuss geknackt hat.

Ich habe die Abfrage der Hauptschleife über query_posts() geändert. Warum funktioniert die Paginierung nicht und wie behebe ich sie?

Große Antwort Chip erstellt muss heute geändert werden.
Seit einiger Zeit haben wir die Variable $wp_query unmittelbar nach der $wp_query der Hauptabfrage dem $wp_query global entsprechen sollte.

Deshalb ist dies der Teil von Chip’s Antwort:

Hack das Hauptabfrageobjekt

wird nicht mehr benötigt. Wir können diesen Teil mit der Erstellung der temporären Variable vergessen.

 // Pagination fix $temp_query = $wp_query; $wp_query = NULL; $wp_query = $custom_query; 

So können wir jetzt anrufen:

 $wp_query = $wp_the_query; 

oder besser können wir anrufen:

 wp_reset_query(); 

Alles andere Chip skizziert bleibt. Nach diesem Query-Reset-Part können Sie die Paginierungsfunktionen aufrufen, die f($wp_query) , – sie hängen von $wp_query global ab.


Um die Paginierungsmechanik weiter zu verbessern und der function query_posts mehr Freiheit zu geben, query_posts ich diese mögliche Verbesserung geschaffen:

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