在CakePHP中为每个URL添加前缀

20

如何在CakePHP中以最清晰的方式添加前缀到每个URL,比如一种语言参数?

http://example.com/en/controller/action
http://example.com/ru/admin/controller/action

它需要与像admin这样的“真实”前缀一起工作,并且理想情况下,裸的URL /controller/action可以重定向到/DEFAULT-LANGUAGE/controller/action

现在对于我来说在一个改装后的应用程序中运行良好,但它有点像黑客一样,我需要手动在大多数链接中包含语言参数,这不好。

因此,问题有两个方面:

  • 最佳方法是什么,使得语言参数默认情况下被隐式地包括而不必为每个新定义的路由指定?
    • Router::connect('/:controller/:action/*', ...) 应该隐式包括前缀。
    • 该参数应该可在类似 $this->params['lang'] 或其他类似位置中使用,并在 AppController::beforeFilter() 中进行评估。
  • 如果未明确指定,如何获取Router::url()自动包括前缀在URL中?
    • Router::url(array('controller' => 'foo', 'action' => 'bar')) 应返回/en/foo/bar
    • 由于Controller::redirect()、Form::create()甚至直接需要具有相同行为的Router::url(),因此覆盖每个函数并不是真正的选项。但是,例如,Html::image()应产生没有前缀的URL。

以下方法似乎会调用Router::url

  • Controller::redirect
  • Controller::flash
  • Dispatcher::__extractParams通过Object::requestAction
  • Helper::url
  • JsHelper::load_
  • JsHelper::redirect_
  • View::uuid,但仅用于哈希生成

从中看来,控制器和助手方法需要被覆盖,我可以在AppController或者可能只是bootstrap.php中编写一个通用函数来处理参数插入。覆盖的控制器和助手方法将使用此功能,如果我想手动调用Router::url,则我也会使用此功能。这足够吗?

3个回答

14

以下是我最终用来解决这个问题的所有代码(至少我认为那就是全部;-):

/config/bootstrap.php

define('DEFAULT_LANGUAGE', 'jpn');

if (!function_exists('router_url_language')) {
    function router_url_language($url) {
        if ($lang = Configure::read('Config.language')) {
            if (is_array($url)) {
                if (!isset($url['language'])) {
                    $url['language'] = $lang;
                }
                if ($url['language'] == DEFAULT_LANGUAGE) {
                    unset($url['language']);
                }
            } else if ($url == '/' && $lang !== DEFAULT_LANGUAGE) {
                $url.= $lang;
            }
        }

        return $url;
    }
}

/config/core.php

Configure::write('Config.language', 'jpn');

/app_helper.php

class AppHelper extends Helper {

    public function url($url = null, $full = false) {
        return parent::url(router_url_language($url), $full);
    }

}

/app_controller.php

class AppController extends Controller {

    public function beforeFilter() {
        if (isset($this->params['language'])) {
            Configure::write('Config.language', $this->params['language']);
        }
    }

    public function redirect($url, $status = null, $exit = true) {
        parent::redirect(router_url_language($url), $status, $exit);
    }

    public function flash($message, $url, $pause = 1) {
        parent::flash($message, router_url_language($url), $pause);
    }

}

/config/routes.php

Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
Router::connect('/:language/', array('controller' => 'pages', 'action' => 'display', 'home'), array('language' => '[a-z]{3}'));
Router::connect('/:language/pages/*', array('controller' => 'pages', 'action' => 'display'), array('language' => '[a-z]{3}'));
Router::connect('/:language/:controller/:action/*', array(), array('language' => '[a-z]{3}'));

这允许使用默认语言(在我的情况下为JPN)的默认URL,例如/controller/action,以及使用另一种语言的URL,例如/eng/controller/action。此逻辑可以在router_url_language()函数中很容易地更改。

为了使其正常工作,我还需要为每个路由定义两个路由,一个包含/:language/参数,另一个不包含。至少我没想到其他方法来做到这一点。


2
@gacrux 注意,在Cake 1.3中,这应该会简单得多,因为它包括可自定义的前缀。上述内容适用于Cake 1.2,它只知道一个前缀。 :) - deceze

5

根据评论,覆盖AppController :: url可以为控制器完成此操作。到目前为止还不错,还有其他需要注意的吗?其他部分是否直接调用Router :: url - deceze

0

一个更简单的方法可能是将选择的语言存储在cookie中,然后不必重写所有URL。您还可以自动检测用户的浏览器语言。

但是,搜索引擎可能不太可能捕捉到各种语言,如果有人尝试共享链接,您也会失去语言。

但是我喜欢您发布的完整解决方案,非常全面,谢谢。 :-)


1
“无 URL” 语言切换的问题在于搜索引擎。它们只会索引单一语言,或者如果在同一 URL 上看到不同内容,它们就会感到困扰。这只适用于您网站的私人(仅限登录)区域。 - deceze
哦,是的,那是个好观点。我会更新我的答案来注明这一点。 - Simon East

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接