WordPress: Încărcarea dinamică a articolelor cu un anumit term

Zilele trecute am avut următoarea situație:

  • În backend: o taxonomie custom cu câteva zeci de terms
  • În frontend: afișez ultimele zece articole dintr-un term + buton de încărcare a următorului term.

Prima idee a fost: încarc toate articolele dintr-un foc și fac toggle la vizibilitate cu JS. Dar se ajunge lejer la câteva sute de articole, lucru ce afectează performanța din toate punctele de vedere.

Concret, aveam o taxonomie pentru țări – fiecare term reprezenta o țară – și trebuia să afișez ultimele articole pentru prima țară. Apăsai buton, mai încărca o țară. Apăsai iar, încă o țară șamd.

Ca să facem asta cât mai eficient, trebuie să extragem logica necesară afișării articolelor dintr-o țară ori într-o funcție ori într-o clasă separată. Folosim o funcție:

function showCountry($countryID)
{
  $term = get_term($countryID);

  $countryQuery = new WP_Query([
    'post_type' => 'myPostType',

    'tax_query' => [
      [
        'taxonomy' => $term->taxonomy,
        'terms' => $term->term_id,
      ],
    ],

    'posts_per_page' => 10,
  ]);

  while ($countryQuery->have_posts()) {
    $countryQuery->the_post();
    require get_template_directory() . '/templates/loop-more-countries.php';
  }

  wp_reset_postdata();
}

Tot codul responsabil de markup se află în loop-more-countries.php dar acolo nu se întâmplă nimic interesant ce ar merita menționat.

Afișarea primei țări

Provocarea cea mai mare a fost să găsesc o modalitate eficientă de a afișa primul term la încărcarea paginii și restul la cerere, dinamic. Afișarea primului post e simplă, ne folosim de funcția de mai sus:

<?php 
  $countries = get_terms([
    'taxonomy' => 'country',
    'fields' => 'ids'
  ]);
?>

<div class="js-countries">
  <?php showCountry($countries[0]); ?>
<div>

Problema era: cum notific JS-ul despre ultimul term încărcat?

<button class="js-loadMoreCountries" 
  data-countries='<?php echo json_encode( array_slice($countries, 1) ) ?>'>Load more countries</button>

Păi… simplu: setez atributul data-countries ca un JSON ce conține ID-urilor tuturor terms mai puțin primul, cel deja afișat. Observă te rog că folosesc ghilimele simple, nu duble; asta pentru că jQuery știe să parseze JSON dintr-un atribut! Ce convenabil!

Vrei să afișezi mai multe terms inițial? Ori apelezi funcția showCountry de mai multe ori, ori, mai elegant, folosești array_walk:

array_walk(array_slice($countries, 0, 3), 'showCountry');

Evident, nu uităm să actualizăm și butonul:

json_encode( array_slice($countries, 3)

AJAX

Înainte de JS, hai să-i spunem WP-ului că așteptăm request-uri AJAX:

function loadMoreCountries()
{
  $termID = absint($_REQUEST['termID']);

  if ($termID>0) {
    showCountry($termID);
  }
  die();
}

add_action('wp_ajax_load_more_countries', 'loadMoreCountries');
add_action('wp_ajax_nopriv_load_more_countries', 'loadMoreCountries');

Javascript

Pentru javascript logica e simplă: facem un request AJAX la fiecare apăsare de buton și ținem minte index-ul ultimului term încărcat. Când încărcăm ultimul term, ascundem butonul:

jQuery(document).ready(function($) {
    var loadMoreButton = $('.js-loadMoreCountries');
    var container = $('.js-countries');
    var countriesIDs = loadMoreButton.data('countries'); // atributul data-countries al butonului de mai sus

    var index = 0; // index-ul ultimului term încărcat

    var loadingClass = 'is-loading';
    
    var callbacks = {
        beforeLoading: function() {
            button.addClass(loadingClass)
            container.addClass(loadingClass)
        },

        completeLoading: function() {
            button.removeClass(loadingClass);
            container.removeClass(loadingClass);
        },

        success: function(data) {
            container.append(data);
        },
    }

    loadMoreButton.on('click', function() {
        if (loadMoreButton.hasClass(loadingClass)) {
            return false;
        }

        var data = {
            action: 'load_more_countries',
            termID: countriesIDs[index]
        };

        index++; // următorul term

        if (index >= countriesIDs.length) {
            loadMoreButton.hide();
        }

        $.ajax({
            url: loadMoreCountries.ajaxUrl,
            data: data,
            type: 'GET',
            beforeSend: callbacks.beforeLoading,
            complete: callbacks.completeLoading,
            success: callbacks.success
        });

        return false;
    })
});

Cam atât. Atenție însă, nu vrei să folosești această metodă dacă ai foarte multe terms! Cred că se pretează bine la maximum 50-100 items, dar peste s-ar putea să ai nevoie de o altă abordare. Oricum ar fi, nu uita să faci și cache la terms, altfel s-ar putea să aibă un impact asupra performanței (dar despre asta într-un alt articol).

Publicat de

Ionuț Staicu

este frontend & WordPress developer, iar în timpul liber administrează DevForum.