2009-11-25 18 views
20

¿Cuál es la forma más limpia de agregar un prefijo a cada URL en CakePHP, como un parámetro de idioma?Agregar un prefijo a cada URL en CakePHP

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

Se necesita trabajar con los prefijos "reales" como admin, e idealmente la URL desnuda /controller/action podría ser redirigido a /DEFAULT-LANGUAGE/controller/action.

Ahora estoy trabajando en una aplicación retroadaptada, pero fue un truco, y necesito incluir el parámetro de idioma a mano en la mayoría de los enlaces, lo cual no es bueno.

Así que la pregunta es doble:

  • Cuál es la mejor manera de estructurar las rutas, por lo que el parámetro de idioma se incluye implícitamente por defecto sin tener que ser especificado para cada ruta que acaba de definir?
    • Router::connect('/:controller/:action/*', ...) debe incluir implícitamente el prefijo.
    • El parámetro debe estar disponible en $this->params['lang'] o en algún lugar similar para ser evaluado en AppController::beforeFilter().
  • ¿Cómo obtener Router::url() para incluir automáticamente el prefijo en la URL, si no se especifica explícitamente?
    • Router::url(array('controller' => 'foo', 'action' => 'bar')) deberían volver /en/foo/bar
    • Desde Controller::redirect(), Form::create() o incluso Router::url() necesita directamente a tener el mismo comportamiento, ignorando cada función no es realmente una opción. Html::image(), por ejemplo, debería producir una URL sin prefijo.

Los siguientes métodos parecen llamar Router::url.

  • Controller::redirect
  • Controller::flash
  • Dispatcher::__extractParams través Object::requestAction
  • Helper::url
  • JsHelper::load_
  • JsHelper::redirect_
  • View::uuid, pero sólo para una generación de hash

De los que parece que los métodos Controller y Helper tendrían que ser anulados, podría vivir sin el JsHelper. Mi idea sería escribir una función general en AppController o tal vez solo en bootstrap.php para manejar la inserción de parámetros. Los métodos reemplazados de Controller y Helper usarían esta función, como lo haría si quisiera llamar manualmente al Router::url. ¿Esto sería suficiente?

Respuesta

14

Esto es esencialmente todo el código que he implementado para resolver este problema en el final (al menos creo que eso es todo ;-)):

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

Esto permite que las direcciones URL por defecto, como /controller/action a utilizar el idioma por defecto (JPN en mi caso), y URLs como /eng/controller/action para usar una altern lenguaje ativo. Esta lógica se puede cambiar bastante fácilmente en la función router_url_language().

Para que esto funcione también necesito definir dos rutas para cada ruta, una que contenga el parámetro /:language/ y otra que no lo tengan. Al menos no pude entender cómo hacerlo de otra manera.

+0

Gran respuesta, esto realmente me ayudó. – gacrux

+2

@gacrux Tenga en cuenta que esto debería ser mucho menos complejo en Cake 1.3, que incluye prefijos personalizables. Lo anterior se aplica a Cake 1.2, que solo conocía * one * prefix. :) – deceze

0

Una forma más fácil podría ser almacenar el idioma elegido en una cookie y luego no tener que volver a escribir todas las URL. También es posible que detecte el idioma del navegador del usuario automáticamente.

Sin embargo, motores de búsqueda sería poco probable que la recolección de los diversos idiomas y también se iba a perder el idioma si alguien trató de compartir el enlace.

Pero amo la solución completa que ha publicado, muy completa, gracias. :-)

+1

El problema con los modificadores de idioma "sin URL" son los motores de búsqueda. Solo indexarán un solo idioma o incluirán sus bragas si ven contenido diferente en la misma URL. Eso solo funciona para las áreas privadas (solo para iniciar sesión) de su sitio. – deceze

+0

Oh, sí, ese es un buen punto. Actualizaré mi respuesta para notar eso. –

Cuestiones relacionadas