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. $postprima che il loop sia impostato sul primo post nel loop e $postsia impostato sull'ultimo post nel loop dopo il loop, che è previsto. Ciò di cui ho bisogno è che $postsia impostato sull'oggetto pagina corrente, ovvero l'oggetto interrogato.
Inoltre, $wp_the_query->poste$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 $injectPageIntoLoopsu 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 wpall'interno del mio preGetPostsmetodo
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 preGetPostsmetodo interno
add_action( 'wp', [$this, 'wp'] );
Da questo, $wp_the_query->post, $wp_query->poste $posttutte le stive l'oggetto pagina.
DOPO IL LOOP
Questo è il mio grande problema, dopo il ciclo. Dopo aver violato i globi attraverso il wpgancio e il metodo,
$wp_the_query->poste$wp_query->postviene 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 wpmetodo loop_endall'azione, che non funziona. Collegare il wpmetodo get_sidebarall'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->poste $wp_query->postsono ancora impostati sul primo post e $postsull'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_postssu un vero e proprio della pagina e pagina statica e ancora mantengono l'integrità di $wp_the_query->post, $wp_query->poste $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->poste $postattraverso 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_postsai 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 $postquale 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
