Utilizzo di WP_Query per eseguire query su più categorie con post limitati per categoria?


10

Ho 3 categorie con ogni 15 post, voglio fare UNA query al db portando solo 5 primi post per ogni categoria, come posso farlo?

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

Nel caso in cui ciò non fosse possibile, cosa è più efficiente, ottenere tutti i post per la categoria padre e scorrere ciclicamente o creare 3 diverse query?


Come vuoi che vengano ordinati?
MikeSchinkel,

recentemente pubblicato .. quindi più recente 5 della categoria A, roba più recente di categoria B ecc ..
Amit

Ok, guarda la mia risposta qui sotto
MikeSchinkel,

Risposte:


16

Quello che vuoi è possibile ma ti richiederà di approfondire SQL che mi piace evitare quando possibile (non perché non lo conosco, sono uno sviluppatore SQL avanzato, ma perché in WordPress vuoi usare l'API ogni volta che è possibile per ridurre al minimo i futuri problemi di compatibilità correlati a future modifiche alla struttura del database.)

SQL con un UNIONoperatore è una possibilità

Per utilizzare SQL quello che vi serve è un UNIONoperatore la tua ricerca, qualcosa di simile assumendo le vostre lumache categoria sono "category-1", "category-1"e "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
)

È possibile utilizzare SQL UNION con un posts_joinfiltro

Utilizzando quanto sopra puoi semplicemente effettuare la chiamata direttamente oppure puoi usare un posts_joinhook di filtro come segue; nota che sto usando un herpoc PHP, quindi assicurati che SQL;sia a filo. Nota anche che ho usato una var globale per permetterti di definire le categorie al di fuori del gancio elencando le lumache di categoria in un array. Puoi inserire questo codice in un plugin o nel functions.phpfile del tuo 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 " ;
}

Ma ci possono essere effetti collaterali

Ovviamente usare gli hook di modifica delle query come posts_joinsempre invita gli effetti collaterali in quanto agiscono globalmente sulle query e quindi di solito è necessario racchiudere le modifiche in uno ifche le utilizza solo quando necessario e quali criteri per testare possono essere difficili.

Concentrarsi sull'ottimizzazione, invece?

Tuttavia, suppongo che la tua domanda riguardi più l'ottimizzazione che la possibilità di eseguire una query 5 con la top 5, giusto? In tal caso, forse ci sono altre opzioni che utilizzano l'API di WordPress?

Meglio usare l'API dei transitori per la memorizzazione nella cache?

Presumo che i tuoi post non cambieranno così spesso, giusto? Cosa succede se si accetta periodicamente il tre (3) hit della query e si memorizzano nella cache i risultati usando l' API Transients ? Otterrai codice gestibile e grandi prestazioni; un po 'meglio della UNIONquery sopra perché WordPress memorizzerà gli elenchi di post come un array serializzato in un record della wp_optionstabella.

Puoi prendere il seguente esempio e rilasciarlo nella radice del tuo sito web test.phpper provarlo:

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

Sommario

Sebbene sì, puoi fare ciò che hai richiesto utilizzando una UNIONquery SQL e l' posts_joinhook del filtro che probabilmente ti è meglio offrire utilizzando la memorizzazione nella cache con l' API Transients .

Spero che sia di aiuto?


2
La memorizzazione nella cache tramite transitori è una buona cosa per ridurre l'impatto sul sito.
Hacre,

1
@Mike ottima idea sull'uso dei Transitori.
Chris_O,

@Mike, se vivessi nel tuo continente avrei preso lezioni con te! grazie ancora!
Dal

@Amit: LOL! :-)
MikeSchinkel,

1
potresti anche salvare il transitorio a tempo indeterminato e quindi scaricarlo su save_post? c'è un buon esempio (usando l'hook edit_term) nel codice
helgatheviking

1

WP_Query()non supporta qualcosa come First X For Each Cat . Invece di WP_Query()te puoi usare get_posts()su ciascuna delle tue categorie, per così dire tre volte:

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

$posts ora contiene i primi cinque post per ogni categoria.


non sono 3 i successi nel db? Volevo sapere se riesci a farlo in 1 colpo perché ho pensato che fosse più efficiente.
Entro il

bene, questo è ciò che serve per il db, giusto? interrogare i dati da esso. il db è fatto per queste cose. probabilmente è più veloce dell'esecuzione di una query sql complicata che stai chiedendo. non aver paura di usare le cose. se si rompe il tuo sito, segnalalo qui.
Hacre,

mm .. capito, non so molto sull'efficienza di php / mysql / db (vorrei leggere di più), ero sicuro che meno colpi sono migliori e quindi analizzo il risultato da solo.
Entro il

1
beh, vero, generalmente più è più e meno è meno. Ma prima prova e prova. Più "cattivo" è il tuo software, maggiore è il potenziale che ha per l'ottimizzazione. Quindi meglio imparare facendo, perché alla fine si scriverà un software migliore più velocemente e meglio si scriverà un software più veloce.
Hacre,

0

Non conosco un modo per ottenere i primi cinque post per ciascuna delle categorie in una singola query. Se hai mai solo 45 post, quindi colpire il database una volta e ottenere i tuoi cinque post per ogni categoria è probabilmente l'approccio più efficiente. Colpire il database tre volte e combinare i risultati non è male.

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.