Real World Craft CMS

Coding a Twig Entry Template

We walk through how to create an entry template in Twig that support a content builder field using Matrix and switch statements.

With our home­page cod­ed, and the tes­ti­mo­ni­als work com­plete, we’re ready to tack­le the adven­tures sin­gle entry tem­plate. This is the tem­plate that will show an indi­vid­ual adven­ture with the con­tent, stats, and images.

Going back to the con­trol pan­el, let’s refresh our mem­o­ries on what we’ve built. We have the fol­low­ing fields:

  • title
  • descrip­tion
  • a Matrix-pow­ered con­tent builder
  • images

The con­tent builder is made up of a rich text field (via the Redac­tor plu­g­in), a CTA block with a text and URL field, and a stats block, pow­ered by table field with a name and value.

Let’s first set up our extend­ed lay­out tem­plate in the _entry.twig template.

{#  Adventures Entry Template #}  
{% extends '_layouts/_wrapper.twig' %}  

{% block main %}  
{% endblock %}

The next step is to grab the sta­t­ic tem­plate you down­loaded as part of this course. Look for the tem­plate called adventure-entry.html.

Grab every­thing between and includ­ing the main ele­ment and paste it in between the main block in the _entry.twig template.

Okay, now we’re ready to make the sta­t­ic con­tent dynamic!

Since we’re in a sin­gle entry view, and Craft knows that this tem­plate is the entry view tem­plate for the Adven­tures sec­tion per our section’s set­tings, we auto­mat­i­cal­ly access the entry object with all of the entry data. Craft knows this based on the URI request­ed by the browser.

Just like with Sin­gles, we don’t have to query for the entry data; we just have it. Very handy! That means we can access the adven­ture con­tent using entry and then a prop­er­ty for the field handle.

We’ll start at the top with the title. 

<h1  
        class="mt-2 text-3xl leading-8 font-extrabold tracking-tight text-gray-900 sm:text-4xl"  
>  
    {{ entry.title }}  
</h1>

And then the adven­ture description:

<div class="text-base max-w-prose mx-auto lg:max-w-none">  
    <p class="text-lg text-gray-500">  
        {{ entry.description }}  
    </p>  
</div>

Those are the easy ones. Let’s tack­le the con­tent builder now, which is pow­ered by a Matrix field.

The first thing we want to do is iden­ti­fy the markup for each of our dif­fer­ent block, and then label that markup using Twig com­ments. This will make it eas­i­er to code the Matrix field in Twig since we’ll already know which markup pow­ers which block.

Then we can query for our Matrix field and iter­ate over the blocks. We’ll use a switch state­ment to do this because it’s sim­pler than using con­di­tion­al to check for the block type. We’ll switch the block type and out­put the cor­rect markup and Twig for that block.

First, let’s query for our Matrix field:

{% for block in entry.contentBuilder.all() %}  
    {% switch block.type %}
    
	{% endswitch %}
{% endfor %}

Start­ing with the stats block…

{# Stats #}  
    {% case 'stats' %}  
    <div class="mt-10">  
        <dl class="grid grid-cols-2 gap-x-4 gap-y-8">  
            {% for stat in block.stat %}  
            <div class="border-t-2 border-gray-100 pt-6">  
                <dt class="text-base font-medium text-gray-500">{{ stat.name }}</dt>  
                <dd class="text-3xl font-extrabold tracking-tight text-gray-900">{{ stat.value }}</dd>  
            </div>            {% endfor %}  
        </dl>  
    </div>{# Stats End #}

And then the rich text block:

{# Rich Text #}  
    {% case 'richText' %}  
    <div  
    class="mt-5 prose prose-indigo text-gray-500 mx-auto lg:max-w-none lg:row-start-1 lg:col-start-1">  
        {{ block.text }}  
    </div>  
{# Rich Text End #}

And then the CTA:

{# Call to Action #}  
    {% case 'cta' %}  
    <div class="mt-10">  
        <a href="{{ block.linkUrl }}" class="text-base font-medium text-primary">{{ block.linkText}} <span aria-hidden="true">&rarr;</span> </a>  
    </div>{# Call to Action End #}

Next up, is the images along the right side of the page in the desk­top view. 

{# Images #}  
{% set images = entry.images.all() %}  
<div class="relative lg:row-start-1 lg:col-start-2">  
    <div class="relative text-base mx-auto max-w-prose lg:max-w-none">  
        {% for image in images %}  
        <figure class="mb-8">  
            <div class="aspect-w-12 aspect-h-7 lg:aspect-none">  
                <img                        class="shadow-lg object-cover object-center lg:rotate-1 border-gray-200 border-8"  
                        src="{{ image.url }}"  
                        alt="{{ image.title }}"  
                        width="1184"  
                        height="1376"  
                />  
            </div>        
        </figure>        
	    {% endfor %}  
    </div>  
</div>  
{# End Images #}

We also have the loca­tions list­ing at the bot­tom. Let’s abstract that out into a shared tem­plate so we can use the same code we cre­at­ed for the home­page on any tem­plates that need to have the loca­tions listing. 

First, we’ll cre­ate a new direc­to­ry called _shared, and inside of it a new tem­plate named _locations.twig.

Let’s grab the loca­tions code, from the section ele­ment wrap­ping that out­put, from the home­page and place it on the new template.

We have to include the new shared tem­plate in the home­page index.twig tem­plate so the loca­tions still appear as expected.

{% include '_shared/_locations.twig' %}

And, then we’ll do the same on the adven­tures entry tem­plate, replac­ing the sta­t­ic markup and con­tent with the include tag.

Real World Craft CMS is made up of the following videos: