Search code examples
ajaxwordpresstimber

Load more posts in the same category - WordPress + Ajax + Timber


I use Timber with WordPress.

I'm trying to create a list of posts with a "Load more posts" button.

I would like to display 10 posts of the same category and to load 10 others posts of the same category when the user click on the button "Load more posts"

All work fine when there is no distinction between categories. The button "Load more posts" work fine. 10 posts are display.

But when I try to display posts in the same category when I click on the button "Load more posts". No post are display. What's wrong with category ?

Can I have some help please ?

archive.php

$category = get_the_category($post->ID);
$category = $category[0]->cat_ID;

$context['posts'] = Timber::get_posts(array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'category__in' => array($category),
    'posts_per_page' => 10,
    'paged' => 1,
    'has_password' => FALSE
));

script.js

function load_more_news() {
    var page = 1;

    $(document).on('click', '#load-more-news', function(e) {
        e.preventDefault();
        page++;

        $.ajax({
            type: 'POST',
            url: '/wp-admin/admin-ajax.php',
            dataType: 'html',
            data: {
                'action' : 'get_news',
                'get_page' : page
            },
            success: function(data) {
                if($('<div></div>').html(data).find('.archive__item.ended').size() > 0) $('#load-more-news').parents('.cta').remove();
                else $('#load-more-news').parents('.cta').show();
                $('#archive__list').append(data);
            },
            error: function(data) {
                console.log(data);
            }
        });
    });
}

functions.php

add_action( 'wp_ajax_nopriv_get_news', 'get_news' );
add_action( 'wp_ajax_get_news', 'get_news' );
function get_news() {
    global $post;

    $context = Timber::get_context();
    $context['get_page'] = empty($_POST['get_page']) ? 1 : $_POST['get_page'];

    $category = get_the_category($post->ID);
    $category = $category[0]->cat_ID;

    $context['posts'] = Timber::get_posts(array(
        'post_type' => 'post',
        'post_status' => 'publish',
        'category__in' => array($category),
        'posts_per_page' => 10,
        'paged' => $context['get_page'],
        'has_password' => FALSE
    ));
    $count = count(Timber::get_posts(array(
        'post_type' => 'post',
        'posts_per_page' => -1,
        'post_status' => 'publish',
        'category__in' => array($category)
    )));

    if($count <= $context['get_page'] * 10) $context['ended'] = 'ended';

    Timber::render( 'bloc_news.twig', $context );

    die();
}

archive.twig

<section class="archive">
    <p class="archive__title h-headline text-size-l">In the same thematic</p>
    <div id="archive__list" class="archive__list">
        <article class="post">
            ...
        </article>
    </div>
</section>

{% if posts|length >= 10 %}
<section class="cta">
    <a href="#" id="load-more-news">
        <p class="cta__text">Load more posts</p>
    </a>
</section>
{% endif %}

Solution

  • To be honest, it's difficult to understand you, but it seems to me that your problem is that you are pointing all the time to a global post

    function get_news() {
        global $post;
    

    You need to explicitly request the category what does this mean, I will explain

    You need to transfer id categories to the server, and not use global $post. The algorithm is as follows:

    1) return the category id to the button on the client

    <button data-cid="{category_id}">Load more posts</button>
    

    or somewhere in the parent element of the category

    <div class="category" data-cid="<?php print $category_id; ?>">
            <!-- 10 posts -->
            <article class="post">...</article>
            ....
            <button>Load more posts</button>
    </div>
    

    2) After clicking on the button, get this СID (category id) and send it to the server

    function load_more_news() {
        var page = 1;
    
        $(document).on('click', '#load-more-news', function(e) {
            let $el = $(this); // of e.currentTarget;
            e.preventDefault();
            page++;
    
            $.ajax({
                type: 'POST',
                url: '/wp-admin/admin-ajax.php',
                dataType: 'html',
                data: {
                    'action': 'get_news',
                    'get_page': page,
                    'cid': $el.data('cid') // CID or $el.parents('[data-cid]').data('cid');
                },
                success: function(data) {
                    if ($('<div></div>').html(data).find('.archive__item.ended').size() > 0) $('#load-more-news').parents('.cta').remove();
                    else $('#load-more-news').parents('.cta').show();
                    $('#archive__list').append(data);
                },
                error: function(data) {
                    console.log(data);
                }
            });
        });
    }
    

    3) use this on server instead of global post

    'category__in' => array($_POST['cid'])
    

    Addition:

    What means: when I click on the button "Load more posts". No post are display. ?

    1. What is returned from the server?
    2. Check what you get with ::get_posts()?
    3. Why don't you just use get_posts of native wordpress api function ?

    Check and debug all data that comes to the server and returns

    Organize your code: 1.) send ajax url to the client

    wp_register_script('your-script', PLUG_URL.'your-script.js', array('jquery'), filemtime(  PLUG_DIR.'your-script.js' ), true);
       
    wp_localize_script('your-script', 'glb',
        array(
            'ajax_url' => admin_url('admin-ajax.php')
        )
    );
    

    and use it and use this instead of hardcore with $.post function of jquery (recommended)

    let payload = {
      page: 2
    }
    $.post(glb.ajax_url, {action: 'get_news', data: payload, function(e) {
     ....
    });
    

    2.) Check what comes to the server

    function get_news() {
         wp_send_json($_POST['data']);
    }
    $.post(glb.ajax_url, {action: 'get_news', data: payload, function(e) {
         console.log(e); // {page: 2}
    });
    
    function get_news() { 
        if(!isset($_POST['data'])) {
          wp_send_json_error();
    }
    $data = $_POST['data'];
    
    $posts = get_posts( array(
            'numberposts' => 10,
            'post_status' => 'publish',
            'category__in'    => [$category],
            'orderby'     => 'date',
            'order'       => 'DESC',
            'post_type'   => 'post',
            'paged' => $data['page']
        ));
      wp_send_json($posts);
    }
    $.post(glb.ajax_url, {action: 'get_news', data: payload, function(e) {
       console.log(e); // ??? 
    });
    

    Make checks, return data and look for where you have a "hole"