Reputation: 2632
I'm writting a CMS on the great Kohana framework where admin users can choose in the backend several kinds of pages and add them in a menu with fully customized URL.
Example of custom URL : "mmmm/about" or "aaa/bbb/release"
I have a database table where I store what should be displayed for each URL :
Example :
URL : mmm/about Pagekind : Content page ; (Page) Id : 7
URL : aaa/release Pagekind : News ; (News) Id : 1
Each page kind has its own controller (so it will be cleaner, easier to manage, easier to add new page kind) :
Examples :
>>> In /application/classes/Controller/Pages :
class Controller_Pages() extends Controller_Template {
public function action_index(){
...
}
}
>>> In /application/classes/Controller/News :
class Controller_News() extends Controller_Template {
public function action_index(){
...
}
}
I have a route like this that send everything to my menu controller :
Route::set( 'menu_path', '(<menupath>)', array('menupath' => '.*?') )
->defaults(
array(
'controller' => 'menu',
'action' => 'dispatch'
)
);
My menu controller should do the dispatch :
>>> In /application/classes/Controller/Menu :
class Controller_Menu() extends Controller_Template
{
public function action_dispatch()
{
// Get the path :
$menupath = $this->request->param('menupath');
// Get page info
$obj = ORM::factory('tablemenu')->where('URL','=',$menupath)->find();
// Regarding to page kind, call the appropriate controller
switch ($obj->Pagekind)
{
case 'Content page' :
// Call controller Page with $obj->Id as page id
// ????????
// ????????
break;
case 'News' :
// Call controller News with $obj->Id as news id
// ????????
// ????????
break;
}
}
}
So i'm stuck in the call controller part. My questions are :
- is there a way to call a controller within a controller ? (without redirection for SEO)
- if yes, is it clean ? reliable ?
- is there another way to do so ? Like overwriting the Route class ?
Thanks
Upvotes: 2
Views: 1485
Reputation: 9480
You have two options here and I'd say both could be considered clean and reliable.
Kohana allows to make internal calls, so you don't have to do any redirects to achieve what you're looking for.
class Controller_Menu() extends Controller_Template
{
public function action_dispatch()
{
// Get the path :
$menupath = $this->request->param('menupath');
// Get page info
$obj = ORM::factory('tablemenu')->where('URL','=',$menupath)->find();
// Regarding to page kind, call the appropriate controller
switch ($obj->Pagekind)
{
case 'Content page' :
$url = 'pages/action/' . $obj->Id;
break;
case 'News' :
$url = 'news/action/' . $obj->Id;
break;
}
// Override current response
$this->response = Request::factory($url)->execute();
}
}
Note that this is just a quick attempt to show you how this might work (and on its own it probably won't). Note the action
part of the url, you will want to define it at some point in order for this to work.
It's important that you actually have a route that will match the requests you're internally doing here, though.
You will also need to come up with a solution to how not to make these internal requests go via 'menu_path' Route. But since you haven't tried anything yet, I'll let you play with it and if figure it out on your own. :)
I am assuming here you are using Kohana 3.3.x for the project, as Route filters are available only from this version.
Instead of routing all requests to the menu controller you can handle the request within a Route Filter.
Your route might look like this then:
Route::set('menu_path', '(<menupath>)',
array(
'menupath' => '.*?')
)
->filter(function($route, $params, $request)
{
$menupath = $params['menupath'];
// Get page info
$obj = ORM::factory('tablemenu')->where('URL','=',$menupath)->find();
if ($obj->loaded())
{
// Regarding to page kind, call the appropriate controller
switch ($obj->Pagekind)
{
case 'Content page' :
$params['controller'] = 'Page';
$params['id'] = $obj->Id;
break;
case 'News' :
$params['controller'] = 'News';
$params['id'] = $obj->Id;
break;
}
return $params;
}
return FALSE;
})
->defaults(array(
'action' => 'index',
));
Note that there is no need to define a default controller here as you want to return 404 error if the page isn't found. We do want to define a default action though, as I can't see it defined anywhere in your example.
You may want to consider using Pagekind
value that matches Controller name, then you won't need a case
for each kind and you could simply do it with:
if ($obj->loaded())
{
$params['controller'] = $obj->Pagekind;
$params['id'] = $obj->Id;
return $params;
}
With this type of complexity in Route filters (more than just a few lines) I'd also recommend keeping it outside, as a separate callback function (as suggested by @Daan in the comments below), e.g.:
Route::set('menu_path', '(<menupath>)',
array(
'menupath' => '.*?')
)
->filter('MyRouteHelper::myfilter')
->defaults(array(
'action' => 'index',
));
Hope this helps :)
Upvotes: 6