Ho fatto ricerche abbastanza approfondite su come usare pre_get_posts
pagine vere e le prime pagine statiche, e sembra che non esista un metodo infallibile.
L'opzione migliore che ho trovato fino ad oggi era da un post fatto da @birgire su Stackoverflow . L'ho riscritto in una classe demo e ho reso il codice un po 'più dinamico
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();
Funziona bene e pagina come previsto usando la mia funzione di impaginazione .
PROBLEMI:
A causa della funzione, perdo l'integrità della pagina che contiene altre funzioni basandosi sull'oggetto pagina memorizzato $post
. $post
prima che il loop sia impostato sul primo post nel loop e $post
sia impostato sull'ultimo post nel loop dopo il loop, che è previsto. Ciò di cui ho bisogno è che $post
sia impostato sull'oggetto pagina corrente, ovvero l'oggetto interrogato.
Inoltre, $wp_the_query->post
e$wp_query->post
detiene il primo post nel ciclo e non l'oggetto in questione, come su una normale pagina
Uso quanto segue ( fuori dalla mia classe ) per controllare i miei globi prima e dopo il ciclo
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
}
PRIMA DEL CICLO:
Prima del ciclo, il problema è parzialmente risolto impostando $injectPageIntoLoop
su true che inietta l'oggetto pagina come prima pagina nel ciclo. Questo è abbastanza utile se devi mostrare le informazioni sulla pagina prima dei post richiesti, ma se non lo desideri, sei fregato.
Posso risolvere il problema prima del ciclo hackerando direttamente i globi, cosa che non mi piace molto. Aggancio il seguente metodo wp
all'interno del mio preGetPosts
metodo
public function wp()
{
$page = get_post( $this->pageID );
$GLOBALS['wp_the_query']->post = $page;
$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
$GLOBALS['post'] = $page;
}
e preGetPosts
metodo interno
add_action( 'wp', [$this, 'wp'] );
Da questo, $wp_the_query->post
, $wp_query->post
e $post
tutte le stive l'oggetto pagina.
DOPO IL LOOP
Questo è il mio grande problema, dopo il ciclo. Dopo aver violato i globi attraverso il wp
gancio e il metodo,
$wp_the_query->post
e$wp_query->post
viene riportato al primo post del loop, come previsto$post
è impostato sull'ultimo post nel loop.
Ciò di cui ho bisogno è che tutti e tre siano riportati all'oggetto interrogato / oggetto pagina corrente.
Ho provato ad associare il wp
metodo loop_end
all'azione, che non funziona. Collegare il wp
metodo get_sidebar
all'azione funziona, ma è troppo tardi.
add_action( 'get_sidebar', [$this, 'wp'] );
L'esecuzione printGlobals()
diretta dopo il ciclo nel modello conferma che as $wp_the_query->post
e $wp_query->post
sono ancora impostati sul primo post e $post
sull'ultimo post.
Posso aggiungere manualmente il codice all'interno di wp
metodo dopo il ciclo all'interno del modello, ma l'idea non è quella di modificare direttamente i file del modello poiché la classe dovrebbe essere trasferibile in un plugin tra i temi.
Esiste un modo corretto per risolvere questo problema in cui una corsa pre_get_posts
su un vero e proprio della pagina e pagina statica e ancora mantengono l'integrità di $wp_the_query->post
, $wp_query->post
e $post
( avendo quelli insieme all'oggetto interrogato ) prima e dopo il ciclo.
MODIFICARE
Sembra esserci confusione su ciò di cui ho bisogno e perché ne ho bisogno
Ciò che di cui ho bisogno
Ho bisogno di conservare i valori di $wp_the_query->post
, $wp_query->post
e $post
attraverso il modello indipendentemente, e quel valore dovrebbe essere l'oggetto interrogato. In questa fase, con il codice che ho pubblicato, i valori di queste tre variabili non contengono l'oggetto della pagina, ma piuttosto postano gli oggetti dei post nel ciclo. Spero sia abbastanza chiaro.
Ho pubblicato un codice che puoi usare per testare queste variabili
Perché ne ho bisogno
Ho bisogno di un modo affidabile per aggiungere post pre_get_posts
ai modelli di pagina e alle prime pagine statiche senza modificare la funzionalità della pagina intera. In questa fase, così come il codice in questione, interrompe la mia funzione breadcrumb e la relativa funzione di pagina dopo il ciclo a causa della $post
quale contiene l'oggetto post "sbagliato".
Soprattutto, non voglio modificare direttamente i modelli di pagina. Voglio essere in grado di aggiungere post a un modello di pagina senza NESSUNA modifica al modello