Visualizzare i post per mese

27 gen 2015, 11:52:21
Visualizzazioni: 27K
Voti: 3

Voglio ottenere qualcosa del genere, non so se è possibile e quale sarebbe il modo migliore per farlo:

Esempio di visualizzazione post raggruppati per mese

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?

2
Commenti

Per ottenere questa struttura avresti bisogno di più query. Prima dovresti identificare i mesi da mostrare, e poi ciclare attraverso i risultati per prendere i post rilevanti. Proverò a dare una risposta a breve...

David Gard David Gard
27 gen 2015 13:47:13

Puoi ottenere questo con una singola query senza usare query_posts

Pieter Goosen Pieter Goosen
27 gen 2015 15:18:55
Tutte le risposte alla domanda 3
5
11

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, usa WP_Query o get_posts

  • showposts è stato sostituito anni fa da posts_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();

    }

}
27 gen 2015 16:44:06
Commenti

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 :)

David Gard David Gard
27 gen 2015 17:37:37

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.

David Gard David Gard
27 gen 2015 17:39:25

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

Pieter Goosen Pieter Goosen
27 gen 2015 17:50:13

@wpuser piacere mio. Divertiti :-)

Pieter Goosen Pieter Goosen
27 gen 2015 17:50:51

@PieterGoosen - Lo capisco, ho pensato valesse la pena menzionarlo per chiunque altro potrebbe imbattersi in questa risposta in futuro. So di confondermi facilmente, quindi presumo che sia così anche per gli altri!

David Gard David Gard
27 gen 2015 17:56:38
7

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;
27 gen 2015 14:29:12
Commenti

Puoi farlo con una singola query :-)

Pieter Goosen Pieter Goosen
27 gen 2015 14:33:14

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 :-/

David Gard David Gard
27 gen 2015 14:36:49

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

Pieter Goosen Pieter Goosen
27 gen 2015 15:18:14

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

David Gard David Gard
27 gen 2015 15:29:51

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

David Gard David Gard
27 gen 2015 16:39:16

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 :-)

Pieter Goosen Pieter Goosen
27 gen 2015 18:15:08

Grazie :) Di solito mi impegno molto nelle mie risposte, parto sempre dal presupposto che l'utente inizi da zero (a meno che non venga fornito del codice).

David Gard David Gard
27 gen 2015 18:36:53
Mostra i restanti 2 commenti
4

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... :)

15 ago 2017 11:59:30
Commenti

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 birgire
15 ago 2017 16:45:52

@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...

Luca Reghellin Luca Reghellin
16 ago 2017 16:05:42

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

birgire birgire
16 ago 2017 17:52:27

Ok grazie, ho aggiunto suggerimenti sulla sicurezza, penso siano sufficienti, la domanda originale non riguardava problemi di sicurezza :)

Luca Reghellin Luca Reghellin
16 ago 2017 19:09:10