¿Usando WP_Query para consultar varias categorías con publicaciones limitadas por categoría?

10

Tengo 3 categorías con cada 15 publicaciones, quiero hacer UNA consulta a la base de datos con solo 5 primeras publicaciones para cada categoría, ¿cómo puedo hacerlo?

$q = new WP_Query(array( 'post__in' => array(2,4,8), 'posts_per_page' => **FIRST_5_OF_EACH_CAT** ));

En caso de que no sea posible, ¿qué es más eficiente, obtener todas las publicaciones para la categoría principal y recorrerlas o crear 3 consultas diferentes?

Amit
fuente
¿Cómo quieres que se ordenen?
MikeSchinkel
publicado recientemente ... así que 5 más recientes de la categoría A, cosas más recientes de la Categoría B, etc.
Amit
Bien, mira mi respuesta a continuación
MikeSchinkel

Respuestas:

16

Lo que desea es posible, pero requerirá que profundice en SQL, lo que me gusta evitar siempre que sea posible (no porque no lo sepa, soy un desarrollador avanzado de SQL, sino porque en WordPress desea usar la API siempre que sea posible) para minimizar futuros problemas de compatibilidad relacionados con futuros cambios potenciales en la estructura de la base de datos).

SQL con un UNIONoperador es una posibilidad

Para usar SQL, lo que necesita es un UNIONoperador en su consulta, algo como esto, suponiendo que las babosas de su categoría sean "category-1", "category-1"y "category-3":

SELECT * FROM wp_posts WHERE ID IN (
  SELECT tr.object_id
  FROM wp_terms t 
  INNER JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
  INNER JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
  WHERE tt.taxonomy='category' AND t.slug='category-1'
  LIMIT 5

  UNION

  SELECT tr.object_id
  FROM wp_terms t 
  INNER JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
  INNER JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
  WHERE tt.taxonomy='category' AND t.slug='category-2'
  LIMIT 5

  UNION

  SELECT tr.object_id
  FROM wp_terms t 
  INNER JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
  INNER JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
  WHERE tt.taxonomy='category' AND t.slug='category-3'
  LIMIT 5
)

Puede usar SQL UNION con un posts_joinfiltro

Usando lo anterior, puede hacer la llamada directamente o puede usar un posts_joinenlace de filtro de la siguiente manera; tenga en cuenta que estoy usando un PHP heredoc, así que asegúrese de que quede al SQL;ras. También tenga en cuenta que utilicé una var global para permitirle definir las categorías fuera del gancho al enumerar las babosas de categoría en una matriz. Puede poner este código en un complemento o en el functions.phparchivo de su tema :

<?php
global $top_5_for_each_category_join;
$top_5_for_each_category_join = array('category-1','category-2','category-3');
add_filter('posts_join','top_5_for_each_category_join',10,2);
function top_5_for_each_category_join($join,$query) {
  global $top_5_for_each_category_join;
  $unioned_selects = array();
  foreach($top_5_for_each_category_join as $category) {
    $unioned_selects[] =<<<SQL
SELECT object_id
FROM wp_terms t
INNER JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
INNER JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
WHERE tt.taxonomy='category' AND t.slug='{$category}'
LIMIT 5
SQL;
  }
  $unioned_selects = implode("\n\nUNION\n\n",$unioned_selects);
  return $join . " INNER JOIN ($unioned_selects) categories ON wp_posts.ID=categories.object_id " ;
}

Pero puede haber efectos secundarios

Por supuesto, usar los ganchos de modificación de consultas como posts_joinsiempre invita a los efectos secundarios, ya que actúan globalmente en las consultas y, por lo tanto, generalmente necesita envolver sus modificaciones en una ifque solo lo use cuando sea necesario y los criterios para probar pueden ser complicados.

Centrarse en la optimización en su lugar?

Sin embargo, supongo que su pregunta se refiere más a la optimización que a la posibilidad de hacer una consulta de 5 veces 3, ¿verdad? Si ese es el caso, ¿tal vez hay otras opciones que usan la API de WordPress?

¿Es mejor usar API de transitorios para el almacenamiento en caché?

Asumo que tus publicaciones no cambiarán tan a menudo, ¿correcto? ¿Qué sucede si acepta las tres (3) consultas que recibe periódicamente y luego almacena en caché los resultados con la API de transitorios ? Obtendrá código mantenible y un gran rendimiento; un poco mejor que la UNIONconsulta anterior porque WordPress almacenará las listas de publicaciones como una matriz serializada en un registro de la wp_optionstabla.

Puede tomar el siguiente ejemplo y colocarlo en la raíz de su sitio web test.phppara probar esto:

<?php

$timeout = 4; // 4 hours
$categories = array('category-1','category-2','category-3');

include "wp-load.php";
$category_posts = get_transient('top5_posts_per_category');
if (!is_array($category_posts) || count($category_posts)==0) {
  echo "Hello Every {$timeout} hours!";
  $category_posts = array();
  foreach($categories as $category) {
    $posts = get_posts("post_type=post&numberposts=5&taxonomy=category&term={$category}");
    foreach($posts as $post) {
      $category_posts[$post->ID] = $post;
    }
  }
  set_transient('top5_posts_per_category',$category_posts,60*60*$timeout);
}
header('Content-Type:text/plain');
print_r($category_posts);

Resumen

Si bien sí, puede hacer lo que solicitó utilizando una UNIONconsulta SQL y el posts_joinenlace de filtro que probablemente le ofrece mejor utilizando el almacenamiento en caché con la API de transitorios .

¿Espero que esto ayude?

MikeSchinkel
fuente
2
El almacenamiento en caché a través de transitorios es una buena cosa para reducir el impacto en el sitio.
Hakre
1
@Mike gran idea sobre el uso de los transitorios.
Chris_O
@ Mike, si viviera en tu continente, ¡habría tomado clases contigo! ¡gracias de nuevo!
Amit
@Amit: LOL! :-)
MikeSchinkel
1
¿también podría guardar el transitorio indefinidamente y luego enjuagarlo en save_post? hay un buen ejemplo (usando el gancho edit_term) en el códice
helgatheviking
1

WP_Query()no es compatible con algo como First X For Each Cat . En lugar de WP_Query()usarlo get_posts()en cada una de sus categorías, por decirlo tres veces:

<?php
$posts      = array():
$categories = array(2,4,8);
foreach($categories as $cat) {
  $posts[] = get_posts('numberposts=5&offset=1&category='.$cat);
}
?>

$posts ahora contiene las primeras cinco publicaciones para cada categoría.

hakre
fuente
¿No son esos 3 golpes a la base de datos? Quería saber si puedes hacerlo en 1 golpe porque pensé que es más eficiente.
Amit
bueno, para eso está el db, ¿verdad? consultar los datos de ella. El DB está hecho para tales cosas. probablemente sea más rápido que ejecutar una consulta sql complicada que está solicitando. No temas usar las cosas. si rompe su sitio, repórtelo aquí.
Hakre
mm .. lo tengo, no sé mucho sobre la eficiencia de php / mysql / db (me gustaría leer más), estaba seguro de que menos hits es mejor y luego analizar el resultado yo mismo.
Amit
1
bueno, cierto, generalmente más es más y menos es menos. Pero intenta y prueba primero. Cuanto más "malo" es su software, más potencial tiene para la optimización. Así que mejor aprende haciendo, porque al final escribirás mejor software más rápido y mejor escribirás más rápido.
Hakre
0

No conozco una forma de obtener las primeras cinco publicaciones para cada una de las categorías en una sola consulta. Si alguna vez va a tener solo 45 publicaciones, acceder a la base de datos una vez y obtener sus cinco publicaciones para cada categoría es probablemente el enfoque más eficiente. Sin embargo, golpear la base de datos tres veces y combinar los resultados no es algo malo.

Bernard Chen
fuente