I'm setting up SEO fields (of which there are quite a few) using Matrix.

My plan is to offer the user the option to adjust the SEO on a page-by-page basis. This could become unwieldy and the user might not always add the SEO information. So to assist i've created a 'default' SEO entry which will always append unless the user makes changes.

So, the basic setup is "If the field has content, use it. If not, use the default"

At the moment i have the following code (just done the title field so far), but you can see that this will become a lot of {{ ifelse }}, {{ if not }}, {{ for block }}, {{ endfor }}... etc..

So my question is, can this be refined into something much less intensive?

{% for block in entry.seoSettings %}

                {% if block.type == "seoBasics" %}

                    {% if not block.seoPageTitle|length %}
                        {% for entry in craft.entries.section('defaultSeo').find() %}
                            {% for block in entry.seoSettings %}
                                <title>{{ block.seoPageTitle }}</title>
                            {% endfor %}
                        {% endfor %}
                    {% else %}
                        <title>{{ block.seoPageTitle }}</title>
                    {% endif %}

                    <meta name="description" content="{{ block.seoDescription }}" />
                    <meta name="google-site-verification" content="{{ block.googleVerificationNumber }}" />
                    <meta name="msvalidate.01" content="{{ block.bingVerificationNumber }}" />

                {% elseif block.type == "seoAdditional" %}

                    <link rel="canonical" href="{{ block.seoCanonicalLinks }}" />
                    <meta name="robots" content="{{ block.seoRobotContent }}" />

                {% elseif block.type == "seoGeographic" %}

                    <meta name="geo.region" content="{{ seoGeoRegion }}" />
                    <meta name="geo.placename" content="{{ seoGeoPlacename }}" />
                    <meta name="geo.position" content="{{ seoGeoLattitude }};{{ seoGeoLongitude }}" />
                    <meta name="ICBM" content="{{ seoGeoLattitude }}, {{ seoGeoLongitude }}" />

               {% elseif block.type == "seoOpenGraph" %}

                    <meta property="og:title" content="{{ block.ogTitle }}" />
                    <meta property="og:description" content="{{ block.ogDescription }}" />
                    <meta property="og:type" content="{{ block.ogType }}" />
                    <meta property="og:url" content="{{ block.ogUrl }}" />
                    <meta property="og:image" content="{% for image in block.ogImage %}{{ image.getUrl() }}{% endfor %}" />
                    <meta property="og:author" content="{{ block.ogAuthor }}" />
                    <meta property="og:site_name" content="{{ block.ogSiteName }}" />
                    <meta property="og:locale" content="{{ block.ogLocale }}" />
                    <meta property="og:locale:alternate" content="{{ block.ogAlternativeLocale }}" />
                    <meta property="fb:admins" content="{{ block.ogFacebookAdmin }}"/>

                {% elseif block.type == "twitterCard" %}

                    <meta name="twitter:card" content="{{ block.twitterCard }}" />
                    <meta name="twitter:title" content="{{ block.twitterTitle }}" />
                    <meta name="twitter:description" content="{{ block.twitterDescription }}" />
                    <meta name="twitter:site" content="{{ block.twitterSite }}">
                    <meta name="twitter:creator" content="{{ block.twitterCreator }}">
                    <meta name="twitter:url" content="{{ block.twitterUrl }}" />
                    <meta name="twitter:image" content="{% for image in block.twitterImage %}{{ image.getUrl() }}{% endfor %}" />

                {% elseif block.type == "googlePlus" %}

                    <meta itemscope itemtype="http://schema.org/{{ block.schemaPageType }}" />
                    <meta itemprop="name" content="{{ block.schemaName }}" />
                    <meta itemprop="description" content="{{ block.schemaDescription }}" />
                    <meta itemprop="image" content="{% for image in block.schemaImage %}{{ image.getUrl() }}{% endfor %}" />

                {% elseif block.type == "rdPartyVerification" %}

                    <meta name="{{ block.partyName }}" content="{{ block.partyContent }}"/>

                {% endif %}

              {% endfor %}

I hope that makes sense?

Many thanks


2 Answers 2


We do something similar to the setup you have here, whereby if the SEO fields haven't been filled for an entry the default is used, but there is a cleaner way of doing this, especially for your clients when they want to go in and make changes.

Globals would be a perfect fit for what you are after. In your Global settings, create a new one called 'SEO' or similar, and in there create your fields for the various SEO attributes.

Globals allow you to define content and information which can be accessed from anywhere on the site, and by doing this would mean you will no longer need the DefaultSEO entry you have created.

All of the default information you have set in the Default SEO entry can instead be set in the Globals section, which makes for a cleaner experience for you clients.

I think with the very rich SEO set up you have here, you can't really get round having the if statements in your templates, as this is how matrix fields work.

But as I say, you can really tidy things up by using Globals instead:

{% if block.type == "seoBasics" %} 

    {# If a page title has been set for this entry, use that #}
    {% if block.seoPageTitle | length %}
        <title>{{ block.seoPageTitle }}</title>

    {# If it hasn't, use the default page title from the SEO Global #}
    {% else %}
        <title>{{ seo.seoPageTitle }}</title>

    {% endif %}

I hope this helps you in the right direction, if you have any questions then let me know.

  • Thanks Jamie, before I give this a shot can you confirm the information can be changed on a page-by-page (entry-by-entry) basis? The way I read it, I set up a Global with the information which a client can alter, but this will still apply to every page (entry)?
    – Martin
    Nov 3, 2014 at 11:27
  • Yes, you will still have the SEO fields on each entry. The improvement I suggested was to remove the DefaultSEO entry you have set up, and instead create an SEO Global where you set your default SEO information. The template code will check to see if SEO information has been set for the entry. If it has, it will use that. If it hasn't, it will grab the info you have set in the Global section.
    – Jamie Wade
    Nov 3, 2014 at 11:29
  • Hi Jamie, i've looked at using this method and can't get the 'default' content from the Global to show: {% if block.type == "seoBasics" %} {# If a page title has been set for this entry, use that #} {% if block.seoPageTitle | length %} <title>{{ block.seoPageTitle }}</title> {# If it hasn't, use the default page title from the SEO Global #} {% else %} <title>{{ seoSetup.seoPageTitle }}</title> {% endif %} Where seoSetup is the handle for my Global setup. Any ideas? Thanks How the hell do I show code in a Stack Exchange comment?! :/
    – Martin
    Nov 3, 2014 at 15:46
  • I've got it... I still need to wrap {% for block in seoSetup.seoSettings %} around {{ block.seoPageTitle }}
    – Martin
    Nov 3, 2014 at 16:03
  • You got it, sorry for the confusion. I started my code example from inside the for loop.
    – Jamie Wade
    Nov 3, 2014 at 16:07

Thanks to the outstanding support from Pixel and Tonic I can answer this with code supplied by Brad at P&T. Also thanks to Jamie for looking into this.

{% for block in entry.seoInfo %}
    {% if block.type == "seoBasics" %}

        {% if block.seoPageTitle | length %}
            <title>{{ block.seoPageTitle }}</title>
        {% else %}
            {% for block in seoSettings.seoInfo.type('seoBasics') %}
                <title>{{ block.seoPageTitle }}</title>
            {% endfor %}
        {% endif %}
    {% endif %}
{% endfor %}

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.