Skip to content

Instantly share code, notes, and snippets.

@taotiwordpress
Last active March 4, 2022 21:43
Show Gist options
  • Save taotiwordpress/156cc220e9d3eab5e854562a660c9e44 to your computer and use it in GitHub Desktop.
Save taotiwordpress/156cc220e9d3eab5e854562a660c9e44 to your computer and use it in GitHub Desktop.
Timber & TWIG - Example CPT Archive #timber #archive #cpt #cheatsheet

Timber & TWIG - Example CPT Archive

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:

The WordPress-Default Query

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.

Default Query Code AND Template file

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

The Timber-based Query

The Timber-base Code

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

The Timber-base Theming

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 %}

Timber and AJAX

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:

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();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment