Uso de pre_get_posts en páginas verdaderas y portadas estáticas

19

He realizado una investigación bastante extensa sobre cómo usar pre_get_postsen páginas verdaderas y en las portadas estáticas, y parece que no hay un método infalible.

La mejor opción que encontré hasta la fecha fue una publicación realizada por @birgire en Stackoverflow . Lo reescribí en una clase de demostración e hice el código un poco más dinámico

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();

Esto funciona bien y la página como se esperaba al usar mi propia función de paginación .

CUESTIONES:

Debido a la función, pierdo la integridad de la página, que rellena otras funciones que dependen del objeto de la página almacenado $post. $postantes de que el bucle se establezca en la primera publicación del bucle y $postse establezca en la última publicación del bucle después del bucle, que se espera. Lo que necesito es que $postesté configurado para el objeto de página actual, es decir, el objeto consultado.

Además, $wp_the_query->posty $wp_query->postcontiene la primera publicación en el bucle y no el objeto consultado como en una página normal

Utilizo lo siguiente ( fuera de mi clase ) para revisar mis globales antes y después del 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
}

ANTES DEL BUCLE:

Antes del ciclo, el problema se resuelve en parte al establecer $injectPageIntoLoopen verdadero, que inyecta el objeto de página como la primera página del ciclo. Esto es bastante útil si necesita mostrar la información de la página antes de las publicaciones solicitadas, pero si no quiere eso, está jodido.

Puedo resolver el problema antes del bucle pirateando directamente los globales, lo que realmente no me gusta. Engancho el siguiente método wpdentro de mi preGetPostsmétodo

public function wp()
{
    $page                          = get_post( $this->pageID );
    $GLOBALS['wp_the_query']->post = $page;
    $GLOBALS['wp_query']           = $GLOBALS['wp_the_query'];
    $GLOBALS['post']               = $page;
}

y dentro del preGetPostsmétodo

add_action( 'wp', [$this, 'wp'] );

A partir de esto, $wp_the_query->post, $wp_query->posty $posttodas las bodegas objeto de página.

DESPUÉS DEL BUCLE

Aquí es donde está mi gran problema, después del ciclo. Después de hackear los globos a través del wpgancho y el método,

  • $wp_the_query->posty $wp_query->postvuelve a la primera publicación del bucle, como se esperaba

  • $post se establece en la última publicación del bucle.

Lo que necesito es que los tres se vuelvan a establecer en el objeto consultado / objeto de página actual.

He intentado conectar el wpmétodo a la loop_endacción, que no funciona. Enganchar el wpmétodo a la get_sidebaracción funciona, pero es demasiado tarde.

add_action( 'get_sidebar', [$this, 'wp'] );

Correr printGlobals()directamente después del bucle en lo confirma la plantilla que a medida $wp_the_query->posty $wp_query->postestán siendo ajustado en el primer puesto y $postel último mensaje.

Puedo agregar manualmente el código dentro del wpmétodo después del ciclo dentro de la plantilla, pero la idea no es alterar los archivos de la plantilla directamente ya que la clase debería ser transferible en un complemento entre los temas.

¿Hay alguna forma adecuada de resolver este problema por el que una carrera pre_get_postsen una página verdadera y primera página estática y aún así mantener la integridad de $wp_the_query->post, $wp_query->posty $post( tener los fijados al objeto consultada ) antes y después del bucle.

EDITAR

Parece haber confusión sobre lo que necesito y por qué lo necesito

Lo que necesito

Necesito retener los valores de $wp_the_query->post, $wp_query->posty a $posttravés de la plantilla independientemente, y ese valor debe ser el objeto consultado. En esta etapa, con el código que he publicado, los valores de esas tres variables no contienen el objeto de página, sino que publican objetos de publicaciones en el bucle. Espero que sea lo suficientemente claro.

He publicado un código que puedes usar para probar estas variables

Porque lo necesito

Necesito una forma confiable de agregar publicaciones a través pre_get_postsde plantillas de página y portadas estáticas sin cambiar la funcionalidad de la página completa. En esta etapa, tal como está el código en cuestión, rompe mi función de ruta de navegación y la función de página relacionada después del bucle debido al $postcual contiene el objeto de publicación "incorrecto".

Sobre todo, no quiero alterar las plantillas de página directamente. Quiero poder agregar publicaciones a una plantilla de página sin NINGUNA modificación a la plantilla

Pieter Goosen
fuente
¿Qué estás tratando de hacer, tus objetivos o requisitos funcionales? No lo declaras en ningún lado por lo que puedo decir.
adelval

Respuestas:

13

Finalmente lo hice funcionar, pero no con el código en mi pregunta. Eliminé totalmente esa idea y comencé a ir en una nueva dirección.

NOTA:

Si alguien puede resolver los problemas de mi pregunta, no dude en publicar una respuesta. Además, si tiene otras soluciones, no dude en publicar una respuesta.

CLASE Y SOLUCIÓN REFABRICADAS:

Lo que traté de hacer aquí fue usar la inyección posterior, en lugar de alterar por completo la consulta principal y atascarme con todos los problemas anteriores, incluidos (a) alterar directamente los globales, (b) encontrarse con el problema del valor global y (c) reasignación de plantillas de página.

Mediante el uso de la inyección, que soy capaz de mantener la integridad post completo, por lo que $wp_the_query->post, $wp_query->post, $postsy la $postestancia constante durante toda la plantilla. Cada una de estas variables hace referencia al objeto de la página actual (como es el caso de las páginas verdaderas). De esta manera, las funciones como migas de pan saben que la página actual es una página verdadera y no una especie de archivo.

Sin embargo, tuve que modificar ligeramente la consulta principal (a través de filtros y acciones ) para ajustar la paginación, pero llegaremos a eso.

CONSULTA POSTERIOR A LA INYECCIÓN

Para lograr la inyección posterior, utilicé una consulta personalizada para devolver las publicaciones necesarias para la inyección. También utilicé la $found_pagespropiedad de la consulta personalizada para ajustar la de la consulta principal para que la paginación funcione desde la consulta principal. Las publicaciones se inyectan en la consulta principal a través de la loop_endacción.

Para hacer que la consulta personalizada sea accesible y utilizable fuera de la clase, introduje un par de acciones.

  • Ganchos de paginación para enganchar las funciones de paginación:

    • pregetgostsforgages_before_loop_pagination

    • pregetgostsforgages_after_loop_pagination

  • Contador personalizado que cuenta las publicaciones en el bucle. Estas acciones se pueden usar para alterar cómo se muestran las publicaciones dentro del bucle según el número de publicación.

    • pregetgostsforgages_counter_before_template_part

    • pregetgostsforgages_counter_after_template_part

  • Enlace general para acceder al objeto de consulta y al objeto de publicación actual

    • pregetgostsforgages_current_post_and_object

Estos ganchos le brindan una experiencia total, ya que no necesita cambiar nada en la plantilla de la página, que era mi intención original desde el principio. Una página puede modificarse completamente desde un complemento o un archivo de función, lo que hace que esta solución sea muy dinámica.

También lo he usado get_template_part()para cargar una parte de la plantilla, que se usará para mostrar las publicaciones. La mayoría de los temas actuales utilizan partes de plantillas, lo que hace que esto sea muy útil en la clase. Si sus usos temáticos content.php, sólo tiene que pasar contenta $templatePartla carga content.php.

Si necesita ayuda del formato de mensaje para las piezas de la plantilla, es fácil - simplemente puede pasar contenta $templateParty conjunto $postFormatSupporta true. Como resultado, la parte de la plantilla content-video.phpse cargará para una publicación con un formato de publicación de video.

LA CONSULTA PRINCIPAL

Los siguientes cambios se realizaron en la consulta principal a través de los respectivos filtros y acciones:

  • Para paginar la consulta principal:

    • El $found_postsvalor de propiedad de la consulta del inyector se pasa al del objeto de consulta principal a través del found_postsfiltro.

    • El valor del parámetro pasado por el usuario posts_per_pagese establece a través de la consulta principal pre_get_posts.

    • $max_num_pagesse calcula utilizando la cantidad de publicaciones en $found_posts y posts_per_page. Como is_singulares cierto en las páginas, inhibe la LIMITcláusula que se establece. Simplemente establecer is_singularen falso causó algunos problemas, así que decidí establecer la LIMITcláusula a través del post_limitsfiltro. Me quedé con la offsetdel LIMITconjunto cláusula para 0evitar 404 de las páginas con paginación activado.

Esto se encarga de la paginación y de cualquier problema que pueda surgir de la inyección posterior.

EL OBJETO DE PÁGINA

El objeto de la página actual está disponible para mostrarse como una publicación utilizando el bucle predeterminado en la página, separado y encima de las publicaciones inyectadas. Si no necesita esto, simplemente puede establecerlo $removePageFromLoopen verdadero, y esto ocultará el contenido de la página para que no se muestre.

En esta etapa, estoy usando CSS para ocultar el objeto de la página a través de las acciones loop_starty loop_endya que no puedo encontrar otra forma de hacerlo. La desventaja de este método es que todo lo que esté conectado al the_postgancho de acción dentro de la consulta principal también estará oculto.

LA CLASE

La PreGetPostsForPagesclase se puede mejorar y también debe tener un espacio de nombres adecuado. Si bien simplemente puede colocar esto en el archivo de funciones de su tema, sería mejor colocarlo en un complemento personalizado.

Usa, modifica y abusa como mejor te parezca. El código está bien comentado, por lo que debería ser fácil de seguir y ajustar

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

Ahora puede iniciar la clase ( también en su plugin o archivo de funciones ) de la siguiente manera para orientar la página con ID 251, sobre la cual mostraremos 2 publicaciones por página del posttipo de publicación.

$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(); 

AÑADIR PAGINACIÓN Y ESTILO PERSONALIZADO

Como mencioné anteriormente, hay algunas acciones en la consulta del inyector para agregar paginación y / o estilo personalizado.

En el siguiente ejemplo, agregué paginación después del ciclo usando mi propia función de paginación de la respuesta vinculada . Además, usando mi contador personalizado, agregué un <div>para mostrar mis publicaciones en dos columnas.

Aquí están las acciones que usé

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();    
});

Tenga en cuenta que la consulta principal establece la paginación, no la consulta del inyector, por lo que las funciones integradas como the_posts_pagination()también deberían funcionar.

este es el resultado final

ingrese la descripción de la imagen aquí

PÁGINAS FRONTALES ESTÁTICAS

Todo funciona como se esperaba en las portadas estáticas junto con mi función de paginación sin requerir ninguna modificación adicional.

CONCLUSIÓN

Esto puede parecer un montón de gastos generales, y puede ser, pero los profesionales superan el gran momento de la estafa.

GRANDES PRO

  • No necesita modificar la plantilla de página para la página específica de ninguna manera. Esto hace que todo sea dinámico y se puede transferir fácilmente entre temas sin realizar modificaciones en el código, siempre y cuando todo se realice en un complemento.

  • A lo sumo, solo necesita crear una content.phpparte de plantilla en su tema si su tema aún no tiene uno.

  • Cualquier paginación que funcione en la consulta principal funcionará en la página sin ningún tipo de alteración ni nada extra de la consulta que se pasa a funcionar.

Hay más profesionales en los que no puedo pensar ahora, pero estos son los importantes.

Pieter Goosen
fuente
3
i.imgur.com/LTfLK.gif
TheDeadMedic