Подтвердить что ты не робот

Добавление префикса к каждому URL-адресу в CakePHP

Какой самый чистый способ добавить префикс для каждого URL-адреса в CakePHP, например параметр языка?

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, но только для генерации хешей

Из них кажется, что методы Controller и Helper необходимо переопределить, я мог бы жить без JsHelper. Моя идея состояла бы в том, чтобы написать общую функцию в AppController или, возможно, только в bootstrap.php для обработки вставки параметров. Переопределенные методы Controller и Helper будут использовать эту функцию, как если бы я захотел вручную вызвать Router::url. Достаточно ли этого?

4b9b3361

Ответ 2

Это, по сути, весь код, который я реализовал для решения этой проблемы в конце (по крайней мере, я думаю, что все; -)):

/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}'));

Это позволяет использовать URL по умолчанию, например /controller/action, для использования языка по умолчанию (JPN в моем случае) и для URL-адресов, например /eng/controller/action, для использования альтернативного языка. Эту логику можно легко изменить в функции router_url_language().

Для этого мне также нужно определить два маршрута для каждого маршрута, один из которых содержит параметр /:language/, а один без него. По крайней мере, я не мог понять, как это сделать по-другому.

Ответ 3

Более простым способом может быть сохранение выбранного языка в файле cookie, а затем не нужно переписывать все URL-адреса. Вы также можете автоматически определить язык пользовательского браузера.

Однако, поисковые системы вряд ли будут захватывать различные языки, и вы также потеряете язык, если кто-то попытается поделиться ссылкой.

Но любите полное решение, которое вы разместили, очень всеобъемлющее, спасибо.: -)