Utilizzo di pre_get_posts su pagine vere e prime pagine statiche


19

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


Cosa stai cercando di fare, i tuoi obiettivi o requisiti funzionali? Non lo dici da nessuna parte, per quanto posso dire.
Adelval,

Risposte:


13

Finalmente ho funzionato, ma non con il codice nella mia domanda. Ho completamente eliminato l'intera idea e ho ricominciato andando in una nuova direzione.

NOTA:

Se qualcuno è mai in grado di risolvere i problemi della mia domanda, sentiti libero di inviare una risposta. Inoltre, se hai altre soluzioni, sentiti libero di inviare una risposta.

CLASSE E SOLUZIONE REWORKED:

Quello che ho cercato di fare qui è stato quello di utilizzare post-iniezione, piuttosto che alterare completamente la query principale e rimanere bloccato con tutti i problemi di cui sopra, tra cui (a) alterare direttamente i globi, (b) imbattersi nel problema del valore globale e (c) riassegnazione dei modelli di pagina.

Utilizzando post iniezione, sono in grado di mantenere l'integrità intero post, quindi $wp_the_query->post, $wp_query->post, $postse $postcostante permanenza per tutto il modello. Ognuna di queste variabili fa riferimento all'oggetto pagina corrente (come nel caso delle pagine vere). In questo modo, funzioni come il pangrattato sanno che la pagina corrente è una pagina vera e non una sorta di archivio.

Ho dovuto modificare leggermente la query principale ( tramite filtri e azioni ) per adattarmi alla paginazione, ma ci arriveremo.

RICHIESTA DI INIEZIONE POST

Al fine di realizzare post iniezione, ho usato una query personalizzata per restituire i post necessari per l'iniezione. Ho anche usato la $found_pagesproprietà della query personalizzata per adattare quella della query principale per far funzionare l'impaginazione dalla query principale. I post vengono iniettati nella query principale tramite l' loop_endazione.

Al fine di rendere la query personalizzata accessibile e utilizzabile al di fuori della classe, ho introdotto un paio di azioni.

  • Ganci di impaginazione per agganciare le funzioni di impaginazione:

    • pregetgostsforgages_before_loop_pagination

    • pregetgostsforgages_after_loop_pagination

  • Contatore personalizzato che conta i post nel ciclo. Queste azioni possono essere utilizzate per modificare la modalità di visualizzazione dei post all'interno del loop in base al numero di post.

    • pregetgostsforgages_counter_before_template_part

    • pregetgostsforgages_counter_after_template_part

  • Hook generale per accedere all'oggetto query e all'oggetto post corrente

    • pregetgostsforgages_current_post_and_object

Questi hook ti offrono un'esperienza totale, in quanto non è necessario modificare nulla nel modello di pagina stesso, che era la mia intenzione originale dall'inizio. Una pagina può essere completamente modificata da un plugin o da un file di funzioni, il che rende questa soluzione molto dinamica.

Ho anche usato get_template_part()per caricare una parte del modello, che verrà utilizzata per visualizzare i post. La maggior parte dei temi oggi utilizza parti del modello, il che lo rende molto utile in classe. Se il tuo tema utilizza content.php, puoi semplicemente passare contenta $templatePartper caricare content.php.

Se avete bisogno di assistenza post formato per le parti di modello, è facile - si può semplicemente passare contentper $templateParte insieme $postFormatSupporta true. Di conseguenza, la parte del modello content-video.phpverrà caricata per un post con un formato post di video.

LA DOMANDA PRINCIPALE

Le seguenti modifiche sono state apportate alla query principale tramite i rispettivi filtri e azioni:

  • Per impaginare la query principale:

    • Il $found_postsvalore della proprietà della query dell'iniettore viene passato a quello dell'oggetto query principale tramite il found_postsfiltro.

    • Il valore del parametro passato dall'utente posts_per_pageè impostato sulla query principale pre_get_posts.

    • $max_num_pagesviene calcolato utilizzando la quantità di post in $found_posts e posts_per_page. Poiché is_singularè vero nelle pagine, inibisce l' LIMITimpostazione della clausola. La semplice impostazione is_singularsu false ha causato alcuni problemi, quindi ho deciso di impostare la LIMITclausola tramite il post_limitsfiltro. Ho mantenuto la clausola offsetof of LIMITset impostata per 0evitare i 404 nelle pagine con l'impaginazione attivata.

Questo si occupa della paginazione e di qualsiasi problema che potrebbe derivare dalla post iniezione.

L'OGGETTO PAGINA

L'oggetto pagina corrente è disponibile per la visualizzazione come post utilizzando il ciclo predefinito nella pagina, separato e in cima ai post iniettati. Se non ti serve, puoi semplicemente impostare $removePageFromLoopsu true e ciò nasconderà la visualizzazione del contenuto della pagina.

In questa fase, sto usando i CSS per nascondere l'oggetto pagina attraverso le azioni loop_starte loop_endpoiché non riesco a trovare un altro modo per farlo. Il rovescio della medaglia con questo metodo è che qualsiasi cosa agganciata al the_postgancio di azione all'interno della query principale verrà nascosta.

LA CLASSE

La PreGetPostsForPagesclasse può essere migliorata e dovrebbe anche essere adeguatamente spaziata. Mentre puoi semplicemente rilasciarlo nel file delle funzioni del tuo tema, sarebbe meglio rilasciarlo in un plug-in personalizzato.

Usa, modifica e abusa a tuo piacimento. Il codice è ben commentato, quindi dovrebbe essere facile da seguire e regolare

class PreGetPostsForPages
{
    /**
     * @var string|int $pageID
     * @access protected     
     * @since 1.0.0
     */
    protected $pageID;

    /**
     * @var string $templatePart
     * @access protected     
     * @since 1.0.0
     */
    protected $templatePart;

    /**
     * @var bool $postFormatSupport
     * @access protected     
     * @since 1.0.0
     */
    protected $postFormatSupport;

    /**
     * @var bool $removePageFromLoop
     * @access protected     
     * @since 1.0.0
     */
    protected $removePageFromLoop;

    /**
     * @var array $args
     * @access protected     
     * @since 1.0.0
     */
    protected $args;

    /**
     * @var array $mergedArgs
     * @access protected     
     * @since 1.0.0
     */
    protected $mergedArgs = [];

    /**
     * @var NULL|\stdClass $injectorQuery
     * @access protected     
     * @since 1.0.0
     */
    protected $injectorQuery = NULL;

    /**
     * @var int $validatedPageID
     * @access protected     
     * @since 1.0.0
     */
    protected $validatedPageID = 0;

    /** 
     * Constructor method
     *
     * @param string|int $pageID The ID of the page we would like to target
     * @param string $templatePart The template part which should be used to display posts
     * @param string $postFormatSupport Should get_template_part support post format specific template parts
     * @param bool $removePageFromLoop Should the page content be displayed or not
     * @param array $args An array of valid arguments compatible with WP_Query
     *
     * @since 1.0.0
     */      
    public function __construct( 
        $pageID             = NULL,
        $templatePart       = NULL,
        $postFormatSupport  = false,
        $removePageFromLoop = false,
        $args               = [] 
    ) {
        $this->pageID             = $pageID;
        $this->templatePart       = $templatePart;
        $this->postFormatSupport  = $postFormatSupport;
        $this->removePageFromLoop = $removePageFromLoop;
        $this->args               = $args;
    }

    /**
     * Public method init()
     *
     * The init method will be use to initialize our pre_get_posts action
     *
     * @since 1.0.0
     */
    public function init()
    {
        // Initialise our pre_get_posts action
        add_action( 'pre_get_posts', [$this, 'preGetPosts'] );
    }

    /**
     * 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;
    }

    /**
     * Private method mergedArgs()
     *
     * Merge the default args with the user passed args
     *
     * @since 1.0.0
     */
    private function mergedArgs()
    {
        // Set default arguments
        if ( get_query_var( 'paged' ) ) {
            $currentPage = get_query_var( 'paged' );
        } elseif ( get_query_var( 'page' ) ) {
            $currentPage = get_query_var( 'page' );
        } else {
            $currentPage = 1;
        }
        $default = [
            'suppress_filters'    => true,
            'ignore_sticky_posts' => 1,
            'paged'               => $currentPage,
            'posts_per_page'      => get_option( 'posts_per_page' ), // Set posts per page here to set the LIMIT clause etc
            'nopaging'            => false
        ];    
        $mergedArgs = wp_parse_args( (array) $this->args, $default );
        $this->mergedArgs = $mergedArgs;
    }

    /**
     * Public method preGetPosts()
     *
     * This is the callback method which will be hooked to the 
     * pre_get_posts action hook. This method will be used to alter
     * the main query on the page specified by ID.
     *
     * @param \stdClass WP_Query The query object passed by reference
     * @since 1.0.0
     */
    public function preGetPosts( \WP_Query $q )
    {
        if (    !is_admin() // Only target the front end
             && $q->is_main_query() // Only target the main query
             && $q->is_page( filter_var( $this->validatedPageID, FILTER_VALIDATE_INT ) ) // Only target our specified page
        ) {
            // Remove the pre_get_posts action to avoid unexpected issues
            remove_action( current_action(), [$this, __METHOD__] );

            // METHODS:
            // Initialize our method which will return the validated page ID
            $this->validatePageID();
            // Initiale our mergedArgs() method
            $this->mergedArgs();
            // Initiale our custom query method
            $this->injectorQuery();

            /**
             * We need to alter a couple of things here in order for this to work
             * - Set posts_per_page to the user set value in order for the query to
             *   to properly calculate the $max_num_pages property for pagination
             * - Set the $found_posts property of the main query to the $found_posts
             *   property of our custom query we will be using to inject posts
             * - Set the LIMIT clause to the SQL query. By default, on pages, `is_singular` 
             *   returns true on pages which removes the LIMIT clause from the SQL query.
             *   We need the LIMIT clause because an empty limit clause inhibits the calculation
             *   of the $max_num_pages property which we need for pagination
             */
            if (    $this->mergedArgs['posts_per_page'] 
                 && true !== $this->mergedArgs['nopaging']
            ) {
                $q->set( 'posts_per_page', $this->mergedArgs['posts_per_page'] );
            } elseif ( true === $this->mergedArgs['nopaging'] ) {
                $q->set( 'posts_per_page', -1 );
            }

            // FILTERS:
            add_filter( 'found_posts', [$this, 'foundPosts'], PHP_INT_MAX, 2 );
            add_filter( 'post_limits', [$this, 'postLimits']);

            // ACTIONS:
            /**
             * We can now add all our actions that we will be using to inject our custom
             * posts into the main query. We will not be altering the main query or the 
             * main query's $posts property as we would like to keep full integrity of the 
             * $post, $posts globals as well as $wp_query->post. For this reason we will use
             * post injection
             */     
            add_action( 'loop_start', [$this, 'loopStart'], 1 );
            add_action( 'loop_end',   [$this, 'loopEnd'],   1 );
        }    
    }    

    /**
     * Public method injectorQuery
     *
     * This will be the method which will handle our custom
     * query which will be used to 
     * - return the posts that should be injected into the main
     *   query according to the arguments passed
     * - alter the $found_posts property of the main query to make
     *   pagination work 
     *
     * @link https://codex.wordpress.org/Class_Reference/WP_Query
     * @since 1.0.0
     * @return \stdClass $this->injectorQuery
     */
    public function injectorQuery()
    {
        //Define our custom query
        $injectorQuery = new \WP_Query( $this->mergedArgs );

        // Update the thumbnail cache
        update_post_thumbnail_cache( $injectorQuery );

        $this->injectorQuery = $injectorQuery;

        return $this->injectorQuery;
    }

    /**
     * Public callback method foundPosts()
     * 
     * We need to set found_posts in the main query to the $found_posts
     * property of the custom query in order for the main query to correctly 
     * calculate $max_num_pages for pagination
     *
     * @param string $found_posts Passed by reference by the filter
     * @param stdClass \WP_Query Sq The current query object passed by refence
     * @since 1.0.0
     * @return $found_posts
     */
    public function foundPosts( $found_posts, \WP_Query $q )
    {
        if ( !$q->is_main_query() )
            return $found_posts;

        remove_filter( current_filter(), [$this, __METHOD__] );

        // Make sure that $this->injectorQuery actually have a value and is not NULL
        if (    $this->injectorQuery instanceof \WP_Query 
             && 0 != $this->injectorQuery->found_posts
        )
            return $found_posts = $this->injectorQuery->found_posts;

        return $found_posts;
    }

    /**
     * Public callback method postLimits()
     *
     * We need to set the LIMIT clause as it it is removed on pages due to 
     * is_singular returning true. Witout the limit clause, $max_num_pages stays
     * set 0 which avoids pagination. 
     *
     * We will also leave the offset part of the LIMIT cluase to 0 to avoid paged
     * pages returning 404's
     *
     * @param string $limits Passed by reference in the filter
     * @since 1.0.0
     * @return $limits
     */
    public function postLimits( $limits )
    {
        $posts_per_page = (int) $this->mergedArgs['posts_per_page'];
        if (    $posts_per_page
             && -1   !=  $posts_per_page // Make sure that posts_per_page is not set to return all posts
             && true !== $this->mergedArgs['nopaging'] // Make sure that nopaging is not set to true
        ) {
            $limits = "LIMIT 0, $posts_per_page"; // Leave offset at 0 to avoid 404 on paged pages
        }

        return $limits;
    }

    /**
     * Public callback method loopStart()
     *
     * Callback function which will be hooked to the loop_start action hook
     *
     * @param \stdClass \WP_Query $q Query object passed by reference
     * @since 1.0.0
     */
    public function loopStart( \WP_Query $q )
    {
        /**
         * Although we run this action inside our preGetPosts methods and
         * and inside a main query check, we need to redo the check here aswell
         * because failing to do so sets our div in the custom query output as well
         */

        if ( !$q->is_main_query() )
            return;

        /** 
         * Add inline style to hide the page content from the loop
         * whenever $removePageFromLoop is set to true. You can
         * alternatively alter the page template in a child theme by removing
         * everything inside the loop, but keeping the loop
         * Example of how your loop should look like:
         *     while ( have_posts() ) {
         *     the_post();
         *         // Add nothing here
         *     }
         */
        if ( true === $this->removePageFromLoop )
            echo '<div style="display:none">';
    }   

    /**
     * Public callback method loopEnd()
     *
     * Callback function which will be hooked to the loop_end action hook
     *
     * @param \stdClass \WP_Query $q Query object passed by reference
     * @since 1.0.0
     */
    public function loopEnd( \WP_Query $q )
    {  
        /**
         * Although we run this action inside our preGetPosts methods and
         * and inside a main query check, we need to redo the check here as well
         * because failing to do so sets our custom query into an infinite loop
         */
        if ( !$q->is_main_query() )
            return;

        // See the note in the loopStart method  
        if ( true === $this->removePageFromLoop )
            echo '</div>';

        //Make sure that $this->injectorQuery actually have a value and is not NULL
        if ( !$this->injectorQuery instanceof \WP_Query )
            return; 

        // Setup a counter as wee need to run the custom query only once    
        static $count = 0;    

        /**
         * Only run the custom query on the first run of the loop. Any consecutive
         * runs (like if the user runs the loop again), the custom posts won't show.
         */
        if ( 0 === (int) $count ) {      
            // We will now add our custom posts on loop_end
            $this->injectorQuery->rewind_posts();

            // Create our loop
            if ( $this->injectorQuery->have_posts() ) {

                /**
                 * Fires before the loop to add pagination.
                 *
                 * @since 1.0.0
                 *
                 * @param \stdClass $this->injectorQuery Current object (passed by reference).
                 */
                do_action( 'pregetgostsforgages_before_loop_pagination', $this->injectorQuery );


                // Add a static counter for those who need it
                static $counter = 0;

                while ( $this->injectorQuery->have_posts() ) {
                    $this->injectorQuery->the_post(); 

                    /**
                     * Fires before get_template_part.
                     *
                     * @since 1.0.0
                     *
                     * @param int $counter (passed by reference).
                     */
                    do_action( 'pregetgostsforgages_counter_before_template_part', $counter );

                    /**
                     * Fires before get_template_part.
                     *
                     * @since 1.0.0
                     *
                     * @param \stdClass $this->injectorQuery-post Current post object (passed by reference).
                     * @param \stdClass $this->injectorQuery Current object (passed by reference).
                     */
                    do_action( 'pregetgostsforgages_current_post_and_object', $this->injectorQuery->post, $this->injectorQuery );

                    /** 
                     * Load our custom template part as set by the user
                     * 
                     * We will also add template support for post formats. If $this->postFormatSupport
                     * is set to true, get_post_format() will be automatically added in get_template part
                     *
                     * If you have a template called content-video.php, you only need to pass 'content'
                     * to $template part and then set $this->postFormatSupport to true in order to load
                     * content-video.php for video post format posts
                     */
                    $part = '';
                    if ( true === $this->postFormatSupport )
                        $part = get_post_format( $this->injectorQuery->post->ID ); 

                    get_template_part( 
                        filter_var( $this->templatePart, FILTER_SANITIZE_STRING ), 
                        $part
                    );

                    /**
                     * Fires after get_template_part.
                     *
                     * @since 1.0.0
                     *
                     * @param int $counter (passed by reference).
                     */
                    do_action( 'pregetgostsforgages_counter_after_template_part', $counter );

                    $counter++; //Update the counter
                }

                wp_reset_postdata();

                /**
                 * Fires after the loop to add pagination.
                 *
                 * @since 1.0.0
                 *
                 * @param \stdClass $this->injectorQuery Current object (passed by reference).
                 */
                do_action( 'pregetgostsforgages_after_loop_pagination', $this->injectorQuery );
            }
        }

        // Update our static counter
        $count++;       
    }
}  

USO

Ora puoi avviare la classe ( anche nel tuo plugin o file di funzioni ) come segue per indirizzare la pagina con ID 251, su cui mostreremo 2 post per pagina dal posttipo di post.

$query = new PreGetPostsForPages(
    251,       // Page ID we will target
    'content', //Template part which will be used to display posts, name should be without .php extension 
    true,      // Should get_template_part support post formats
    false,     // Should the page object be excluded from the loop
    [          // Array of valid arguments that will be passed to WP_Query/pre_get_posts
        'post_type'      => 'post', 
        'posts_per_page' => 2
    ] 
);
$query->init(); 

AGGIUNTA DI PAGINAZIONE E STILE PERSONALIZZATO

Come accennato in precedenza, ci sono alcune azioni nella query dell'iniettore per aggiungere l'impaginazione e / o lo stile personalizzato.

Nel seguente esempio, ho aggiunto l'impaginazione dopo il ciclo usando la mia funzione di impaginazione dalla risposta collegata . Inoltre, usando il mio contatore personalizzato, ho aggiunto a<div> a per visualizzare i miei post in due colonne.

Ecco le azioni che ho usato

add_action( 'pregetgostsforgages_counter_before_template_part', function ( $counter )
{
    $class = $counter%2  ? ' right' : ' left';
    echo '<div class="entry-column' . $class . '">';
});

add_action( 'pregetgostsforgages_counter_after_template_part', function ( $counter )
{
    echo '</div>';
});

add_action( 'pregetgostsforgages_after_loop_pagination', function ( \WP_Query $q )
{
    paginated_numbers();    
});

Si noti che l'impaginazione è impostata dalla query principale, non dalla query dell'iniettore, quindi anche funzioni integrate come the_posts_pagination()dovrebbero funzionare.

Questo è il risultato finale

inserisci qui la descrizione dell'immagine

PAGINE ANTERIORI STATICHE

Tutto funziona come previsto sulle prime pagine statiche insieme alla mia funzione di impaginazione senza richiedere ulteriori modifiche.

CONCLUSIONE

Questo potrebbe sembrare un sacco di spese generali, e potrebbe essere, ma il professionista supera i grandi tempi della truffa.

BIG PRO'S

  • Non è necessario modificare in alcun modo il modello di pagina per la pagina specifica. Questo rende tutto dinamico e può essere facilmente trasferito tra i temi senza apportare modifiche al codice, purché tutto sia fatto in un plugin.

  • Al massimo, devi solo creare una content.phpparte modello nel tuo tema se il tuo tema non ne ha ancora uno.

  • Qualsiasi impaginazione che funziona sulla query principale funzionerà sulla pagina senza alcun tipo di alterazione o qualcosa in più dalla query passata alla funzione.

Ci sono più professionisti a cui non riesco a pensare ora, ma questi sono quelli importanti.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.