DataObjects as Pages URLSegment

<%- if @topic_view.topic.tags.present? %>
<%= t 'js.tagging.tags' %>: <%- @topic_view.topic.tags.each do |t| %> <%= t %> <%- end %>
<% end %>

Is there a tidy way to implement the SS lesson regarding DataObjects as pages to include a SEO friendly URL rather than a page ID. I have done this before in version 3.x but as of 4.x it not longer seems to work.

The lesson I am referring to is here:

It seems easy enough to generate the link on the DataObjects by changing

    public function Link()
        return $this->RegionsPage()->Link('show/'.$this->ID);


    public function Link()
        return $this->RegionsPage()->Link('show/'.$this->Title); //or URLSegment or another field

but how would I modify the controller to accept the URLSegment or Title or whatever

public function show(HTTPRequest $request)
        $region = Region::get()->byID($request->param('ID'));

        if(!$region) {
            return $this->httpError(404,'That region could not be found');

        return [
            'Region' => $region,
            'Title' => $region->Title,

Thanks for any assistance.

Hey TimDK,

you need to set a the allowed_actions and an url-handler.

Once you checked the links from the docs, it will be clear :slight_smile:
Do not forget to flush!

Hi there!

I don’t know why you want to pass the title and not the unique ID. Such a title can exist multiple times, but not an ID!

But if you want to test it, you can try the following:
In the show function you can of course not search for the ID in the region if you transfer the title.

Your SQL query currently looks something like this:

SELECT * from Region WHERE ID = "<Title>"; 

It cannot work.

It should look something like this: (untested!)

$region=Region::get()->filter (['Title' => '$ request-> param (' ID ')');

However, you now have the problem that you can also get more like a data set. Try that with an additional "->first()", then you will always get the first record.

Greetings RALF

@Ralf is right I think, just a typo on the quoting. I would use URLSegment like this:

$region = Region::get()->filter('URLSegment', $request->param('ID'))->first();

And ensure the URL Segment is unique for each object. Here is an old piece of code from SS3, perhaps you can modify it for SS4.

p.s. if you don’t want to have ‘/show’ in the URL, you should be able to omit that by adding:

private static $url_handlers = [
	'$ID!' => 'show',


Thanks so much for your replies! I will give it a crack today and let yo know how I get on.


Hi TimDK,

There exists this module I wrote to do exactly what you’re asking for :slight_smile:

It basically does what everyone has described above, but in a simple to apply manner.
The readme should hopefully explain the usage in an easy to understand manner, but I always welcome feedback if you have trouble!

It was originally written in as a response to a very similar question in the past, which I then ‘modularised’. Recently I updated it to be Silverstripe CMS 4 compatible, but it should be well documented in the code to continue helping people wanting to learn about this functionality as you are.

It does have a few extra features though such as avoiding the conflict problem RALF described above.


Thanks Nightjar I will have a good look at you module.

Hi Nightjar,

I have a couple of Qs about your module. I am running SS4.5.1

When I install the module and setup a test item then dev/build it get the following error:

I remove changed “ParentID” to “Parent” and the build works.

The dataobject is crearing the correct slug:

But when I click the link i get the following error:

Any insight into this would be appreciated.


Ok, by looking at the examples in the module I have fixed the first problem, but I still get the array error when i go to the detail page.

Hi Tim,

Great you managed to solve the first problem!
The second issue you’re having appears to be because you don’t have any slug_trails defined on the Controller or DataObject being slugged.

You need to either define this config setting, or pass a parameter to the extension application similarly to how Slug does - e.g. SlugHandler::class . "('MyItems')"

Hopefully the configuration section of the readme can help :slight_smile: - although I realise the examples are missing this configuration option now looking at them again :confused:

The issue you’re experiencing is described in Have SlugHandler look up slugs automatically · Issue #8 · NightJar/ssrigging-slug · GitHub

In the past I’ve used a SiteTree method to create the segment:

$this->Permalink = SiteTree::create()->generateURLSegment($this->Title);