Kohana 3.2。 - 如何在URI中使用连字符

时间:2011-09-13 15:41:00

标签: php kohana uri kohana-3 routes

最近我一直在研究搜索引擎优化,以及如何区分使用连字符或下划线的URI,尤其是那些将连字符视为分隔符的谷歌。

无论如何,急于调整我当前的项目以满足这个标准,我发现因为Kohana使用函数名来定义页面,我收到了意外的' - '警告。

我想知道是否有任何方法可以在Kohana中使用URI,如:

http://www.mysite.com/controller/function-name

显然我可以为此设置一个routeHandler ......但是如果我要用户生成的内容,即新闻。然后,我必须从数据库中获取所有文章,生成URI,然后为每个文章进行路由。

有没有替代解决方案?

5 个答案:

答案 0 :(得分:3)

注意:这与Laurent's answer中的方法相同,只是略微更多的OOP。 Kohana允许一个人非常容易地重载任何系统类,因此我们可以使用它来为我们节省一些打字,并允许将来更新更新。

我们可以插入Kohana中的请求流并修复URL的操作部分中的破折号。为此,我们将覆盖Request_Client_Internal系统类及其execute_request()方法。在那里我们将检查request->动作是否有破折号,如果是,我们将它们切换到下划线以允许php正确调用我们的方法。

第1步。打开 application / bootstrap.php 并添加以下行:

define('URL_WITH_DASHES_ONLY', TRUE);

如果您需要在网址中使用下划线,则可以使用此常量在某些请求中快速禁用此功能。

第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-&gt;动作中的所有破折号,因此如果url是 / something / foo-bar ,Kohana现在很乐意将它路由到我们的action_foo_bar () 方法。

同时我们使用下划线阻止所有操作,以避免重复的内容问题。

答案 1 :(得分:1)

无法直接将带连字符的字符串映射到PHP函数,因此您必须进行路由。

就用户生成的内容而言,您可以执行类似Stack Exchange的操作。每次将用户内容保存到数据库时,都会为其生成一个slug(kohana-3-2-how-can-i-use-hyphens-in-uris)并将其与其他信息一起保存。然后,当您需要链接到它时,使用唯一ID并将slug追加到末尾(例如:http://stackoverflow.com/questions/7404646/kohana-3-2-how-can-i-use-hyphens-in-uris)以便于阅读。

答案 2 :(得分:1)

答案 3 :(得分:0)

您可以执行类似

的操作
Route::set('route', '<controller>/<identifier>', array(
    'identifier' => '[a-zA-Z\-]*'
))
->defaults(array(
    'controller' => 'Controller',
    'action'     => 'show',
));

然后在Request::current()->param('identifier')的函数中接收您的内容标识符,并手动解析以查找相关数据。

答案 4 :(得分:0)

在尝试了各种解决方案后,我发现最简单,最可靠的方法是覆盖Kohana_Request_Client_Internal::execute_request。为此,请在“application \ classes \ kohana \ request \ client \ internal.php”中的application文件夹中添加一个文件,然后将其内容设置为:

<?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或lambda函数方法,该操作有时会被称为“my_action”,有时会被称为“my-action”(因为这两种方法都重命名了操作)。