This is a comparison of a default 'WordPress' template and the same template re-written to instead use Timber/TWIG to separate the coding from the theming.
Example Code:
This is for a custom-post type named 'Books' and the template-file is correctly named archive-books.php
.
You can see that the usage of have_posts()
, the get_query_var()
function, and get_post_type()
.
Because the template file is correct, WordPress knows that this is the books
post-type and fills in a lot of details for the Query.
Since we only want just the one post, a custom WP_Query()
was needed instead of just grabbing the global $wp_query
.
From archive-books.php
(on Elder Research, for reference)
$featured_args = array(
'post_type' => 'books',
'posts_per_page' => 1,
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'featured_post',
'value' => '1',
'compare' => '=',
),
),
);
$the_query = new WP_Query( $featured_args );
$featured_posts_args = array(
'post_type' => 'books',
'posts_per_page' => 3,
'offset' => 1,
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'featured_post',
'value' => '1',
'compare' => '=',
),
),
);
$the_posts_query = new WP_Query( $featured_posts_args );
?>
<div class="archiveContent-inner">
<div class="l-container featured-posts">
<div class="featured-row">
<?php if ( $the_query->have_posts() ) { ?>
<?php while ( $the_query->have_posts() ) {
$the_query->the_post(); ?>
<div class="featured-post">
<div class="featured-img">
<?php
$photoBannerId = get_field('featured_teaser_image');
$photoBannerUrlBase = wp_get_attachment_image_src($photoBannerId, 'featured-post')[0];
$photoBannerUrlBaseSmall = wp_get_attachment_image_src($photoBannerId, 'featured-post')[0];
if( $photoBannerId) { ?>
<picture>
<source media="(max-width: 768px)" srcset="<?=esc_attr($photoBannerUrlBaseSmall)?>">
<source media="(min-width: 1024px)" srcset="<?=esc_attr($photoBannerUrlBase)?>">
<source srcset="<?=esc_attr($photoBannerUrlBase)?>">
<img src="<?=esc_attr($photoBannerUrlBase)?>" alt="Photo of <?php the_title();?>">
</picture>
<?php } ?>
</div>
<div class="featured-post-content">
<h2><?php the_title();?></h2>
<div class="featured-post-description"><?php echo wp_trim_words( get_the_excerpt(), 40, '...' );?>
</div>
<a class="arrow-right" href="<?php the_permalink();?>">Read more</a>
</div>
</div>
<?php
} } else {
}
wp_reset_postdata(); ?>
</div>
<div class="featured-lower-row">
<?php if ( $the_posts_query->have_posts() ) { ?>
<?php while ( $the_posts_query->have_posts() ) {
$the_posts_query->the_post(); ?>
<div class="featured-post">
<div class="featured-img">
<?php
$photoBannerId = get_field('featured_teaser_image');
$photoBannerUrlBase = wp_get_attachment_image_src($photoBannerId, 'featured-post')[0];
$photoBannerUrlBaseSmall = wp_get_attachment_image_src($photoBannerId, 'featured-post')[0];
if( $photoBannerId) { ?>
<picture>
<source media="(max-width: 768px)" srcset="<?=esc_attr($photoBannerUrlBaseSmall)?>">
<source media="(min-width: 1024px)" srcset="<?=esc_attr($photoBannerUrlBase)?>">
<source srcset="<?=esc_attr($photoBannerUrlBase)?>">
<img src="<?=esc_attr($photoBannerUrlBase)?>" alt="Photo of <?php the_title();?>">
</picture>
<?php } ?>
</div>
<a href="<?php the_permalink();?>" class="featured-post-link"><?php the_title();?></a>
</div>
<?php
} } else {
}
wp_reset_postdata(); ?>
</div>
</div>
<div id="jump"></div>
<div id="app"></div>
</div><!-- END .archiveContent-inner -->
</div><!-- END .archiveContent -->
<?php
get_footer();
We are not re-declaring the global $wp_query
, because get_posts()
already handles all of that.
archive-books.php
<?php
$args = [
'post_type' => 'books',
'posts_per_page' => 1,
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'featured_post',
'value' => '1',
'compare' => '=',
),
),
];
$featured_posts_args = array(
'post_type' => 'books',
'posts_per_page' => 3,
'offset' => 1,
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'featured_post',
'value' => '1',
'compare' => '=',
),
),
);
$context['posts'] = Timber::get_posts( $args ); // Timber version of new WP_Query( $args )
$context['featured_posts'] = Timber::get_posts( $featured_posts_args );
Timber::render( 'archive-courses.twig', $context );
See the original Timber archive.twig template, for reference.
Notice the usage of post
here repeated, BUT because we aren't within WordPress engine, we aren't re-declaring the WordPress global. No issues!
views/archive-courses.twig
{#
Notice that we are EXTENDING index.twig here. That means we are replacing 'block content'.
Otherwise the header, footer, etc. are all still loading.
No need to make a call to get_header() or get_footer() on every single page.
#}
{% extends "index.twig" %}
{% block content %}
<div class="archiveContent-inner">
<div class="l-container featured-posts">
<div class="featured-row">
{% for post in posts %} {# Notice that we're grabbing the ['posts'] variable from $context. #}
<div class="featured-post">
<div class="featured-img">
{% set bannerImage = Image( post.featured_teaser_image ) %}
<picture>
<source media="(max-width: 768px)" srcset="{{ bannerImage.src('large')|towebp }}">
<source media="(max-width: 1024px)" srcset="{{ bannerImage.src('medium')|towebp }}">
<source srcset="{{ bannerImage.src|towebp }}">
<img src="{{ bannerImage.src }}" alt="{{ bannerImage.alt }}" title="{{ bannerImage.title }}">
</picture>
</div>
<div class="featured-post-content">
<h2>{{ post.title }}</h2>
<div class="featured-post-description">{{ post.preview.length(40).read_more('...') }}</div>
<a class="arrow-right" href="{{ post.link }}">Read more</a>
</div>
</div>
{% endfor %}
</div>
<div class="featured-lower-row">
{% for post in featured_posts %}
<div class="featured-img">
{% set bannerImage = Image( post.featured_teaser_image ) %}
<picture>
<source media="(max-width: 768px)" srcset="{{ bannerImage.src('large')|towebp }}">
<source media="(max-width: 1024px)" srcset="{{ bannerImage.src('medium')|towebp }}">
<source srcset="{{ bannerImage.src|towebp }}">
<img src="{{ bannerImage.src }}" alt="{{ bannerImage.alt }}" title="{{ bannerImage.title }}">
</picture>
</div>
<a href="{{ post.link }}" class="featured-post-link">{{ post.title }}</a>
{% endfor %}
</div>
</div>
</div>
{% endblock %}
Absolutely (and easily) doable, but there are a few odd 'gotchas' that would need to be addressed while setting it up.
Long and short, you'll want to exploit template partials and reload just the part needed.
Here's a few references, for assistance and examples:
- https://stackoverflow.com/questions/56083061/reload-twig-template-with-ajax
- timber/timber#1696 (comment)
A potential example usage:
<?php
function some_action() {
$posts = Timber::get_posts( array(
'post_type' => 'post',
'posts_per_page' => 10
) );
Timber::render( 'partials/my-template.twig', array( 'posts' => $posts ) );
die();
}