Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

hightechredneckwoman's avatar

Dynamic Routing for Pages module

I am hoping that someone can help me out with dynamic routing for urls that can have multiple segments. I've been doing some searching all over the web, but nothing I find helps me out with my specific situation.

A little background ... several years ago, I developed a CMS package for custom client websites that was built on CodeIgniter. This CMS package has several modules (Pages, Blog, Calendar, Inquiries, etc). For the Pages module, I was caching the routes to a "custom routes" config file that associated the full route for the page (including parent, grandparent, etc) with the ID of the page. I did this so that I didn't have to do a database lookup to find the page to display.

I am currently working on rebuilding this CMS package using Laravel (while I'm learning Laravel). I need to figure out the routing situation before I can move on with my Pages module in the new version of the package.

I know that I can do something like ...

// routes.php
Route::get('{slug}', ['uses' => 'PageController@view']);

// PageController.php
class PageController extends Controller
{
    public function view($slug)
    {
        // do a query to get the page by the slug
        // display the view
    }
}

And this would work if I didn't allow nested pages, but I do. And I only enforce uniqueness of the slug based on the parent. So there could be more than one page with a slug of fargo ...

  • locations/fargo
  • staff/fargo

As with the package that I built using CodeIgniter, I would like to be able to avoid extra database lookups to find the correct page to display.

My initial thought was to create a config file that would have the dynamic routes like I did with the old version of the system. The routes will only change at specific times (when page is created, when slug is modified, when parent is changed), so "caching" them would work great. But I'm still new to Laravel, so I'm not sure what the best way to go about this would be.

Is it possible to pass a specific value to a controller via a route ... a value that isn't in the URL? Maybe something like ...

Route::get('about/locations/fargo', ['uses' => 'PageController@view', 'with' => ['id' => 123']]);

Or am I just stuck in a CodeIgniter mindset and over-complicating things?

Any help or direction regarding this would be greatly appreciated.

Thanks!

0 likes
10 replies
hightechredneckwoman's avatar

I was just playing around with some stuff and found out that the following routes work ...

Route::get('about/foobar', function(){
    return App::make('\App\Http\Controllers\PageController')->callAction('view', [123]);
});

Route::get('foobar', function(){
    return App::make('\App\Http\Controllers\PageController')->callAction('view', [345]);
});

But is there any way to do something like this without using a closure? Or is there a better way to do it?

uxweb's avatar

@hightechredneckwoman

If 2 or more pages share the same slug but they are in a different category, you can do something like:

Route::get('{category}/{slug}', 'PagesController@show');

Then in your PagesController's show method:

public function show($category, $slug)
{
    $page = Page::where('category', $category)->where('slug', $slug);

    return view('pages.show')->withPage($page);
}

Is this what you are trying to achieve?, maybe i get it wrong.

hightechredneckwoman's avatar

No. There are no categories involved. It is a straight up page editor. But the pages can be nested under each other.

For example ...

  • There is a Locations page that can be found at /locations
  • There is a Fargo Location page that can be found at /locations/fargo
  • There is a Staff page that can be found at /staff
  • There is a Fargo Staff page that can be found at /staff/fargo

All pages are rendered by the PageController because they are pages stored in the database. They are all in the pages table in the database and thus have different IDs. Since the Fargo Staff and Fargo Location pages both have the same slug, I can't query based on the slug alone.

I am looking for a way to tell the system that a specific route is associated with a specific page ID. That way I can do a database query for a page based on the ID rather than multiple slugs and multiple parents. Does that make any sense?

uxweb's avatar

@hightechredneckwoman

Ok, then, does it makes sense to pass the id in the route?, like:

locations/fargo/432

staff/fargo/568

Or maybe you can assign a more specific slug, something like:

locations/fargo-432

staff/fargo-568

If two or more resources have the same slug, then, what is the purpose of that slug?, slugs should be like a more meaningful way of identify a resource (instead of using a number).

hightechredneckwoman's avatar

I'm am trying to create SEO friendly URLs. But I don't want to do multiple database queries just to get the page that I want to display.

I did this when I built my CMS page using CodeIgniter. I am just trying to find the best way to do it with Laravel.

uxweb's avatar

@hightechredneckwoman

Even slugs like:

locations/fargo-1

staff/fargo-2

are SEO friendly, here we are just adding a number to make them different and be able to easily cache them and find them.

If you think that this will give you any performance issues by touching the database to search for it, Laravel has Caching facilities, so don't worry! :).

Of course, when using caching with Laravel, it will need you to provide a "cache key" to identify the cached data, then this is why having non repeating slugs important.

It is as easy as:

public function show($slug)
{
    $minutes = Config::get('cache.pages_caching_minutes');

    $page = Cache::remember($slug, $minutes, function() {
        Page::where('slug', $slug)->firstOrFail();
    });

    return view('pages.show')->withPage($page);
}

This will fetch a page from the db if it does not exists in cache, but if it exists in cache it will fetch it from there.

This is just 1 query to get a page, so i think there is no overhead :).

This is a link to the Cache documentation http://laravel.com/docs/5.1/cache.

Check it out!, it's really easy :).

hightechredneckwoman's avatar

This has gone way off course from my initial inquiry ... is there a way to associate a route with a specific page ID. I think I'm going to go with the closure route that I found works unless I find another way to do it. I'm not even considering bringing page caching into the picture until I sort out the rest of my system.

uxweb's avatar

@hightechredneckwoman

Currently, i don't know of other way of doing it, i know it can be done by a route parameter or by passing the id by the query string:

locations/123

staff/345

staff/fargo?id=123

location/fargo?id=345

The way of solving it by using the closure is good, but you'll need to manually bind all pages to routes, that will be a pain in the neck if you end up having 100+ pages.

hightechredneckwoman's avatar

My plan was to manually bind (or cache or whatever) the routes (to in a "custom routes" config file that would be pulled into the main *routes.php file) when the pages are created or edited or if the parent page is changed. It is an on-demand situation so it isn't a pain. It's what I'm doing in my current version of the package.

The page ID will not be in the route in any way, shape, or form (segment or query string).

LozovoyV's avatar

The same problem. I find one of the ways. It must be last rule:

Route::get('{path}', function($path){
    return App\Page::findpage($path);
    })->where('path', '[0-9,a-z,/]+');

Then you can define method "findpage" that explodes $path and makes single query or simply store it's value in DB and use

public function getRouteKeyName()
    {
        return 'full_path';
    }

Please or to participate in this conversation.