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 (5.1) [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 ...
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.
I did manage to figure out that the following routes work. But is this the best way to set this up?
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]);
});
Basically, I would like to bind a specific route to a specific page ID when the page is created (or when the slug or parent are changed).
Am I just overcomplicating things?
Any help or direction regarding this would be greatly appreciated.
Thanks!
The way I handle this is to use two routes, one for the home page (which generally contains more complex logic like news, pick up articles, banners, etc), and a catch all for any other page.
// Home page
Route::get('/', [
'as' => 'home',
'uses' => 'PageController@index'
]);
// Catch all page controller (place at the very bottom)
Route::get('{slug}', [
'uses' => 'PageController@getPage'
])->where('slug', '([A-Za-z0-9\-\/]+)');
The important part to note in the above is the ->where()
method chained on the end of the route. This allows you to declare regex pattern matching for the route parameters. In this case I am allowing alphanumeric characters, hyphens and forward slashes for the {slug}
parameter.
This will match slugs liketest-page
test-page/sub-page
another-page/sub-page
public function index()
{
$page = Page::where('route', '/')->where('active', 1)->first();
return view($page->template)
->with('page', $page);
}
public function getPage($slug = null)
{
$page = Page::where('route', $slug)->where('active', 1);
$page = $page->firstOrFail();
return view($page->template)->with('page', $page);
}
I keep the template file information in the database, as I allow users to create templates in the content management system.
The response from the query on the database is then passed to the view where it can be output to the metadata, page, breadcrumbs, etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With