¿Cómo puedo crear una página de inicio de categoría seguida de páginas de publicaciones?

9

Estoy tratando de crear una plantilla de categoría en mi tema que muestre contenido estático si el usuario está en la primera página del archivo, y luego mostrará las publicaciones en la categoría en las siguientes páginas. Básicamente estoy tratando de replicar el comportamiento de la categoría en Wired.com donde http://www.wired.com/category/design es una página de destino mientras / category / design / page / 1 muestra una lista cronológica inversa de publicaciones como usted esperaría en un archivo de categoría.

La clave aquí es que no estoy mostrando ninguna publicación del archivo de categorías en la primera página, por lo que necesito que la siguiente página comience con la primera publicación. Primero intenté hacer esto usando el desplazamiento y la paginación manual estableciendo el desplazamiento de la consulta en 0 si la consulta 'paginada' var era 2. Pero el desplazamiento se ignora cuando se establece en cero, por lo que lo mejor que puedo hacer es establecer el desplazamiento en 1 y comience con la segunda publicación en la categoría.

Esto es lo que agregué a functions.php:

add_action('pre_get_posts', 'category_query_offset', 1 );
function category_query_offset(&$query) {
  // Before anything else, make sure this is the right query
  if (!$query->is_category('news')) {
    return;
  }

  // Next, determine how many posts per page
  $ppp = get_option('posts_per_page');

  //Next, detect and handle pagination
  if ($query->is_paged) {
    // Manually determine page query offset
    $page_offset = (($query->query_vars['paged']-1) * $ppp) - $ppp;
    // Apply adjust page offset
    $query->set('offset', $page_offset );
  }
}

Lo que sería mejor, y parece que lo hace Wired , es usar la paginación predeterminada para que las publicaciones comiencen en la página 1, pero inserte una página 0 para el contenido estático. Hipotéticamente, esto parece posible si se muestra el contenido estático si 'paginado' es 0 y se muestra la primera página de publicaciones si 'paginado' es 1. El problema es que 'paginado' nunca es 1 porque Wordpress establece 'paginado' 'a cero cuando el usuario solicita la primera página. Eso significa que 'paginado' es 0 para / categoría / noticias / página / 1 y / categoría / noticias.

¿Hay alguna manera de verificar si el usuario solicitó / category / news / page / 1 en lugar de / category / news? De lo contrario, ¿hay alguna manera de mostrar todas las publicaciones en la categoría a partir de la página 2?

willcwelch
fuente

Respuestas:

7

Esta es una pregunta bastante interesante ( que he votado, especialmente por su enfoque e investigación ). La gran bola curva aquí es la primera página de la consulta:

  • No puede configurar la consulta para que devuelva 0publicaciones en la primera página

  • Al mover el contenido de cada página una página hacia arriba, perderá la última página, ya que la consulta solo tendrá la misma cantidad de publicaciones, por lo que la $max_num_pagespropiedad seguirá siendo la misma.

Tendremos que "engañar" de alguna manera a la WP_Queryclase para que devuelva nuestras publicaciones correctamente con el desplazamiento de una página, y también obtenga el número correcto de páginas para no perder la última página de la consulta

Veamos la siguiente idea e intentemos poner todo en código. Sin embargo, antes de hacerlo, me gustaría plantear algunas notas aquí.

NOTAS IMPORTANTES:

  • No se ha probado todo, por lo que puede tener errores. Asegúrese de probar esto localmente con la depuración activada

  • El código requiere al menos PHP 5.3, cualquier versión por debajo de 5.3 causará un error fatal. Tenga en cuenta que si todavía usa alguna versión por debajo de PHP 5.5, ya debería haber actualizado hace mucho tiempo

  • Modifique y abuse del código como mejor le parezca para satisfacer sus necesidades exactas

LA IDEA DE LA BOMBILLA:

LO QUE NECESITAREMOS

Para que todo funcione, necesitaremos lo siguiente:

  • El número de página actual que se está viendo

  • La posts_per_pageopción establecida en la configuración de lectura

  • Personalizado offset

  • Modificar la $found_postspropiedad de la consulta para corregir la $max_num_pagespropiedad.

La paginación se WP_Queryreduce a unas pocas líneas de código muy simples

if ( empty($q['nopaging']) && !$this->is_singular ) {
    $page = absint($q['paged']);
    if ( !$page )
        $page = 1;
    // If 'offset' is provided, it takes precedence over 'paged'.
    if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) ) {
        $q['offset'] = absint( $q['offset'] );
        $pgstrt = $q['offset'] . ', ';
    } else {
        $pgstrt = absint( ( $page - 1 ) * $q['posts_per_page'] ) . ', ';
    }
    $limits = 'LIMIT ' . $pgstrt . $q['posts_per_page'];
}

Lo que sucede básicamente, una vez que se establece explícitamente un desplazamiento, pagedse ignora el parámetro. El primer parámetro de la LIMITcláusula SQL se vuelve a calcular a partir del desplazamiento y será el número de publicaciones que se omitirán en la consulta SQL generada.

Según su pregunta, aparentemente cuando se establece offseten 0, la consulta de desplazamiento falla, lo cual es extraño, ya que la siguiente comprobación debería devolver verdadero

if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) )

0es un número válido y debe devolver verdadero. Si esto no ocurre en su instalación, debe depurar el problema

Para volver al tema en cuestión, utilizaremos el mismo tipo de lógica para calcular y establecer nuestro desplazamiento para obtener la publicación 1 en la página 2 y desde allí paginar la consulta. Para la primera página, no modificaremos nada, por lo que las publicaciones que se supone que están en la página 1 seguirán estando en la página de forma normal, solo tendremos que "ocultarlas" más adelante para que no las visualicemos en la página 1

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $current_page = $q->get( 'paged' ); // Get the current page number
        // We will only need to run this from page 2 onwards
        if ( $current_page != 0 ) { // You can also use if ( is_paged() ) {
            // Get the amount of posts per page
            $posts_per_page = get_option( 'posts_per_page' );
            // Recalculate our offset
            $offset = ( ( $current_page - 1) * $posts_per_page ) - $posts_per_page; // This should work on page 2 where it returns 0

            // Set our offset
            $q->set( 'offset', $offset );
        }
    }
});

Debería ver las mismas publicaciones de la página 1 en la página 2. Como dije anteriormente, si esto no sucede, is_numeric( 0 )está devolviendo falso ( que no debería ) o tiene otra pre_get_postsacción que también está tratando de establecer un desplazamiento o usted están utilizando los posts_*filtros de cláusula ( más específicamente, el post_limitsfiltro ). Esto sería algo que necesitaría depurar usted mismo.

El siguiente problema es corregir la paginación, ya que, como dije anteriormente, tendrá una página corta. Para esto, tendremos que agregar el valor de get_option( 'posts_per_page' )a la cantidad de publicaciones encontradas en la consulta, ya que estamos compensando la consulta por esa cantidad. Al hacer esto, estamos agregando efectivamente 1a la $max_num_pagespropiedad.

add_action( 'found_posts', function ( $found_posts, $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $found_posts = $found_posts + get_option( 'posts_per_page');
    }
}, 10, 2 );

Esto debería ordenar todo, excepto la primera página.

TODOS JUNTOS AHORA ( y especialmente para @ialocin - Yellow Submarine )

Todo esto debería entrar en functions.php

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $current_page = $q->get( 'paged' ); // Get the current page number
        // We will only need to run this from page 2 onwards
        if ( $current_page != 0 ) { // You can also use if ( is_paged() ) {
            // Get the amount of posts per page
            $posts_per_page = get_option( 'posts_per_page' );
            // Recalculate our offset
            $offset = ( ( $current_page - 1) * $posts_per_page ) - $posts_per_page; // This should work on page 2 where it returns 0

            // Set our offset
            $q->set( 'offset', $offset );
        }
    }
});

add_filter( 'found_posts', function ( $found_posts, $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $found_posts = $found_posts + get_option( 'posts_per_page');
    }
    return $found_posts;
}, 10, 2 );

OPCIONES DE PRIMERA PÁGINA

Aquí hay algunas opciones:

OPCIÓN 1

Lo más probable es que elija esta opción. Lo que querría hacer aquí es crear un category-news.php( si aún no lo ha hecho ). Esta será la plantilla que se utilizará cada vez que se vea la newscategoría. Esta plantilla será muy sencilla.

Ejemplo

<?php
get_header()

if ( !is_paged() ) { // This is the first page
    get_template_part( 'news', 'special' );
} else { // This is not the first page
    get_template_part( 'news', 'loop' );
}

get_sidebar();
get_footer();

Como puede ver, he incluido dos partes de plantilla, news-special.phpy news-loop.php. Ahora, los conceptos básicos de las dos plantillas personalizadas son:

  • news-special.php-> Esta parte de la plantilla será lo que quieras mostrar en la primera página. Agregue toda su información estática personalizada aquí. Tenga mucho cuidado de no llamar al bucle en esta plantilla ya que esto mostraría las publicaciones de la primera página.

  • news-loop.php-> Esta es la plantilla donde invocaremos el bucle. Esta sección se verá así:

    global $wp_query;
    while ( have_posts() ) {
    the_post();
    
        // Your template tags and markup
    
    }

OPCION 2

Cree una plantilla separada con su contenido estático y simplemente use el category_templatefiltro para usar esta plantilla cuando veamos la primera página de la newscategoría. Además, asegúrese de no invocar el bucle predeterminado en esta plantilla. Además, asegúrese de que su convención de nomenclatura aquí no choque con los nombres de las plantillas dentro de la jerarquía de plantillas

Espero que esto sea útil. Siéntase libre de dejar comentarios con inquietudes

EDITAR

Gracias al OP, hay un error definitivo en la WP_Queryclase, verifique el boleto de tránsito # 34060 . El código que publiqué es de Wordpress v4.4, y el error se corrigió en esta versión.

Regresé al código fuente de v4.3, donde está el error, y puedo confirmar que 0se ignora cuando se establece como valor, offsetya que el código simplemente verifica si el offsetparámetro es empty. 0se toma como vacío en PHP. No estoy seguro de si este comportamiento (error) solo se encuentra en v4.3 solo o en todas las versiones anteriores (de acuerdo con el ticket, este error está en v4.3), pero hay un parche para este error que puede verificar fuera en el boleto de trac. Como dije, este error definitivamente se ha solucionado en v4.4

Pieter Goosen
fuente
2
Gracias por tu respuesta detallada. Esto es más o menos lo que implementé en mi primer intento. El problema era que establecer 'offset' en 0 no funcionaba. Resulta que en realidad era un error en el Core.trac.wordpress.org/ticket/34060 de WordPress Core . Parcharlo hizo que todo funcionara. Lo único que falta en su código es return $found_posts;después de la instrucción if en la acción found_posts. ¡Gracias!
willcwelch
Gracias por sus comentarios positivos. Regresé al código fuente de la versión 4.3 query.php, y sí, 0será ignorado porque la marca aquí está vacía y 0se toma como vacía. El código en mi respuesta es el código de actualización que se envía con v4.4, por lo que puedo confirmar que el error se corrigió en 4.4. Actualizaré mi respuesta en consecuencia. Sobre el found_poststema, buena captura, en realidad es un filtro y debe devolverse. Disfruta
Pieter Goosen
jaja, gracias :) y por todo lo que se preguntan por qué ... uno, dos, tres, cuatro, ¿puedo tener un poco más ... el enlace real a la canción todos juntos ahora
Nicolai
@ialocin jajaja, recordaré este ;-)
Pieter Goosen el
1
es bastante pegadizo :) pero no es de extrañar, porque está escrito como una canción para niños
Nicolai
0

Actualizar:

add_action('pre_get_posts', 'category_query_offset', 1 );
function category_query_offset($query) {
 // Before anything else, make sure this is the right query
 if (!$query->is_category('news')) {
    return;
 }

 // Next, determine paged
 $ppp = get_option('paged');
}
// Next, handle paged
if($ppp > 1){
    $query->set('paged', $ppp-1);
}

Prueba algo como

$paged = ( get_query_var('paged') ) ? get_query_var('paged')-1 : 1;
$args = array (
    'paged' => $paged,
);
Kika
fuente
He corregido mi explicación de cómo funciona la variable 'paginada'. Parece que lo que intenta hacer es reducir cada número de página en 1, por lo que una solicitud para la página 2 le da la página 1. El problema es que cuando se consulta la página 1, 'paginado' se establece en 0. Además, get_query_var () debe usarse en lugar de get_option para 'paginado'. Con esa solución, cuando esto se ejecuta, cada vez que se cambia la consulta, se repite nuevamente hasta que 'paginado' sea 0 para cualquier solicitud de página.
willcwelch