Creating Permalinks in SilverStripe Templates

Table of Contents

  1. 1. Hardcode the link
  2. 2. Pretend you’re using Wordpress
  3. 3. Use existing structures
  4. 4. Hook into holders
  5. 5. Make the links editable in the CMS
  6. The Verdict

One of the less documented features of SilverStripe is the best approach to creating permalinks to specific pages within templates.

For example, you may need a permalink for a standalone "contact us" link in the footer that isn't generated as part of a <% loop %>. I've tried a few different approaches, each with pros and cons, here's the skinny:

This is the templating equivalent of string literals and the worst option, but not necessarily an invalid option. The biggest issue with hardcoding a link to a page with an editable URLSegment is that it's quite possible the URL will be changed leaving the link broken.

<a href="{$BaseURL}contact-us">Contact Us</a>

(I always include {$BaseURL} as a safety net to ensure there are no path issues.)

However, the SilverStripe CMS module is smart and doesn't just leap into showing a 404. Instead it fires extension hooks before showing the error and one of these hooks looks for old pages.

This means that, as long as your hardcoded URL existed at some point, it'll continue to work.

2. Pretend you’re using Wordpress

If you're familiar with WordPress then you'll know about get_permalink(), we can do the same thing with SilverStripe.

In your controller (probably mysite/code/Page.php to make it available to all pages), you can add this:

public function get_permalink($id)
  $page = Page::get()->byID($id);
  return $page ? $page->Link() : false;

Then in your page template, call it with:

<a href="{$get_permalink(123)}">Contact Us</a>

Where 123 is the ID of the page you want to link to.

This is an all round horrible solution though, it relies on using IDs that you have to hunt down in the CMS and is unreadable unless you know every page by its ID.

The only upside is the ID will never change, unless someone deletes or unpublishes the page. That's not really an upside though, just don't do this. I've included it because I want to save you from making my mistakes.

Note: You could also use {$List(Page).byID(123).Link} if you really want to do an ID based link.

3. Use existing structures

Similar to the above but using the tools that SilverStripe provides. You can reference a page by URLSegment like this:

<% with $Page('pages-url-segment') %>
  <a href="$Link">$MenuTitle</a>
<% end with %>

Notice that I'm accessing the page as a whole using with so can also access the MenuTitle, dynamic defeats static every time.

If you just wanted to access the link, you can use the shorter form:

<a href="{$Page('pages-url-segment').Link}">Contact Us</a>

The downside is you're stuffed if the URLSegment changes and it also adds overhead in terms of a lookup.

The latter is easily rectified with partial caching (I prefer the Cache Include module to the built in option but either works fine).

4. Hook into holders

This works well for things like News sections where you're using one-off page types for holders.

You can easily reference the first instance of the holder, and assuming you'll only ever have one, you'll get away with it.

<a href="{$List('PageHolder').First.Link}">Latest News</a>

This removes any pitfalls associated with referencing a URLSegment as changing the name of a page type is a code edit rather than a CMS edit, so unlikely to happen. The obvious downside is that it breaks if you have more than one page of type "PageHolder".

As above, it does incur extra lookups so would typically be something you'd want to cache.

While not really a permalink, this is the most pleasant option as it's the hardest to break and is editable without touching templates. 

9 times out of 10 my permalink requirements are for footer menus ,so I use this great module called Menu Manager, it lets you define a custom menu and then output it wherever you feel like.

This option involves a bit more work that the others but is nearly always worth it, however there are instances when it doesn't really suit so use reason and judgement to decide.

Again, it adds overhead so caching, caching and more caching.

The Verdict

  • MenuManager where practical
  • $List() or $Page() otherwise
  • Cache it either way

Published on

14th January 2015
by Colin Richardson

Filed Under