4

I'm new to Craft and have created some SEO fields similar to this, and the template code appears in all templates as shown below. I don't want to be repeating all that code in every template though, so I thought I'd be better of putting it in an include template (assuming that's how best to handle this in Craft).

But I'm getting an error when I try to do this, so can anyone show me the correct way of doing this please?

Here's my starting code, where I'm repeating the SEO code in every template (see the start and end comments):

{% extends "_layout" %}

{# START OF SEO CODE #}
{% if entry.metaTitle != '' %}
  {% set title = entry.metaTitle %}
{% else %}
  {% set title = entry.title %}
{% endif %}

{% if entry.metaDescription != '' %}
  {% set description = entry.metaDescription %}
{% endif %}

{% if entry.metaKeywords != '' %}
  {% set keywords = entry.metaKeywords %}
{% endif %}
{# END OF SEO CODE #}

{% block content %}

  <article class="grid-60">

    <h1>{{ entry.title }}</h1>

    {{ entry.body }}

  </article><!-- grid-60 -->

{% endblock %}

And for completeness, I have the following in my _layout template:

{% set metaTitle = globalMetadata.metaTitle %}
{% set metaDescription = globalMetadata.metaDescription %}
{% set metaKeywords = globalMetadata.metaKeywords %}

<title>{% if title is defined %}{{ title }}{% else %}{{ globalMetadata.metaTitle }}{% endif %} | {{ siteName }}</title>
<meta name="description" content="{% if description is defined %}{{ description }}{% else %}{{ globalMetadata.metaDescription }}{% endif %}" />
<meta name="keywords" content="{% if keywords is defined %}{{ keywords }}{% else %}{{ globalMetadata.metaKeywords }}{% endif %}" />

So I've tried moving the SEO code out of the entry template, and into the following include template: _includes/seo_meta:

{# START OF SEO CODE #}
{% if entry.metaTitle != '' %}
  {% set title = entry.metaTitle %}
{% else %}
  {% set title = entry.title %}
{% endif %}

{% if entry.metaDescription != '' %}
  {% set description = entry.metaDescription %}
{% endif %}

{% if entry.metaKeywords != '' %}
  {% set keywords = entry.metaKeywords %}
{% endif %}
{# END OF SEO CODE #}

And am trying to call it from my entry template like this:

{% include "_includes/seo_meta" %}

But I'm getting the following error message, and the error is highlighting my include line:

A template that extends another one cannot have a body.

Thanks in advance for any help, Stephen.

4

That seo code will need to be in a block of its own. Create a seo block in your _layout template and then wrap your seo code in your include template with block tags of the same name.

4

What I do is to handle the metadata in a macro that I call from _layout, which is passed the _context twig global variable

In _layout.html:

{% import '_macros/_metaData' as m_metaData %}
<head>
  {{m_metaData.output(_context)}}
...

Here is the content of _macros/_metaData.html. The output macro constructs a metaData object, based on what is in the context, and the _printIt macro actually outputs the metadata.

{% macro _printIt(m) %}
  <title>{{m.title ? m.title~' | '}}{{siteName}}</title>
  {% if m.description %}
    <meta name="description" content="{{m.description}}"/>
  {% endif %}
  {% if m.keywords %}
    <meta name="keywords" content="{{m.keywords}}"/>
  {% endif %}
{% endmacro %}

{% macro output(context) %}
{% import _self as self %}
  {% if context.meta is defined %}
    {# if someone gave us a meta object, use that #}
    {% set metaData = context.meta %}
  {% elseif context.entry is defined %}
    {# if we have an entry, use that #}
    {% set metaData = {
      title: context.entry.metaTitle|default(context.entry.title),
      description: context.entry.metaDescription,
      keywords: context.entry.metaKeywords
    } %}
  {% else %}
    {# we got nothing #}
    {% set metaData = {
      title: '',
      description: '',
      keywords: ''
    } %}
  {% endif %}
  {{self._printIt(metaData)}}
{% endmacro %}

For regular templates which have an entry, I don't have to do anything in the template. If there is no entry, but there is some sensible metadata, then I can set it in the template:

{% set meta = {
  title: 'some title',
  description: 'this is the meta description',
  keywords: 'meta, keywords'
} %}

And if there is no entry, and no meta is set, I get no metadata, but no errors either.

  • This is a much more efficient way of doing it. Thanks marion – since1976 Jan 20 '15 at 2:40
3

I was speaking with Andy Harris on IM earlier, and he told me that there was no need to pass the meta data from the entry template to the layout template. Instead, he said I can just put the metadata code straight into the layout template, and it would parse for the individual entries (I had it in my head that I had to pass this information from the entry template to the layout template).

So I no longer have meta code repeated in each template, and instead, I just have the following in my _layout.html template:

{% if entry.metaTitle %}
  <title>{{ entry.metaTitle }} | {{ siteName }}</title>
{% else %}
  <title>{{ entry.title }} | {{ siteName }}</title>
{% endif %}

{% if entry.metaDescription %}
  <meta name="description" content="{{ entry.metaDescription }}" />
{% else %}
  <meta name="description" content="{{ globalMetadata.metaDescription }}" />
{% endif %}

{% if entry.metaKeywords %}
  <meta name="keywords" content="{{ entry.metaKeywords }}" />
{% else %}
  <meta name="keywords" content="{{ globalMetadata.metaKeywords }}" />
{% endif %}
2

Another little thing to consider, instead of using:

{% if entry.metaDescription != '' %}{% endif %}

which effectively means 'if this field is not equal to nothing', try using Twigs |length filter:

{% if entry.metaDescription|length %}{% endif %}

But yes to answer your question, since1976 has already said it but you will just need to wrap your SEO code (from {# START OF SEO CODE #} to {# END OF SEO CODE #}) in a block, and then add that block to the template you are including.

  • 1
    Or since an empty string evaluates to false, just test: {% if entry.metaDescription %}...{% endif %} – Marion Newlevant Jan 19 '15 at 18:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.