{{craftSnippets}}
Home Articles Plugins Starter theme Components on Github Gists About
  • Home
  • Articles
  • Fetching routes into Twig templates in Craft CMS
Posted on Feb 22, 2019 by Piotr Pogorzelski

Fetching routes into Twig templates in Craft CMS

URL macro on github gists

Notification

Some time after writing this article, I released Named Routes plugin which handles functionality described here.
I recommend using this plugin instead of method described in this article.

Most Craft developers hardcode routes structure within links created in Twig templates. There is however a way to fetch routes directly into templates and avoid creating duplicate code.
Table of contents:
  • What's it all about?
  • Accessing routes from within Twig templates
  • Macro to the rescue
  • Inspiration

This article deals only with routes defined in config/routes.php file - also called URL rules. It is also written with the assumption that you have basic understanding of Craft Cms routing functionality. You can learn more about routes in Craft Cms documentation.

What's it all about? #

One of the most common applications of routing functionality in Craft Cms is creating a tag page. Let's consider such situation.

First, we need to define route in config/routes.php file:

<?php
return [
    tag-list/<slug>' => ['template' => '_pages/single_tag'],`
];

Now, we need to create link to tag page inside Twig template. In example below we are looping trough tags assigned to some entry:

{% for singletag in entry.tags.all() %}
<a href="tag-list/{{singletag.slug}}">{{singletag.title}}</a>`
{% endfor %}

Do you already see the problem? tag-list keyword is hardcoded inside routes.php, but also in Twig file. What if we create ten such links within various fragments of our twig templates? And then we need to change tag-list fragment of route to - for example, tags? In such situation, we would need to change it in 11 places - one time inside routes.php and ten times within Twig templates. This is in direct contradiction of DRY (dont repeat yourself) rule - route URL structure should be defined only in one place.

Accessing routes from within Twig templates #

Craft CMS exposes most of its core functionality within craft.app Twig variable. Let's use dump to inspect our routes, using craft.app.routes.getConfigFileRoutes method:

<pre>{{ dump(craft.app.routes.getConfigFileRoutes) }}</pre>
array(1) {
  ["tag-list/<slug>"]=>
  array(1) {
    ["template"]=>
    string(28) "_pages/single_tag"
  }
}

As we can see, route URL structure IS available for us inside Twig templates. But array returned by getConfigFileRoutes is numeric - do we need to remember numeric index of each route? Let's modify routes.php file a little, by giving each route name attribute.

<?php
return [
    'tag-list/<slug>' => ['template' => '_pages/single_tag', 'name' => 'tag'],
];

Now, we can loop through array of routes and find the one we need - route that has name attribute with value of tag.


{% set routeName == 'tag' %}

{% set keyword = 'name' %}
{% set routeString = null %}
{% for route, routeSettings in craft.app.routes.getConfigFileRoutes %}
    {% if routeSettings[keyword] is defined and routeSettings[keyword] == routeName %}
    {% set routeString = route %}
    {% endif %}
{% endfor %}

Twig code above will assign url structure of route to variable - in our example this structure is tags/<slug>. Now, we just need to replace <slug> token with actual slug of specific tag we are linking to. In order to do that, we need to divide route structure into an array of segments using split filter, use regular expression to find segment with token and replace it.

{% set token = singletag.slug %}
{% set routes = routeString|split('/') %}
{% set resultArray = [] %}
{% for value in routes %}
    {% if value matches '/\\<[\\w]+\\>/' %}
            {% set value = token %} 
    {% endif %}
    {% set resultArray = resultArray|merge([value]) %}
{% endfor %}
{% set result = resultArray|join('/') %}

And there we have it - variable result contains ready to use route url! We can use it inside href attribute of link. For tag with slug 'super-blog-tag', variable result will contain 'tag-list/super-blog-tag'.

Now we just have pass route url to url function - it will prepent it with base url of current site.

{{url(result)}}

Macro to the rescue #

Of course we don't need to paste all this code into template each time we want to use it. When dealing with repetitive fragments of Twig code, its always worth to encapsule it within macro.

{% macro url(routeName, tokens) %}
{% spaceless %}
{% set keyword = 'name' %}
{% set routeString = null %}
{% for route, routeSettings in craft.app.routes.getConfigFileRoutes %}
    {% if routeSettings[keyword] is defined and routeSettings[keyword] == routeName %}
    {% set routeString = route %}
    {% endif %}
{% endfor %}
{% if routeString and tokens is not empty %}
    {% set routes = routeString|split('/') %}
    {% set tokenIndex = 0 %}
    {% set resultArray = [] %}
    {% for value in routes %}
        {% if value matches '/\\<[\\w]+\\>/' %}
            {% if tokens is iterable %}
                {% set value = tokens[tokenIndex] %} 
                {% set tokenIndex = tokenIndex + 1 %}
            {% else %}
                {% set value = tokens %} 
            {% endif %}
        {% endif %}
        {% set resultArray = resultArray|merge([value]) %}
    {% endfor %}
    {% set result = resultArray|join('/') %}
    {{url(result)}}
{% endif %}
{% endspaceless %}
{% endmacro %}

This macro accepts two parameters - routeName and tokens. routeName is string identyfying our route within config/routes.php. tokens can be either string - when your route contains single token, or numeric array, if it contasins multiple tokens.

This is how such macro would be used in practice - assuming that singletag object is avaible:

{% import 'macros' as m %}
<a href="{{m.url('tag', singletag.slug)}}">{{singletag.title}}</a>

Inspiration #

Those of you that worked with Django before, might have noticed this macro's similarity to Django url template tag. This is no coincidence - I wanted to replicate same functionality with Craft CMS.

I hope that some day Craft devs will add such functionality to Craft - and if you also would like that to happen, you can upvote THIS github issue.


TAGS:
#routing #macro
If you want to get latest updates on Craft CMS tutorials and components, follow me on Twitter or subscribe to RSS feed.
Quick links for this article:
URL macro on github gists
Articles on blog:
  • Frontend testing for Craft CMS websites with Codeception and Cypress
  • Building reactive Craft Commerce product page with Sprig plugin
  • Dynamically generated PDF attachments for Freeform submissions
  • Using template hooks in Craft CMS
  • Alpine JS modal component for Craft CMS
  • Using template UI elements to extend Craft CMS control panel
  • Matrix within a Matrix - possible solutions for Craft CMS
  • Universal email template for Craft CMS
  • Creating attributes table from entry fields in Craft CMS
  • Namespacing forms in Craft CMS
  • Creating map-based navigation for Craft CMS
  • Placeholder image macro for Craft CMS
  • Building AJAX contact form with Craft CMS
  • Using incognito field plugin for Craft CMS
  • Email footer creator made with Craft CMS
  • Infinite scrolling and lazy loading with Craft CMS
  • Using Javascript in Twig templates with Craft CMS
  • Twig templating tips and tricks for Craft CMS
  • Basic SEO functionality for Craft CMS
  • Working with dates in Craft CMS templates
  • Working with SVG images in Craft CMS templates
  • Responsive and lazy-loaded youtube videos with Craft CMS
  • Debugging and inspecting Twig templates in Craft CMS
  • Creating article excerpts with Twig component in Craft CMS
  • Adding favicons to Craft CMS website
  • Truncating text with Twig macros in Craft CMS
  • Universal language switcher for Craft CMS
  • Read time macro for Craft CMS
  • Using attr() function to render HTML attributes in Craft CMS
  • Building dynamic, AJAX based pagination for Craft CMS
  • How to add Disqus comments to Craft CMS website
  • Ellipsis pagination component for Craft CMS
  • Converting email addresses into links using Twig macro
  • Breadcrumb created from URL for Craft CMS
  • Best developer-oriented Craft CMS plugins
  • Search autocomplete component for Craft CMS
  • RSS feed - template component for Craft CMS
  • Testing emails sent by Craft CMS using Mailtrap
  • Quick edit link - Twig component for Craft CMS
  • Filtering entries in control panel using Searchit plugin
  • Fetching routes into Twig templates in Craft CMS


Frontend testing for Craft CMS websites with Codeception and Cypress

Building reactive Craft Commerce product page with Sprig plugin

Dynamically generated PDF attachments for Freeform submissions

Using template hooks in Craft CMS

Alpine JS modal component for Craft CMS

Using template UI elements to extend Craft CMS control panel

Matrix within a Matrix - possible solutions for Craft CMS

Universal email template for Craft CMS

Copyright ©2023 Piotr Pogorzelski