2

我正在编写一个很棒的 Kohana 框架上的 CMS,管理员用户可以在其中选择后端几种页面,并将它们添加到具有完全自定义 URL 的菜单中。

Example of custom URL : "mmmm/about" or "aaa/bbb/release"

我有一个数据库表,其中存储了每个 URL 应该显示的内容:

Example : 
URL : mmm/about     Pagekind : Content page ; (Page) Id : 7
URL : aaa/release   Pagekind : News         ; (News) Id : 1

每个页面类型都有自己的控制器(因此它会更干净、更易于管理、更容易添加新页面类型):

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(){
        ...
    }
}

我有这样一条路线,可以将所有内容发送到我的菜单控制器:

Route::set( 'menu_path', '(<menupath>)', array('menupath' => '.*?') )
->defaults(
    array(
        'controller' => 'menu',
        'action' => '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;

        }

    }
}

所以我被困在呼叫控制器部分。我的问题是:
- 有没有办法在控制器中调用控制器?(没有针对 SEO 的重定向)
- 如果是,它是否干净?可靠的 ?
- 还有其他方法吗?喜欢覆盖 Route 类?

谢谢

4

1 回答 1

6

你在这里有两个选择,我想说两者都可以被认为是干净和可靠的。

1. 内部请求

Kohana 允许进行内部调用,因此您无需进行任何重定向即可实现您正在寻找的内容。

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();
    }
}

请注意,这只是向您展示它如何工作的快速尝试(并且它本身可能不会)。请注意actionurl 的一部分,您需要在某个时候定义它以使其正常工作。不过,重要的是您实际上有一条路线来匹配您在此处内部执行的请求。您还需要想出一个解决方案来解决如何不让这些内部请求通过“menu_path”路由。但既然你还没有尝试过任何东西,我会让你玩它,如果你自己想办法。:)

2. 路由过滤器(推荐)

我假设您在此项目中使用的是 Kohana 3.3.x,因为路由过滤器仅在此版本中可用。

您可以在Route Filter中处理请求,而不是将所有请求路由到菜单控制器。

您的路线可能如下所示:

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',
    ));

请注意,这里不需要定义默认控制器,因为如果找不到页面,您希望返回 404 错误。不过,我们确实想定义一个默认操作,因为我在您的示例中的任何地方都看不到它定义。

您可能需要考虑使用Pagekind与控制器名称匹配的值,然后您不需要case为每种类型使用一个,您可以简单地使用:

if ($obj->loaded())
{
    $params['controller'] = $obj->Pagekind;
    $params['id'] = $obj->Id;

    return $params;
}

由于 Route 过滤器中的这种复杂性(不仅仅是几行),我还建议将其作为单独的回调函数保留在外部(正如@Daan 在下面的评论中所建议的那样),例如:

Route::set('menu_path', '(<menupath>)',
    array(
        'menupath' => '.*?')
    )
    ->filter('MyRouteHelper::myfilter')
    ->defaults(array(
        'action'    => 'index',
    ));

希望这可以帮助 :)

于 2013-03-26T14:03:07.877 回答