Dynamic collection routing in Statamic
When you create a new Collection in Statamic, you most likely want to define routing rules for the Entries in that Collection. Like in a Blog, you don’t necessarily want those to land at your site’s root, but maybe be prefixed by the slug of the mounting Entry.
This is all really straight forward - and the Statamic docs will tell you about the handful of routing variables available to you when configuring the Collection’s route.
But what about when you have something a bit weird to consider?
The scenario
For a client’s project, there was a complex routing requirement. Let me paint the picture:
This particular project has a health knowledgebase (called Articles) that needs to be divided up based on different Brands. And more Brands could be added over time too.
First of all, we have a Brands Collection - a simple collection that has the Name of each Brand, let’s say “Apple” and “Banana”.
We also have the Articles Collection that has all of the content for each of the Brands - and when you author an Article, you pick a Brand from the list.
Now, why wouldn’t you create a new Collection for each Brand’s content?
Well, there are other complexities for this project that make that not a possible option (we actually go another level down and group content, which has a lot of business logic with it - meaning that needs to be replicated every time a new Brand is added).
For our Entries, we want to have routing like:
/brands/apple/articles/entry-1
/brands/apple/articles/entry-2
/brands/banana/articles/entry-3
/brands/banana/articles/entry-4
But the challenge here is that the routing here includes either apple
or banana
based on the Brand that the Entry belongs to.
And because this Collection has no mount because it needs to be dynamic, we can’t use the mount
routing variable.
So we can’t use something like /brands/{mount}/articles/{slug}
because we don’t actually have a mounting entry.
One option could be to write your own custom routes and controllers to handle this - but you’re starting to replicate what Statamic is doing, and it becomes your responsibility to manage what it can (and cannot) return. In other words, you can break it.
But, the desired outcome can still be achieved - meaning Statamic’s Visit URL and Live Preview work as expected, plus caching and content protection features too.
The configuration
Statamic’s docs have a section about using fields from the current Entry when it comes to defining routing rules.
And given our Articles have a Brand selected, that means our Article Entry type knows what Brand it belongs to: meaning we can do exactly this.
To do this, we’ll work in our AppServiceProvider
as well as the Articles Collection configuration in Statamic’s CP.
Any of the Entry’s fields are available in the Routing variables - and if you had a simple field - like a Text field - you could simply refer to the field’s handle and get the value.
But this Articles Blueprint has an “Entries” fieldtype that allows the user to pick from a Brand Entry - and this is a complex fieldtype, which has an ID. We don’t want the Brand’s ID in the URL, but its slug.
To get the property value of a complex fieldtype (like a linked Entry), you can define a Computed Property for your Article Blueprint.
Our target is to have a variable called brand_slug
to use in our Routing rules.
Firstly, edit the Articles Blueprint and add a new text field, called “Brand Slug” and make it computed. You can then configure its visibility to meet your needs.
Secondly, in your \App\Providers\AppServiceProvider
(or any Service Provider you like), define your computed property:
1use Statamic\Facades\Collection; 2 3class AppServiceProvider extends ServiceProvider 4{ 5 public function boot(): void 6 { 7 Collection::computed('articles', 'brand_slug', function ($entry, $value) { 8 return $entry->brand?->slug; 9 });10 }11}
This code is saying:
for the Articles collection,
create a computed value for the field with handle
brand_slug
look at the Articles Entry, and the referenced Brand Entry, and return its slug
For “Entry 1” above, that would return apple
, and for “Entry 4”, it would return banana
.
Finally, in your Collection configuration, add the brand_slug
variable to your routing rules:
/brands/{brand_slug}/articles/{slug}
If you go to edit an Articles Entry, and click the “Visit URL” button, you’ll be correctly redirected to our desired URL.
Statamic is now routing our Articles entries based on a property within - essentially giving us dynamic routing on the Articles Collection.
How neat is that? And better yet, how easy was that?
Comments
Reply on Bluesky to join the conversation.