Using a PostQuery in your Template

Code

March 8, 2022

No more tb.get_posts() ! You can do a Timber PostQuery inside your Timber Posts Modules from now on.

Added in Toolbox 1.2.3
Make sure to update your Toolbox version before using the PostQuery function

Previously, when doing a query inside the Timber Posts module you needed to use tb.get_posts(), but had to fetch a seperate Timber Post Object to get access to all Timber Objects:


{% set args = { post_type: 'my_post_type' } %}
{% set posts = tb.get_posts( args ) %}
{% for item in posts %}
    {# reset the WP Post Object-item as a Timber Post-item #}
    {% set item = Post( item.ID ) %}
	<h3>{{item.title}}</h3>
	<div>{{item.preview(50)}}</div>
{% endfor %}


This is quite useful and easy to use, and of course this will still do in most cases. But since Toolbox 1.2.3 there is an alternative that returns Timber Post Objects by default and also brings useful features like pagination.

Let's showcase how easy it now is to create a paginated result container. For this we will use the Twig template below as a basis.


{% set args = { post_type: 'code', posts_per_page: 4 } %}
{% set posts = PostQuery( args ) %}
<div class="uk-child-width-1-2@s the-posts" uk-grid>
{% for item in posts %}
    <div>
        <div class="uk-card uk-card-default uk-card-body">
    	<h3>{{item.title}}</h3>
    	<div>{{item.preview(50)}}</div>
    	</div>
    </div>
{% endfor %}
</div>
<div class="uk-margin-top">
{% for item in posts.pagination.pages %}
    <a href="javascript:void(0);" 
        class="load-more pagination-item{{ item.current ? ' current' }}" 
        data-args="{{ args|json_encode|e( 'html' )}}" 
        data-page="{{item.name}}">
        {{- item.name -}}
    </a>
{% endfor %}
</div>

And this should give you a result similar to this one (using this site's 'code' CPT):

Using a PostQuery in your Template

No more tb.get_posts() ! You can do a Timber PostQuery inside your Timber Posts Modules from now on. Added in Toolbox 1.2.3 Make sure to update your Toolbox version before using the PostQuery function Previously, when doing a query inside the Timber Posts module you needed to use tb.get_posts(), but… Read More

Adding Global Twig Variables

Have you ever had the need to set global variables that can be used across multiple Twig templates? Then you might have tried to create a Twig Template, set a few variables and included that in another template, only to discover that you couldn’t get to those variables because they… Read More

Tabs by month

If you want to avoid the clutter of listing all posts as one big list, you might consider grouping them in accordions by month for instance. But how would you be able to do that yourself? You can do that easily in Toolbox using the Timber Posts module! {# let's… Read More

Adding a masonry grid

A masonry grid is when you have items that are laid out one after the other in the inline direction. When items move onto the next line, they will also move up into any gaps left by shorter items in the first line. Here’s an example: Card Title Officiis odio… Read More
1 2 3 4 5

Adding a WP AJAX action hook

As you can see in the pagination code, we are using our base args (arguments for the query) and our page to set data-attributes on the links. We will be passing these to our AJAX call later using some javascript. For now, let's concentrate on the PHP to add a WP AJAX action hook. We'll be adding a generic function so we can use any Twig template we want for a custom call.


/**
 * Callback to render a Twig template
 * @return [type] [description]
 */
function load_more_twig( $twig ) {

	//if ( !is_user_logged_in() ) wp_die();

	DEFINE( 'DOING_AJAX' , true );

	$data = \Timber::get_context();

	// buffer output
	ob_start();

	// Use try to prevent failure
	try {
		// render $twig template with numberposts and offset as variables
		\Timber::render( $twig , $data );
	} catch( Exception $e ) {
		if ( apply_filters( 'toolbox/twig_error_debug' , true ) ) echo '[ error handling twig template ] ' . $e->getMessage();
	}

	// echo result
	echo ob_get_clean();

	// wp_die() because we want it to end here
	wp_die();

}

/**
 * WP AJAX action callback
 */
function twig_load_more() {
	load_more_twig( 'twig-load-more-demo.twig' );
}

add_action( 'wp_ajax_twig-load-more' , 'twig_load_more' );
add_action( 'wp_ajax_nopriv_twig-load-more' , 'twig_load_more' );



The load_more_twig function

The load_more_twig function is added so we can call any Twig template that we have registered. It takes a variable called $twig, which is the path and filename to that Twig template.

The twig_load_more function

The twig_load_more function is the callback that is getting executed when the wp_ajax action 'twig-load-more' is being called using the /wp-admin/admin-ajax.php?action={action} call. You can see that being added on the last two lines.

To sum things up, when the call is being performed, it will execute the function/callback twig_load_more(), which in turn will execute load_more_twig( 'twig-load-more-demo.twig' ). This should render the template results back to the script calling it.

Adding javascript to perform the request

In order to perform the request, we will add a bit of javascript to our layout. The following javascript will listen to clicks on the DOM elements with a .load-more class attached to it. We've used this class for our pagination links.

When any of the pagination links is clicked, it uses $.ajax to request our WP AJAX action 'twig-load-more' and passes in our link's data-args and data-page attributes as data variables. We'll use these to lign up our Twig request with our original call.

When the call receives the rendered template, it will replace the contents of the element with a class of .the-posts with it.


(function($) {

    $( '.load-more' ).on( 'click' , function() {
        // remove the current class from any load-more item
        $( '.load-more' ).each( function( index ){ $(this).removeClass( 'current' ); } )
        
        $this = $(this);
        // add current class
        $this.addClass( 'current' );
        $.ajax( {
            method: 'POST',
            url: '/wp-admin/admin-ajax.php?action=twig-load-more',
            data: {
                args: $this.data( 'args' ),
                page: $this.data( 'page' ),
            }
        } ).done( function( data ) {
            $( '.the-posts' ).html( data );
        });
    });

})(jQuery);



Adding our Twig template for paginated results

Now all that remains is adding the Twig template that will render our paginated call. For this we will add it to our Twig Templates CPT. Make sure that the slug for the template is 'twig-load-more-demo', so that our template will be called correctly.


{% set args = request.post.args|merge( { paged: request.post.page , post_status: 'publish' } ) %}
{% set posts = PostQuery( args ) %}
{% for item in posts %}
    <div>
        <div class="uk-card uk-card-default uk-card-body">
        	<h3>{{item.title}}</h3>
        	<div>{{item.preview(50)}}</div>
    	</div>
    </div>
{% endfor %}


Notice that here, we are using the Timber request object that was added using the Timber::get_context() function in our PHP, to access any post data. We are merging the args, which we passed to our call, with our page number and a post_status so that we are sure that only published posts are showing.

Because we are replacing the content inside our .the-posts element only, we only render the elements, and not the surrounding container.

Beaverplugins

Web ninja with PHP/CSS/JS and Wordpress skills. Also stand-in server administrator, father of four kids and husband to a beautiful wife.
Always spends too much time figuring out ways to do simple things even quicker. So that you can benefit.