Недавно я занимался некоторыми исследованиями в SEO и как обрабатывать URI, использующие дефисы или подчеркивания, по-разному, особенно Google, которые рассматривают дефисы как разделители.
В любом случае, желая адаптировать мой текущий проект для соответствия этим критериям, я обнаружил, что, поскольку Kohana использует имена функций для определения страниц, я получал неожиданное предупреждение «-».
Мне было интересно, есть ли способ разрешить использование URI в Кохане, например:
http://www.mysite.com/controller/function-name
Очевидно, я мог бы настроить routeHandler для этого … но если бы у меня был пользовательский контент, то есть новости. Я должен был бы получить все статьи из базы данных, создать URI, а затем выполнить маршрутизацию для каждого из них.
Есть ли альтернативные решения?
Примечание. Это тот же подход, что и в ответе Лорана , чуть более ООП. Kohana позволяет очень легко перегрузить любой системный класс, поэтому мы можем использовать его, чтобы сэкономить нам некоторую типизацию, а также обеспечить более чистые обновления в будущем.
Мы можем подключаться к потоку запросов в Кохане и фиксировать тире в действительной части URL-адреса. Для этого мы переопределим системный класс Request_Client_Internal и метод execute_request (). Там мы проверим, имеет ли запрос-> действие тире, и если это так, мы переключим их на подчеркивания, чтобы позволить php правильно вызвать наш метод.
Шаг 1. Откройте приложение / bootstrap.php и добавьте эту строку:
define('URL_WITH_DASHES_ONLY', TRUE);
Вы используете эту константу, чтобы быстро отключить эту функцию для некоторых запросов, если вам нужны символы подчеркивания в URL-адресе.
Шаг 2. Создайте новый php-файл в: application / classes / request / client / internal.php и вставьте этот код:
<?php defined('SYSPATH') or die('No direct script access.'); class Request_Client_Internal extends Kohana_Request_Client_Internal { /** * We override this method to allow for dashes in the action part of the url * (See Kohana_Request_Client_Internal::execute_request() for the details) * * @param Request $request * @return Response */ public function execute_request(Request $request) { // Check the setting for dashes (the one set in bootstrap.php) if (defined('URL_WITH_DASHES_ONLY') and URL_WITH_DASHES_ONLY == TRUE) { // Block URLs with underscore in the action to avoid duplicated content if (strpos($request->action(), '_') !== false) { throw new HTTP_Exception_404('The requested URL :uri was not found on this server.', array(':uri' => $request->uri())); } // Modify action part of the request: transform all dashes to underscores $request->action( strtr($request->action(), '-', '_') ); } // We are done, let the parent method do the heavy lifting return parent::execute_request($request); } } // end_class Request_Client_Internal
То, что это делает, просто заменяет все тире в действии $ request-> символами подчеркивания, поэтому, если url был / something / foo-bar , Kohana теперь с радостью направит его к нашему методу action_foo_bar ().
В то же время мы блокируем все действия с подчеркиваниями, чтобы избежать дублирования проблем с содержимым.
Невозможно напрямую сопоставить переносимую строку с PHP-функцией, поэтому вам придется выполнять маршрутизацию.
Что касается контента, созданного пользователем, вы можете сделать что-то вроде Stack Exchange. Каждый раз, когда пользовательский контент сохраняется в базе данных, создается пуля для него ( kohana-3-2-how-can-i-use-hyphens-in-uris
) и сохраняет его вместе с другой информацией. Затем, когда вам нужно установить ссылку на него, используйте уникальный идентификатор и добавьте пул до конца (например: http://stackoverflow.com/questions/7404646/kohana-3-2-how-can-i-use-hyphens-in-uris
) для удобочитаемости.
Вы можете сделать это с помощью лямбда-функций: http://forum.kohanaframework.org/discussion/comment/62581#Comment_62581
Вы могли бы сделать что-то вроде
Route::set('route', '<controller>/<identifier>', array( 'identifier' => '[a-zA-Z\-]*' )) ->defaults(array( 'controller' => 'Controller', 'action' => 'show', ));
поRoute::set('route', '<controller>/<identifier>', array( 'identifier' => '[a-zA-Z\-]*' )) ->defaults(array( 'controller' => 'Controller', 'action' => 'show', ));
Затем получите свой идентификатор содержимого в функции с Request::current()->param('identifier')
и проанализируйте его вручную, чтобы найти связанные данные.
Попробовав различные решения, я обнаружил, что самый простой и надежный способ – переопределить Kohana_Request_Client_Internal::execute_request
. Для этого добавьте файл в папку application
в «application \ classes \ kohana \ request \ client \ internal.php», затем установите его содержимое в:
<?php defined('SYSPATH') or die('No direct script access.'); class Kohana_Request_Client_Internal extends Request_Client { /** * @var array */ protected $_previous_environment; /** * Processes the request, executing the controller action that handles this * request, determined by the [Route]. * * 1. Before the controller action is called, the [Controller::before] method * will be called. * 2. Next the controller action will be called. * 3. After the controller action is called, the [Controller::after] method * will be called. * * By default, the output from the controller is captured and returned, and * no headers are sent. * * $request->execute(); * * @param Request $request * @return Response * @throws Kohana_Exception * @uses [Kohana::$profiling] * @uses [Profiler] * @deprecated passing $params to controller methods deprecated since version 3.1 * will be removed in 3.2 */ public function execute_request(Request $request) { // Create the class prefix $prefix = 'controller_'; // Directory $directory = $request->directory(); // Controller $controller = $request->controller(); if ($directory) { // Add the directory name to the class prefix $prefix .= str_replace(array('\\', '/'), '_', trim($directory, '/')).'_'; } if (Kohana::$profiling) { // Set the benchmark name $benchmark = '"'.$request->uri().'"'; if ($request !== Request::$initial AND Request::$current) { // Add the parent request uri $benchmark .= ' « "'.Request::$current->uri().'"'; } // Start benchmarking $benchmark = Profiler::start('Requests', $benchmark); } // Store the currently active request $previous = Request::$current; // Change the current request to this request Request::$current = $request; // Is this the initial request $initial_request = ($request === Request::$initial); try { if ( ! class_exists($prefix.$controller)) { throw new HTTP_Exception_404('The requested URL :uri was not found on this server.', array(':uri' => $request->uri())); } // Load the controller using reflection $class = new ReflectionClass($prefix.$controller); if ($class->isAbstract()) { throw new Kohana_Exception('Cannot create instances of abstract :controller', array(':controller' => $prefix.$controller)); } // Create a new instance of the controller $controller = $class->newInstance($request, $request->response() ? $request->response() : $request->create_response()); $class->getMethod('before')->invoke($controller); // Determine the action to use /* ADDED */ if (strpos($request->action(), '_') !== false) throw new HTTP_Exception_404('The requested URL :uri was not found on this server.', array(':uri' => $request->uri())); /* MODIFIED */ $action = str_replace('-', '_', $request->action()); /* ORIGINAL: $action = $request->action(); */ $params = $request->param(); // If the action doesn't exist, it's a 404 if ( ! $class->hasMethod('action_'.$action)) { throw new HTTP_Exception_404('The requested URL :uri was not found on this server.', array(':uri' => $request->uri())); } $method = $class->getMethod('action_'.$action); $method->invoke($controller); // Execute the "after action" method $class->getMethod('after')->invoke($controller); } catch (Exception $e) { // Restore the previous request if ($previous instanceof Request) { Request::$current = $previous; } if (isset($benchmark)) { // Delete the benchmark, it is invalid Profiler::delete($benchmark); } // Re-throw the exception throw $e; } // Restore the previous request Request::$current = $previous; if (isset($benchmark)) { // Stop the benchmark Profiler::stop($benchmark); } // Return the response return $request->response(); } } // End Kohana_Request_Client_Internal
Затем, чтобы добавить действие с дефисами, например, «controller / my-action», создайте действие под названием «my_action ()».
Этот метод также выдает ошибку, если пользователь пытается получить доступ к «controller / my_action» (во избежание дублирования содержимого).
Я знаю, что некоторым разработчикам не нравится этот метод, но преимущество этого в том, что он не переименовывает действие, поэтому, если вы проверите текущее действие, он будет постоянно называться «my-action» повсюду. С помощью метода Route или лямбда-функции действие будет называться «my_action», иногда «my-action» (поскольку оба метода переименовывают действие).