Visualizzare i post per mese
Voglio ottenere qualcosa del genere, non so se è possibile e quale sarebbe il modo migliore per farlo:
Il modo in cui interrogo i post è questo:
<div class="post">
<?php global $wp_query;
query_posts( array(
'post_type' => array( 'lecturas-post' ),
'showposts' => 15,
'paged'=>$paged,
'order' => 'DESC'
));?>
<?php while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>
<div><?php the_title() ?></a>
<?php endwhile; // fine del ciclo ?>
</div>
Qualcuno può darmi un suggerimento su come o qual è il modo migliore per farlo?

Come detto in un commento, puoi farlo con una singola query. Il principio qui è visualizzare l'intestazione della data solo se il mese della data del post corrente non corrisponde a quello del post precedente.
ALCUNE NOTE
Prima di iniziare, alcune note:
Non usare mai
query_posts
, a meno che non sia necessario interrompere tutto su una pagina. Non solo riavvia la query principale e la interrompe, ma scombussola la paginazione e le variabili globali, oltre a interferire con le funzioni dell'oggetto della query. Se devi eseguire una query personalizzata, usaWP_Query
oget_posts
showposts
è stato sostituito anni fa daposts_per_page
Non è necessario utilizzare la variabile globale
$wp_query
.query_posts
la scombussola comunque
IL PIANO
I post vengono serviti in ordine cronologico con il post più recente per primo e il più vecchio per ultimo, quindi sono già nell'ordine corretto. Si tratta solo di visualizzare la data nel punto appropriato.
Per farlo, tutto ciò che devi fare è ottenere il mese e l'anno correnti dalla data del post corrente e confrontarli con il mese della data del post precedente, quindi visualizzare la data se i mesi non corrispondono o non visualizzarla se corrispondono.
Come spiegazione, utilizzerò il loop principale con la query principale.
Per ottenere questo risultato, devi:
Ottenere il mese dalla data del post corrente. Per farlo, usa
get_the_date( 'F' )
Ottenere il post precedente nel loop con
$wp_query->posts['questo sarà il post corrente -1 ']->post
.Ottenere e confrontare i mesi tra i due post
Visualizzare o non visualizzare la data in base al confronto
IL CODICE
Questo codice va inserito nel tuo loop, subito dopo l'istruzione while()
.
$current_month = get_the_date('F');
if( $wp_query->current_post === 0 ) {
the_date( 'F Y' );
}else{
$f = $wp_query->current_post - 1;
$old_date = mysql2date( 'F', $wp_query->posts[$f]->post_date );
if($current_month != $old_date) {
the_date( 'F Y' );;
}
}
QUERY PERSONALIZZATA
Se hai bisogno di eseguire una query personalizzata, prova questo
$q = new WP_Query( array('post_type' => array( 'lecturas-post' ),'posts_per_page' => 15, 'paged'=>$paged, 'order' => 'DESC' ));
if( $q->have_posts() ) {
while( $q->have_posts() ) {
$q->the_post();
$current_month = get_the_date('F');
if( $q->current_post === 0 ) {
the_date( 'F Y' );
}else{
$f = $q->current_post - 1;
$old_date = mysql2date( 'F', $q->posts[$f]->post_date );
if($current_month != $old_date) {
the_date( 'F Y' );;
}
}
the_title();
}
}

Buona risposta, e un concetto simile per la sezione della tua query personalizzata come nella mia risposta aggiornata, solo un modo diverso per verificare se il titolo è stato visualizzato. Immagino che sia l'OP a decidere se vuole incorporare il date_query
o meno :)

Una cosa che dirò però è che un mese potrebbe essere diviso tra più pagine. Se all'OP non interessa, allora tutto bene, ho solo pensato valesse la pena sottolinearlo.

@DavidGard grazie. La data verrà visualizzata su ogni pagina sul primo post a prescindere. Se non ne hai bisogno, aggiungi la condizione is_paged()
.

Risposta Aggiornata
Dopo aver riflettuto sul commento di @PieterGoosen qui sotto, ho aggiunto un metodo per raggiungere l'obiettivo utilizzando una singola query.
Ho fatto dei benchmark sui metodi e la singola query è più veloce, con il metodo a query multiple più lento di circa il 15%. Non una differenza enorme, ma ogni piccolo aiuto conta, e onestamente il metodo può probabilmente essere ulteriormente perfezionato.
Ho lasciato il metodo a query multiple nella risposta per completezza, ma consiglio di utilizzare il metodo a singola query.
Metodo a Singola Query
$time_start = microtime(true);
/** Imposta un oggetto DateInterval per 6 mesi fa (puoi modificarlo come necessario) */
$interval = new DateInterval('P6M');
$interval->invert = 1;
/** Ottiene la data di 6 mesi fa */
$date = new DateTime(date('Y-m-d'));
$date->add($interval);
/** Interroga il database per tutti i post più recenti del dato intervallo di date */
$args = array(
'nopaging' => true,
'posts_per_page' => -1,
'post_type' => 'post',
'post_status' => 'publish',
'order_by' => 'date',
'date_query' => array(
'after' => $date->format('Y-m-d')
)
);
$month_query = new WP_Query($args);
/** Verifica che ci siano articoli per questo mese... */
if($month_query->have_posts()) :
$month_titles = array();
$close_ul = false;
//echo '<ul style="padding-left: 250px;" id="monthly-posts">';
/** Imposta gli attributi per visualizzare il titolo come attributo */
$title_attribute_args = array(
'before' => 'Visita l\'articolo \'',
'after' => '\' ',
'echo' => false
);
/** Cicla attraverso ogni post per questo mese... */
while($month_query->have_posts()) : $month_query->the_post();
/** Controlla il mese/anno del post corrente */
$month_title = date('F Y', strtotime(get_the_date('Y-m-d H:i:s')));
/** Se la data non è stata già mostrata, la visualizza in formato leggibile */
if(!in_array($month_title, $month_titles)) :
if($close_ul) echo '</ul>'; // Controlla se la lista non ordinata dei post deve essere chiusa (non dovrebbe per il primo '$monthe_title')
echo '<h1 style="padding-left: 250px;" id="monthly-title">' . $month_title . '</h1>'; // Mostra il '$month_title'
echo '<ul style="padding-left: 250px;" id="monthly-posts">'; // Apre una lista non ordinata per i post che verranno
$month_titles[] = $month_title; // Aggiunge questo `$month_title' all'array di `$month_titles` così che non venga ripetuto
$close_ul = true; // Indica che la lista non ordinata deve essere chiusa alla prossima opportunità
endif;
/** Mostra ogni articolo per questo mese */
printf(
'<li id="monthly-post-%1$s">%2$s <a href="%3$s" title="%4$s">%3$s</a></li>',
get_the_ID(), /** %1$s - L'ID del post */
get_the_title(), /** %2$s - Il titolo dell'articolo */
get_permalink(get_the_ID()), /** %3$s - Il link dell'articolo */
the_title_attribute($title_attribute_args) /** %4$s - Il titolo da usare come attributo */
);
endwhile;
if($close_ul) echo '</ul>'; // Chiude l'ultima lista non ordinata di post (se ce ne sono di mostrati)
endif;
/** Resetta la query così che WP non faccia cose strane */
wp_reset_query();
Risposta Originale
Prova questo. L'ho impostato in modo che vengano selezionati solo gli ultimi 6 mesi e solo gli ultimi 5 post di ogni mese, ma puoi modificarlo come preferisci.
Sostanzialmente il codice prima verificherà quali mesi hanno dei post e poi mostrerà gli ultimi cinque post di quel mese, insieme a un link.
Metodo a Query Multiple
global $wpdb, $wp_locale;
/** Interroga i singoli mesi da mostrare (ho scelto gli ultimi 6 mesi) */
$query = $wpdb->prepare('
SELECT DISTINCT YEAR(`%1$s`.`post_date`) AS year, MONTH(`%1$s`.`post_date`) AS month
FROM `%1$s`
WHERE `%1$s`.`post_type` = "post"
ORDER BY `%1$s`.`post_date` DESC
LIMIT 6',
$wpdb->posts
);
$months = $wpdb->get_results($query);
/** Conta il numero di mesi */
$month_count = count($months);
/** Verifica che ci siano mesi da mostrare... */
if($month_count || ($month_count === 1 && $months[0]->month > 0)) :
/** Cicla attraverso ogni mese... */
foreach($months as $month) :
if($month->year === 0) :
continue;
endif;
/** Prende il singolo mese e anno, e costruisce una data leggibile (per il titolo) */
$m = zeroise($month->month, 2);
$y = $month->year;
$human_date = sprintf(__('%1$s %2$d'), $wp_locale->get_month($m), $y);
/** Prende i post per questo mese (ho scelto solo gli ultimi 5 post) */
$args = array(
'nopaging' => true,
'posts_per_page' => 5,
'post_type' => 'post',
'post_status' => 'publish',
'order_by' => 'date',
'year' => $y,
'monthnum' => $m
);
$month_query = new WP_Query($args);
/** Verifica che ci siano articoli per questo mese... */
if($month_query->have_posts()) :
/** Mostra una data leggibile */
echo '<h1 id="monthly-title">' . $human_date . '</h1>';
echo '<ul id="monthly-posts">';
/** Imposta gli attributi per visualizzare il titolo come attributo */
$title_attribute_args = array(
'before' => 'Visita l\'articolo \'',
'after' => '\' ',
'echo' => false
);
/** Cicla attraverso ogni post per questo mese... */
while($month_query->have_posts()) : $month_query->the_post();
/** Mostra ogni articolo per questo mese */
printf(
'<li id="monthly-post-%1$s">%2$s <a href="%3$s" title="%4$s">%3$s</a></li>',
get_the_ID(), /** %1$s - L'ID del post */
get_the_title(), /** %2$s - Il titolo dell'articolo */
get_permalink(get_the_ID()), /** %3$s - Il link dell'articolo */
the_title_attribute($title_attribute_args) /** %4$s - Il titolo da usare come attributo */
);
endwhile;
echo '</ul>';
endif;
/** Resetta la query così che WP non faccia cose strane */
wp_reset_query();
endforeach;
endif;

Davvero? A cosa stai pensando? Avevo considerato GROUP BY
, ma l'OP vuole intestazioni per mese/anno, quindi il mio piccolo cervello non riesce a vedere oltre le query multiple :-/

Vedrò se riesco a postare una soluzione più tardi. Sono solo un po' impegnato :-)

Hmm, sono incuriosito… Suppongo che potresti recuperare tutti i post più recenti di X usando date_query
, e poi suddividerli in un array multidimensionale. Potrei provarci, ma aspetto con ansia una risposta super semplice da te più tardi ;-)

@PieterGoosen Ho provato un approccio con una singola query, ma sarei grato per eventuali critiche (costruttive) che potresti avere!

La tua versione aggiornata sembra buona. Sicuramente un altro metodo valido, anche se potrebbe essere un po' eccessivo. Avrai il mio voto per la versione aggiornata :-)

Questa è una funzione che ho creato per esigenze generali, per recuperare dati di post o custom post types prima o dopo un determinato anno/mese, oppure l'anno corrente, nell'ordine di cui hai bisogno:
// puoi cambiare il nome nel caso entri in conflitto con qualche altro plugin
function get_posts_by_date($user_options = array()){
$options = array(
'year_limit' => '1980'
,'month_limit' => '01'
,'operator' => '>=' // operatore di confronto date
,'current_year' => true // limita i dati all'anno corrente
,'post_type' => 'post'
,'year_order' => 'DESC'
,'month_order' => 'DESC'
,'post_ids_order' => 'DESC'
,'raw_output' => false
);
extract(array_merge($options, $user_options));
global $wpdb;
if($operator == '>=' || $operator == '=='){
$day = "01";
} elseif($mode == '<='){
$day = "31";
}
if($current_year){ // sarà dopo il 31/12/[anno precedente]
$year_limit = date('Y', strtotime('-1 year'));
$month_limit = '12';
$day = "31";
$operator == '>=';
}
// attenzione: se i parametri provengono da input utente/form,
// passali usando $wpdb::prepare()
// https://developer.wordpress.org/reference/classes/wpdb/prepare/
$results = $wpdb->get_results("
SELECT tbl.y year, group_concat(month_posts ORDER BY tbl.m " . $month_order . " SEPARATOR '-') months
FROM (
SELECT YEAR(p.post_date) y, MONTH(p.post_date) m, concat(MONTH(p.post_date), ':', group_concat(p.id ORDER BY p.post_date " . $post_ids_order . " )) month_posts
FROM $wpdb->posts p
WHERE (p.post_status = 'publish' OR p.post_status = 'future')
AND p.post_type = '" . $post_type . "'
AND p.post_date " . $operator . " DATE('" . $year_limit . "-" . $month_limit . "-" . $day . " 00:00:00')
GROUP BY y, m
) tbl
GROUP BY tbl.y
ORDER BY tbl.y " . $year_order
);
if($raw_output) return $results;
global $wp_locale;
foreach ($results as $data){
$months_data = explode('-',$data->months);
$months = array();
$data->count = 0; // conteggio annuale
foreach ($months_data as $month_data){
$month_obj = new stdClass;
$splitted = explode(':',$month_data);
$raw_month = $splitted[0];
$month_obj->number = $raw_month;
$month_obj->name = $wp_locale->get_month($raw_month);
$month_obj->posts = array();
$post_ids = explode(',',$splitted[1]);
$data->count += count($post_ids);
foreach($post_ids as $post_id){
$month_obj->posts[] = get_post($post_id);
$months[$raw_month] = $month_obj;
}// foreach
}// foreach
$data->months = $months;
}// foreach
return $results;
}// get_posts_by_date
Esempio di utilizzo:
$posts_by_date = get_posts_by_date(array(
'year_limit' => '2016'
,'operator' => '<='
,'current_year' => false
,'post_type' => 'product'
,'month_order' => 'ASC'
,'raw_output' => true
));
Se l'opzione raw_output
è true, l'output predefinito sarà qualcosa del genere:
array(2) {
[0]=>
object(stdClass)#6645 (2) {
["year"]=>
string(4) "2017"
["months"]=>
string(65) "8:386,401-7:406,373,380,377,408,399,362-6:1,391,404-5:367,397,394"
}
[1]=>
object(stdClass)#6644 (2) {
["year"]=>
string(4) "2016"
["months"]=>
string(5) "6:429"
}
}
La stringa "months" contiene valori formattati come:
month:[post ids]-month:[post ids]-ecc
Se l'opzione raw_output
è false, otterrai una lista di post così strutturata:
array (array di oggetti)
object
-> year (es. '2017')
-> count (totale post annuali)
-> months (array di oggetti)
month
-> number (del mese)
-> name (localizzato)
-> posts (array di oggetti post)
Buon coding... :)

Attenzione alle possibili iniezioni SQL ed evita extract
(più difficile da debuggare) e non utilizzare nomi di funzioni generici per evitare possibili collisioni di nomi (o usa i namespace).

@birgire: bah... lo so tutto questo, ma sta allo sviluppatore valutare il nome della funzione in relazione al suo ambiente, ed eventualmente sanificare l'input utente/del form. Questo è solo per condividere codice utile, penso onestamente che questa funzione aiuterà molto. Poi, spero davvero che ogni sviluppatore -- (solo) se necessario -- sarà perfettamente in grado di cambiare il nome della funzione, e/o sanificare l'input utente o aggiungere una prepare statement invece della scrittura diretta...

Speriamo lo facciano, ma temo che molti si limitino a copiare/incollare il codice da questo sito e poi lo dimentichino, se ha funzionato la prima volta ;-)
