Guide 03 · Developer docs

The theme repo and the asset pipeline

Everything a developer changes on a Core dna site lives in its theme repository. This guide covers the repository and its build, printing records in templates, stylesheets and client-side code, and the parts of a site around the main content.

L1–L2
Unit 05 · Around the main content

Messages after a redirect

How the platform carries a message such as "Saved" across a redirect, the five message types, how to render them in Smarty and Twig, and which page shows a queued message.

Who this is for. Developers writing the site chrome that shows confirmations and errors after a form, a sign-in or a payment. After this page you can render every message type in both engines and plan a flow so the message appears on the page you intend.

How a message is queued

Messages are queued by controllers, not by templates; a template only reads them. The platform's Messages service offers two lifetimes:

MethodLifetime
addMessage()Shown in the current response only
addRedirectMessage()Stored for a later request: in memcached, or in a cookie named dxpmessages when the site has no memcached servers

The platform's own controllers queue the messages a visitor sees after an action. Returning from an invoice payment, for example, queues an info message.

The five types

Every message has one of five types:

TypeUse
errorThe action failed
warningThe action worked, with something to check
successThe action worked
infoSomething the visitor should know, such as the state of a payment
debugDetail for developers

The messages include in your theme repo, templates/includes/frontend/messages/all.html, decides which types a visitor sees. Render error, warning, success and info on every site, and debug only where developers want it.

Rendering the messages

_messages is the service itself, not a list. hasMessages() tests for any message, hasMessages('error') for one type, and getMessages('error') returns the messages of that type. Each message is an object with getMessage() (the text), getType(), and hasDeleteUrl() and getDeleteUrl(); printing the object itself prints its text.

Template code templates/includes/frontend/messages/all.html
{if isset($_messages) && $_messages->hasMessages()}
    <div class="site-messages" role="status" aria-live="polite">
        {foreach ['error', 'warning', 'success', 'info'] as $type}
            {foreach $_messages->getMessages($type) as $message}
                <div class="message message--{$type}">{$message->getMessage()|escape}</div>
            {/foreach}
        {/foreach}
    </div>
{/if}
{% if _messages is defined and _messages.hasMessages() %}
    <div class="site-messages" role="status" aria-live="polite">
        {% for type in ['error', 'warning', 'success', 'info'] %}
            {% for message in _messages.getMessages(type) %}
                <div class="message message--{{ type }}">{{ message.getMessage()|e }}</div>
            {% endfor %}
        {% endfor %}
    </div>
{% endif %}

Include it once, from the base template, so every full page reads the queue. The isset guard (Twig is defined) lets the include sit in any template without depending on the variable. The text is escaped in both engines in case a message repeats something a visitor entered (Escaping output). To mark up one type differently, give each type its own include:

Template code templates/includes/frontend/messages/error.html
{* included from all.html: {if $_messages->hasMessages('error')}{include './error.html'}{/if} *}
{foreach $_messages->getMessages('error') as $message}
    <div class="callout warning" role="alert"><strong>Error</strong> {$message->getMessage()|escape}</div>
{/foreach}
{# included from all.html when _messages.hasMessages('error') #}
{% for message in _messages.getMessages('error') %}
    <div class="callout warning" role="alert"><strong>Error</strong> {{ message.getMessage()|e }}</div>
{% endfor %}

Which page shows a queued message

A redirect message is kept until a page reads it. The first request that calls hasMessages() or getMessages() takes every stored message and clears the store, so the message is shown by that request and by no other. A redirect renders no template, so it leaves the store alone:

  • A chain of redirects keeps the message. An action followed by three redirects and then a page shows the message on that page.
  • The first page that reads messages shows them. In the usual flow, an action, one redirect and a full page with the messages include, that is the very next page, which is why a message is often described as lasting one redirect.
  • A page that reads without printing uses them up. If a step in the middle of a flow renders a template that calls hasMessages(), the message belongs to that step.
End the flow on a rendered page

Redirect a flow to a page that is rendered for each visitor, not served from the page cache, and that includes the messages partial. A cached page is served before any template runs, so the message is shown by the next page that is rendered. Let only that final page read messages.

Go deeper

Was this page helpful?

Your answer goes to the docs team.