Simplifying Data Definition

One of the great features of Perch templates is that you can use them to define the data schema of an item (e.g. collection item, content region, etc) as well as render the template to output the content on a page. For example if you have a content region for a link, the template may look like this:

<a href="<perch:content id="link" type="text" label="Link URL" required>">
    <perch:content id="link_text" type="text" label="Link Text" required>
</a>

In the above template we define the data schema with 2 Perch tags:

<perch:content id="link" type="text" label="Link URL" required>
<perch:content id="link_text" type="text" label="Link Text" required>

We want a field for the link URL and a field for the link text. The same template is also responsible for outputting the HTML markup. This way we have only use a single file for managing both the data definition (and edit form in the control panel) and the display template which we use to output the content to the page.

This is perfect in some cases, but can lead to hard-to-maintain templates when complexity grows and when we are dealing with stand-alone content.

Stand-alone content

Stand-alone content is content that its own entity like a blog post, a service or a product regardless of whether it is managed as a Runway Collection item, an item in a multiple-item content region or in a Perch app. This is the sort of item you would typically display with multiple templates in multiple places. When you define the data schema for this type of content, you may add fields that you are not necessarily going to output (e.g. a status field).

The easiest way to keep your templates maintainable in this context is to have a separate template for your data definition (and edit form). If you have a Collection for a blog, your edit template can be something like this:

<perch:content id="title" type="text" label="Title" size="xl autowidth" title required>
<perch:content id="slug" type="slug" for="title">
<perch:content id="excerpt" type="textarea" label="Excerpt" size="s" no-index>
<perch:content id="image" type="image" label="Image" no-index>
<perch:content id="image_alt" type="text" label="Image alt text" no-index>

<perch:group label="Publishing">
    <perch:content id="dateTime" type="date" label="Date" time>
    <perch:content id="status" type="select" label="Status" options="Draft|draft,Published|published">
</perch:group>

<perch:group label="Associations">
    <perch:categories id="categories" set="blog" label="Categories" />
    <perch:related id="author" collection="Authors" label="Author" max="1"></perch:related>
</perch:group>

<perch:group label="SEO" collapse>
    <perch:content id="meta_title" label="Meta title" type="text" help="Title for this document with no branding or site name" no-index>"
    <perch:content id="meta_desc" label="Meta description" type="textarea" size="s" no-index>
    <perch:content id="og_image" label="Image when shared" help="Should be at least 1200x630" type="image" width="1200" no-index>
    <perch:content id="og_type" label="Facebook type" type="select" options="article,book,profile,website,video,music" allowempty no-index>
</perch:group>

<perch:content id="post_body" type="textarea" label="Post" editor="simplemde" markdown required divider-before="Post">

I look at the above template and I immediately know what fields I’ll find in the edit form, in what order, how they will be displayed and which fields are set to be indexed. Compare that to the default Perch Blog app post.html template.

You don’t have to worry about preventing the output of some fields with the suppress attribute because you’re never going to use this template to output the item. You don’t need to worry about setting the order of fields with the order attribute because you’re not worrying about the fields order in multiple contexts (edit form and display template). You don’t have to read through HTML code to figure out what fields the template defines. You don’t need to worry about hiding fields you are passing at runtime with type="hidden". It is a big productivity boost to not have to worry about all of this.

The bonus here is on display templates you don’t need to worry about all the edit-form-specific tag attributes such as label, required, divider-before, help, no-index and allowempty. This also makes the display templates more readable as you don’t need to have to read through attributes that are meaningless to the display template context. Instead on display templates you may want to use a bunch of other tag attributes that actually affects how the field is output such as format, words, append and filter.

Let’s look at the excerpt field from above:

<perch:content id="excerpt" type="textarea" label="Excerpt" size="s" no-index>

It has 3 attributes that has no effect on the outptut: label, size and no-index. So when we output this field we can omit them:

<p>
    <perch:content id="excerpt" type="textarea">
</p>

And in another context you may add additional attributes that affect how the field is output:

<meta name="description" content="<perch:content id="excerpt" type="textarea" chars="150" append="..." striptags escape>" />

Content regions

In a content region you may also have complex templates filled with lots of fields and a lot of HTML code. We certainly want to avoid having a template that is hard to maintain. However, When it comes to content regions, in many cases it is desirable to only use a single template to take advantage of caching.

Let’s look at how we’d create and output a region:

// create a region with the template `templates/content/slider.html`
perch_content_create('Slider', ['template' => 'slider.html']);

// output the region
perch_content('Slider');

When we output the region with perch_content() like above, we output the cached pre-rendered content. Perch evaluates the template when you save the region in the control panel and cache this. This way Perch does not have to render the same template every time someone visits the page.

We can output the same region with another template using perch_content_custom():

perch_content_custom('Slider', ['template' => 'another_template.html']);

Though it is nice to get the performance boost of the cached pre-rendered template when possible. Having said that, there are cases where using perch_content_custom() is exactly what you need, but that’s another topic.

Going back to simplifying the content region templates. Now we know why we want to keep everything in a single template in this case. What I like to do instead is define the fields on top and prevent their output with the suppress attribute, and below add my HTML code and use the Perch tags how I need them:

<perch:content id="heading" type="text" label="Section Heading" required suppress>
<perch:content id="desc" type="textarea" label="Section Description" help="The description is output as a single paragraph" required suppress>
<!--**-->
<perch:repeater id="slides" label="Slides">
    <perch:content id="image" type="image" label="Image" suppress>
    <perch:content id="image_alt" type="text" label="Image alt text" suppress>
</perch:repeater>
<!--**-->

<h2><perch:content id="heading"></h2>
<p><perch:content id="desc" type="textarea"></p>

<perch:repeater id="slides">
    <perch:before>
        <div class="slider">
    </perch:before>

        <div class="slide">
            <figure>
                <img src="<perch:content id="image" type="image">" alt="<perch:content id="image_alt">">
            </figure>            
        </div>

    <perch:after>
        </div>
    </perch:after>
</perch:repeater>

While the above is not a very complex template, hopefully you can see how this approach would make it easier to work with truly complex templates.

You can take this further and use template includes if you really want to manage the field definitions and the HTML code in separate files, but note that Perch evaluates them as a single template in this case so you still need to use the suppress attribute when defining fields. I personally don’t bother spliting them into 2 files.

link

Related articles