Template Routes for Single Entry / Permalink URLs
By default ExpressionEngine’s URLs work like this https://example.com/blog/entry/url-title
, where “blog” is a template group, and “entry” is a template, which is fine. But nicer would be something like https://example.com/blog/url-title
, cause who needs that extra entry segment anyway? You could handle all of this in the blog template group’s index, but that’s inelegant, full of conditionals, hard to read, and hard to maintain.
Template Routes give us an easy to create and maintain implementation. What you’ll need is the following. A blog template group with the templates index and single-entry.
Inside of index you’ll put your blog listing, this will cover paginated archives, and categorized listings as well—URLs like https://example.com/blog/P15
and https://example.com/blog/category/cat-url-title
.
Index might look something like this:
{exp:channel:entries channel='blog' limit='25'}
<div class="blog-entry">
<h2><a href="{route='blog/single-entry' url_title='{url_title}'}">{title}</a></h2>
<p>{entry_date format='%n/%j/%Y'}</p>
{if has_categories}
<p><b>in</b>: {categories backspace='2'}<a href="{path='blog'}" title="View more in {category_name}">{category_name}</a>, {/categories}</p>
{/if}
<p>by: {author}</p>
</div>
{if no_results}
{redirect='404'}
{/if}
{/exp:channel:entries}
See that route= variable
above? It’ll come into play in a moment. Next you’ll need a single-entry template that will cover your, well, single entries. And will be reached with URLs like https://example.com/blog/url-title
.
Single Entry will look something like this:
{exp:channel:entries channel='blog' limit='1' require_entry='yes'}
<h1>{title}</h1>
<p>{entry_date format='%n/%j/%Y'}</p>
{if has_categories}
<p><b>in</b>: {categories backspace='2'}<a href="{path='blog'}" title="View more in {category_name}">{category_name}</a>, {/categories}</p>
{/if}
<p>by: {author}</p>
{blog_content}
{if no_results}
{redirect='404'}
{/if}
{/exp:channel:entries}
Now that you have those set up, you need to add your route. Go to Developer > Template Manager > Template Routes and set up a route for the template single-entry
. Give it a Route of /blog/{url_title:regex[(((?!(P\d+|category\/)).)+?)]}
select “no” for “Segments Required?” then save. The regex may look a little complex, so let’s break it down. (((?!(P\d+|category\/)).)+?)
uses a negative lookahead assertion, so the route does not match if the second URL segment is either pagination (P\d+)
or category/
. We want those URL patterns to still use the ExpressionEngine default routing to the blog/index template.
Now in your templates replace any path variables to your single-entry URLs to use the route=
variable in the first code sample. This will make sure that when clicked the visitor is sent to the right place!
That’s it! Now go forth and clean up your ExpressionEngine URLs today!
Comments 4
Nelly
Excellent article, template routes are really easy to use!
Zignature
Excellent article. It works like a charm for me.
But now I’d like to add the category group (category_url_title) to the url e.g.
https://example.com/blog/category_url_title/url_title
orhttps://example.com/blog/category/category_url_title/url_title
, preferably the former.I tried this: Template Route:
/blog/{category_url_title}/{url_title:regex[(((?!(P\d+)).)+?)]}
single-entry href:{route='blog/single-entry' category_url_title='{categories}{category_url_title}{/categories}' url_title='{url_title}'}
and: Template Route:
/blog/category/{category_url_title}/{url_title:regex[(((?!(P\d+)).)+?)]}
single-entry href:{route='blog/single-entry' category_url_title='{categories}{category_url_title}{/categories}' url_title='{url_title}'}
Neither worked :( What am I doing wrong?
Zignature
the single-entry hrefs I mentioned in my previous comment aren’t in the single-entry file but in the index file of course 😊
Zignature
Got it fixed. Added
category_group
,category
andurl_title
to theexp:channel:entries
tag in the single-entry template.