Ich habe ziemlich ausführliche Nachforschungen angestellt, wie man pre_get_posts
True Pages und die statischen Front Pages verwendet, und es scheint, dass es keine narrensichere Methode gibt.
Die beste Option, die ich bisher gefunden habe, war ein Beitrag von @birgire auf Stackoverflow . Ich habe es in eine Demo-Klasse umgeschrieben und den Code etwas dynamischer gemacht
class PreGeTPostsForPages
{
/**
* @var string|int $pageID
* @access protected
* @since 1.0.0
*/
protected $pageID;
/**
* @var bool $injectPageIntoLoop
* @access protected
* @since 1.0.0
*/
protected $injectPageIntoLoop;
/**
* @var array $args
* @access protected
* @since 1.0.0
*/
protected $args;
/**
* @var int $validatedPageID
* @access protected
* @since 1.0.0
*/
protected $validatedPageID = 0;
/**
* Constructor
*
* @param string|int $pageID = NULL
* @param bool $injectPageIntoLoop = false
* @param array| $args = []
* @since 1.0.0
*/
public function __construct(
$pageID = NULL,
$injectPageIntoLoop = true,
$args = []
) {
$this->pageID = $pageID;
$this->injectPageIntoLoop = $injectPageIntoLoop;
$this->args = $args;
}
/**
* Private method validatePageID()
*
* Validates the page ID passed
*
* @since 1.0.0
*/
private function validatePageID()
{
$validatedPageID = filter_var( $this->pageID, FILTER_VALIDATE_INT );
$this->validatedPageID = $validatedPageID;
}
/**
* Public method init()
*
* This method is used to initialize our pre_get_posts action
*
* @since 1.0.0
*/
public function init()
{
// Load the correct actions according to the value of $this->keepPageIntegrity
add_action( 'pre_get_posts', [$this, 'preGetPosts'] );
}
/**
* Protected method pageObject()
*
* Gets the queried object to use that as page object
*
* @since 1.0.0
*/
protected function pageObject()
{
global $wp_the_query;
return $wp_the_query->get_queried_object();
}
/**
* Public method preGetPosts()
*
* This is our call back method for the pre_get_posts action.
*
* The pre_get_posts action will only be used if the page integrity is
* not an issue, which means that the page will be altered to work like a
* normal archive page. Here you have the option to inject the page object as
* first post through the_posts filter when $this->injectPageIntoLoop === true
*
* @since 1.0.0
*/
public function preGetPosts( \WP_Query $q )
{
// Make sure that we are on the main query and the desired page
if ( is_admin() // Only run this on the front end
|| !$q->is_main_query() // Only target the main query
|| !is_page( $this->validatedPageID ) // Run this only on the page specified
)
return;
// Remove the filter to avoid infinte loops
remove_filter( current_filter(), [$this, __METHOD__] );
// METHODS:
$this->validatePageID();
$this->pageObject();
$queryArgs = $this->args;
// Set default arguments which cannot be changed
$queryArgs['pagename'] = NULL;
// We have reached this point, lets do what we need to do
foreach ( $queryArgs as $key=>$value )
$q->set(
filter_var( $key, FILTER_SANITIZE_STRING ),
$value // Let WP_Query handle the sanitation of the values accordingly
);
// Set $q->is_singular to 0 to get pagination to work
$q->is_singular = false;
// FILTERS:
add_filter( 'the_posts', [$this, 'addPageAsPost'], PHP_INT_MAX );
add_filter( 'template_include', [$this, 'templateInclude'], PHP_INT_MAX );
}
/**
* Public callback method hooked to 'the_posts' filter
* This will inject the queried object into the array of posts
* if $this->injectPageIntoLoop === true
*
* @since 1.0.0
*/
public function addPageAsPost( $posts )
{
// Inject the page object as a post if $this->injectPageIntoLoop == true
if ( true === $this->injectPageIntoLoop )
return array_merge( [$this->pageObject()], $posts );
return $posts;
}
/**
* Public call back method templateInclude() for the template_include filter
*
* @since 1.0.0
*/
public function templateInclude( $template )
{
// Remove the filter to avoid infinte loops
remove_filter( current_filter(), [$this, __METHOD__] );
// Get the page template saved in db
$pageTemplate = get_post_meta(
$this->validatedPageID,
'_wp_page_template',
true
);
// Make sure the template exists before we load it, but only if $template is not 'default'
if ( 'default' !== $pageTemplate ) {
$locateTemplate = locate_template( $pageTemplate );
if ( $locateTemplate )
return $template = $locateTemplate;
}
/**
* If $template returned 'default', or the template is not located for some reason,
* we need to get and load the template according to template hierarchy
*
* @uses get_page_template()
*/
return $template = get_page_template();
}
}
$init = new PreGeTPostsForPages(
251, // Page ID
false,
[
'posts_per_page' => 3,
'post_type' => 'post'
]
);
$init->init();
Dies funktioniert gut und funktioniert wie erwartet mit meiner eigenen Paginierungsfunktion .
PROBLEME:
Aufgrund der Funktion verliere ich die Seitenintegrität, was andere Funktionen betrifft, die sich auf das in gespeicherte Seitenobjekt stützen $post
. $post
bevor die Schleife auf den ersten Post in der Schleife und $post
auf den letzten Post in der Schleife nach der Schleife gesetzt wird, was erwartet wird. Was ich brauche ist, dass $post
auf das aktuelle Seitenobjekt eingestellt ist, dh das abgefragte Objekt.
Außerdem enthält $wp_the_query->post
und $wp_query->post
den ersten Beitrag in der Schleife und nicht das abgefragte Objekt wie auf einer normalen Seite
Ich benutze das Folgende ( außerhalb meiner Klasse ), um meine Globalen vor und nach der Schleife zu überprüfen
add_action( 'wp_head', 'printGlobals' );
add_action( 'wp_footer', 'printGlobals' );
function printGlobals()
{
$global_test = 'QUERIED OBJECT: ' . $GLOBALS['wp_the_query']->queried_object_id . '</br>';
$global_test .= 'WP_THE_QUERY: ' . $GLOBALS['wp_the_query']->post->ID . '</br>';
$global_test .= 'WP_QUERY: ' . $GLOBALS['wp_query']->post->ID . '</br>';
$global_test .= 'POST: ' . $GLOBALS['post']->ID . '</br>';
$global_test .= 'FOUND_POSTS: ' . $GLOBALS['wp_query']->found_posts . '</br>';
$global_test .= 'MAX_NUM_PAGES: ' . $GLOBALS['wp_query']->max_num_pages . '</br>';
?><pre><?php var_dump( $global_test ); ?></pre><?php
}
VOR DER SCHLEIFE:
Vor der Schleife wird das Problem teilweise gelöst, indem $injectPageIntoLoop
auf true gesetzt wird, wodurch das Seitenobjekt als erste Seite in die Schleife eingefügt wird. Dies ist sehr nützlich, wenn Sie die Seiteninformationen vor den angeforderten Posts anzeigen müssen, aber wenn Sie das nicht möchten, sind Sie fertig.
Ich kann das Problem vor der Schleife lösen, indem ich die Globals direkt hacke, was mir nicht wirklich gefällt. Ich binde die folgende Methode in wp
meine preGetPosts
Methode ein
public function wp()
{
$page = get_post( $this->pageID );
$GLOBALS['wp_the_query']->post = $page;
$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
$GLOBALS['post'] = $page;
}
und innere preGetPosts
Methode
add_action( 'wp', [$this, 'wp'] );
Daraus $wp_the_query->post
, $wp_query->post
und $post
alles enthält das Seitenobjekt.
Nach der Schleife
Hier liegt mein großes Problem nach der Schleife. Nach dem Hacken der Globals durch den wp
Haken und die Methode,
$wp_the_query->post
und$wp_query->post
wird wie erwartet auf den ersten Post in der Schleife zurückgesetzt$post
wird auf den letzten Beitrag in der Schleife gesetzt.
Was ich brauche ist, dass alle drei auf das abgefragte Objekt / aktuelle Seitenobjekt zurückgesetzt werden.
Ich habe versucht, die wp
Methode mit der loop_end
Aktion zu verknüpfen, was nicht funktioniert. Das Einhängen der wp
Methode in die get_sidebar
Aktion funktioniert, aber es ist zu spät.
add_action( 'get_sidebar', [$this, 'wp'] );
Wird printGlobals()
direkt nach der Schleife in der Vorlage ausgeführt, wird bestätigt, dass als $wp_the_query->post
und $wp_query->post
weiterhin der erste und $post
der letzte Beitrag festgelegt sind.
Ich kann den Code manuell innerhalb der wp
Methode nach der Schleife in der Vorlage hinzufügen , aber die Idee ist nicht, die Vorlagendateien direkt zu ändern, da die Klasse in einem Plugin zwischen Themen übertragbar sein sollte.
Gibt es einen richtigen Weg , um dieses Problem , bei dem ein Laufe zu lösen pre_get_posts
auf einer wahren Seite und statische Titelseite und immer noch die Integrität halten $wp_the_query->post
, $wp_query->post
und $post
( derjenigediejenigedasjenige Satz zu dem abgefragten Objekt mit ) vor und nach der Schleife.
BEARBEITEN
Es scheint Verwirrung darüber zu geben, was ich brauche und warum ich es brauche
Was ich brauche
Ich brauche die Werte zu erhalten $wp_the_query->post
, $wp_query->post
und $post
über die Vorlage unabhängig, und dieser Wert sollte das abgefragten Objekt sein. Zu diesem Zeitpunkt enthalten die Werte dieser drei Variablen mit dem von mir veröffentlichten Code nicht das Seitenobjekt, sondern veröffentlichen Objekte von Posts in der Schleife. Ich hoffe das ist klar genug.
Ich habe Code veröffentlicht, mit dem Sie diese Variablen testen können
Warum ich es brauche
Ich brauche eine zuverlässige Methode, um Posts über pre_get_posts
zu Seitenvorlagen und statischen Titelseiten hinzuzufügen, ohne die volle Seitenfunktionalität zu ändern. In diesem Stadium, wie der fragliche Code steht, bricht er meine Breadcrumb-Funktion und die zugehörige Seitenfunktion nach der Schleife, aufgrund $post
derer das "falsche" Post-Objekt enthalten ist.
Vor allem möchte ich die Seitenvorlagen nicht direkt ändern. Ich möchte in der Lage sein , ohne Beiträge zu einer Seitenvorlage hinzuzufügen jedem der Vorlage Änderung
quelle
Antworten:
Ich habe es endlich zum Laufen gebracht, aber nicht mit dem Code in meiner Frage. Ich habe die ganze Idee komplett verworfen und neu angefangen, in eine neue Richtung zu gehen.
HINWEIS:
Wenn jemand jemals in der Lage ist, die Probleme in meiner Frage zu klären, können Sie gerne eine Antwort posten. Wenn Sie andere Lösungen haben, können Sie auch eine Antwort schreiben.
ÜBERARBEITETE KLASSE UND LÖSUNG:
Was ich hier versucht habe, war, Post-Injection zu verwenden, anstatt die Hauptabfrage komplett zu ändern und bei all den oben genannten Problemen festzuhalten, einschließlich (a) direktes Ändern von Globals, (b) Auflaufen auf das Global-Value-Problem und (c) Seitenvorlagen neu zuweisen.
Durch die Verwendung von Nacheinspritzung, ich bin in der Lage vollständige Beitrag Integrität zu halten, so
$wp_the_query->post
,$wp_query->post
,$posts
und$post
Aufenthalt konstant über die Vorlage. Jede dieser Variablen verweist auf das aktuelle Seitenobjekt (wie dies bei echten Seiten der Fall ist). Auf diese Weise wissen Funktionen wie Breadcrumbs, dass die aktuelle Seite eine echte Seite und keine Art Archiv ist.Ich musste die Hauptabfrage leicht ändern ( durch Filter und Aktionen ), um sie an die Paginierung anzupassen, aber wir werden darauf zurückkommen.
ABFRAGE NACH EINSPRITZUNG
Um die Nachinjektion durchzuführen, habe ich eine benutzerdefinierte Abfrage verwendet, um die für die Injektion erforderlichen Beiträge zurückzugeben. Ich habe auch die
$found_pages
Eigenschaft der benutzerdefinierten Abfrage verwendet , um die der Hauptabfrage anzupassen, damit die Paginierung der Hauptabfrage funktioniert. Beiträge werden durch dieloop_end
Aktion in die Hauptabfrage eingefügt .Um die benutzerdefinierte Abfrage außerhalb der Klasse zugänglich und verwendbar zu machen, habe ich einige Aktionen eingeführt.
Paginierungshaken, um Paginierungsfunktionen zu haken:
pregetgostsforgages_before_loop_pagination
pregetgostsforgages_after_loop_pagination
Benutzerdefinierter Zähler, der die Beiträge in der Schleife zählt. Diese Aktionen können verwendet werden, um zu ändern, wie Beiträge in der Schleife entsprechend der Beitragsnummer angezeigt werden.
pregetgostsforgages_counter_before_template_part
pregetgostsforgages_counter_after_template_part
Allgemeiner Hook für den Zugriff auf das Abfrageobjekt und das aktuelle Beitragsobjekt
pregetgostsforgages_current_post_and_object
Mit diesen Hooks können Sie ganz einfach arbeiten, da Sie nichts an der Seitenvorlage selbst ändern müssen, was von Anfang an meine Absicht war. Eine Seite kann vollständig von einem Plugin oder einer Funktionsdatei geändert werden, wodurch diese Lösung sehr dynamisch wird.
Ich habe auch verwendet
get_template_part()
, um ein Template-Teil zu laden, mit dem die Beiträge angezeigt werden. Die meisten Themen verwenden heutzutage Schablonenteile, was dies in der Klasse sehr nützlich macht. Wenn Ihr Thema verwendetcontent.php
, können Sie einfach passierencontent
zu$templatePart
zu ladencontent.php
.Wenn Sie für Schablonenteile Post - Format - Unterstützung benötigen, ist es einfach - Sie einfach passieren
content
zu$templatePart
und Satz$postFormatSupport
zutrue
. Infolgedessen wird der Vorlagenteilcontent-video.php
für einen Beitrag mit dem Beitragsformat "" geladenvideo
.Die Hauptfrage
Die folgenden Änderungen wurden an der Hauptabfrage durch die entsprechenden Filter und Aktionen vorgenommen:
Um die Hauptabfrage zu paginieren:
Der
$found_posts
Eigenschaftswert der Injektorabfrage wird über denfound_posts
Filter an den des Hauptabfrageobjekts übergeben .Der Wert des vom Benutzer übergebenen Parameters
posts_per_page
wird auf die Hauptabfrage über festgelegtpre_get_posts
.$max_num_pages
berechnet sich aus der Anzahl der Beiträge in$found_posts
undposts_per_page
. Dais_singular
dies auf Seiten zutrifft, wird dasLIMIT
Setzen der Klausel verhindert. Das einfache Setzenis_singular
von false verursachte einige Probleme, und ich entschied mich, dieLIMIT
Klausel über denpost_limits
Filter festzulegen. Ich habe dieoffset
ofLIMIT
-Klausel beibehalten0
, um 404-Werte auf Seiten mit aktivierter Paginierung zu vermeiden.Dies kümmert sich um die Paginierung und alle Probleme, die durch die Nachinjektion entstehen können.
DAS SEITENOBJEKT
Das aktuelle Seitenobjekt kann unter Verwendung der Standardschleife auf der Seite, getrennt und über den eingefügten Posts, als Beitrag angezeigt werden. Wenn Sie dies nicht benötigen, können Sie einfach
$removePageFromLoop
auf true setzen. Dadurch wird der Seiteninhalt nicht angezeigt.In diesem Stadium verwende ich CSS, um das Seitenobjekt durch die Aktionen
loop_start
und auszublenden,loop_end
da ich keine andere Möglichkeit finde, dies zu tun. Der Nachteil bei dieser Methode ist, dass alles, was mit demthe_post
Aktions-Hook in der Hauptabfrage verknüpft ist, ebenfalls ausgeblendet wird.DIE KLASSE
Die
PreGetPostsForPages
Klasse kann verbessert werden und sollte auch einen korrekten Namensraum haben. Während Sie dies einfach in die Funktionsdatei Ihres Themas einfügen können, ist es besser, dies in ein benutzerdefiniertes Plugin zu kopieren.Verwenden, ändern und missbrauchen Sie, wie Sie es für richtig halten. Der Code ist gut kommentiert, daher sollte er leicht zu befolgen und anzupassen sein
VERWENDUNG
Sie können jetzt die Klasse ( auch in Ihrem Plugin oder in Ihrer Funktionsdatei ) wie folgt initiieren , um die Seite mit der ID 251 als Ziel festzulegen. Daraufhin werden 2 Beiträge pro Seite vom
post
Beitragstyp angezeigt.PAGINIERUNG UND CUSTOM-STYLING HINZUFÜGEN
Wie bereits erwähnt, gibt es in der Injektorabfrage einige Aktionen, um Paginierung und / oder benutzerdefiniertes Styling hinzuzufügen.
Im folgenden Beispiel habe ich die Paginierung nach der Schleife mithilfe meiner eigenen Paginierungsfunktion aus der verknüpften Antwort hinzugefügt . Außerdem habe ich mithilfe meines benutzerdefinierten Zählers ein
<div>
to hinzugefügt , um meine Posts in zwei Spalten anzuzeigen.Hier sind die Aktionen, die ich verwendet habe
Beachten Sie, dass die Paginierung von der Hauptabfrage und nicht von der Injektorabfrage festgelegt wird, sodass auch integrierte Funktionen wie funktionieren
the_posts_pagination()
sollten.Dies ist das Endergebnis
STATISCHE VORDERE SEITEN
Alles funktioniert wie erwartet auf statischen Titelseiten zusammen mit meiner Paginierungsfunktion, ohne dass weitere Änderungen erforderlich sind.
FAZIT
Dies mag wie ein großer Aufwand erscheinen, und es mag sein, aber die Profis überwiegen die große Zeit des Betrügers.
GROSSE PRO'S
Sie müssen die Seitenvorlage für die jeweilige Seite in keiner Weise ändern. Dies macht alles dynamisch und kann leicht zwischen Themen übertragen werden, ohne dass Änderungen am Code vorgenommen werden müssen, solange alles in einem Plugin erledigt wird.
Sie müssen höchstens einen
content.php
Vorlagenteil in Ihrem Thema erstellen, wenn Ihr Thema noch keinen hat.Jede Paginierung, die für die Hauptabfrage verwendet wird, funktioniert auf der Seite, ohne dass Änderungen oder zusätzliche Informationen aus der Abfrage an function übergeben werden.
Es gibt noch mehr Profis, an die ich jetzt nicht denken kann, aber das sind die wichtigsten.
quelle