ThinkPHP、ZF2、Yaf、Laravel框架路由大比拼 前言 读过一篇关于Zend Framework2的技术文章《ZF2多级树形路由Route配置实例》,是介绍路由配置的。我觉得很有意思,这是的需求: /user对应用户列表页面 /user/:user_id对应用户的个人主页,比如 /user/AlloVince 就对应AlloVince用户的个人主页 /user/:user_id/blog/对应用户的博客列表页面,比如 /user/AlloVince/blog 就会列出AlloVince写过的Blog /user/:user_id/blog/:blog_id对应用户的一篇博客文章 方案引用自原文: 'router' => array( 'routes' => array( 'user' => array( 'type' => 'Segment', 'options' => array( 'route' => '/user[/]', 'defaults' => array( 'controller' => 'UserController', 'action' => 'index', ), ), 'may_terminate' => true, 'child_routes' => array( 'profile' => array( 'type' => 'Segment', 'options' => array( 'route' => '[:id][/]', 'constraints' => array( 'id' => '[a-zA-Z0-9_-]+' ), 'defaults' => array( 'action' => 'get' ), ), 'may_terminate' => true, 'child_routes' => array( 'blog' => array( 'type' => 'Segment', 'options' => array( 'route' => 'blog[/]', 'constraints' => array( ), 'defaults' => array( 'action' => 'blog' ) ), 'may_terminate' => true, 'child_routes' => array( 'post' => array( 'type' => 'Segment', 'options' => array( 'route' => '[:post_id][/]', 'constraints' => array( 'post_id' => '[a-zA-Z0-9_-]+' ), 'defaults' => array( 'action' => 'post' ) ), 'may_terminate' => true, ), ), ), ), //profile child_routes end ), //profile end ), //user child_routes end ), //user end ), ), 看了这篇文章后,我打算使用我用过的PHP框架来实现这个路由需求。 ThinkPHP 新建一个ThinkPHP项目: 复制代码 代码如下: composer create-project topthink/thinkphp tp --prefer-dist 命令行显示我安装的是3.2.2 Installing topthink/thinkphp (3.2.2) 我看ThinkPHP官网最新稳定版本是3.2.3。 我特意去packagist官网查了一下,库中稳定版确实是3.2.2。 我得使用3.2.3。为什么我特别纠结这一点哩?因为: 3.2的路由功能是针对模块设置的,所以URL中的模块名不能被路由,路由定义也通常是放在模块配置文件中。 3.2.3版本开始增加全局路由定义支持,可以在项目的公共配置文件中定义路由。 也就是说,路由重写的部分是Controller和Action部分,Moudle还是存在。 我希望的是/user,而不是home/user。(ThinkPHP中默认Module是Home,'DEFAULT_MODULE' => 'Home',可以修改) 当然,这个问题也可以修改.htaccess文件的解决。但是,我还是决定安装3.2.3。 在ThinkPHP官网下载最新的包,解压。 使用浏览器访问一下项目的入口文件,让ThinkPHP自动生成了一个默认的应用模块Home。 修改公共配置文件tp\Application\Common\Conf\config.php: true, // URL访问模式,可选参数0、1、2、3,代表以下四种模式: // 0 (普通模式); 1 (PATHINFO 模式); 2 (REWRITE 模式); 3 (兼容模式) 默认为PATHINFO 模式 'URL_MODEL' => 2, // URL伪静态后缀设置,为空表示可以支持所有的静态后缀 // 使用U函数生成URL时会不带后缀 'URL_HTML_SUFFIX' => '', // URL变量绑定到Action方法参数,默认为true 'URL_PARAMS_BIND' => true, // URL变量绑定的类型 0 按变量名绑定 1 按变量顺序绑定,默认为0 'URL_PARAMS_BIND_TYPE' => 0, // 路由配置 'URL_ROUTE_RULES' => array( '/^url$/' => 'Home/User/url', '/^user$/' => 'Home/User/index', '/^user\/([a-zA-Z0-9_-]+)$/' => 'Home/User/show?name=:1', '/^user\/([a-zA-Z0-9_-]+)\/blog$/' => 'Home/Blog/index?name=:1', '/^user\/([a-zA-Z0-9_-]+)\/blog\/([0-9]+)$/' => 'Home/Blog/show?name=:1&blog_id=:2', ), ); ?> 创建文件tp\Application\Home\Controller\UserController.class.php: {$url}
\n"; } } public function index() { echo '我是用户列表^_^'; } public function show($name) { echo "欢迎你,{$name}"; } } ?> 创建文件tp\Application\Home\Controller\BlogController.class.php: 访问:http://127.0.0.1/tp/url 输出: 复制代码 代码如下:
/tp/user
/tp/user/jing
/tp/user/jing/blog
/tp/user/jing/blog/1
访问上面4个链接,依次返回: 我是用户列表^_^ 欢迎你,jing 这是jing的博客列表 jing的这篇博客的id为1 下面其他框架,也同样输出以上内容。 Zend Framework 2 使用ZF2骨架程序创建一个ZF2项目: composer create-project --stability="dev" zendframework/skeleton-application zf2 修改默认模块Application的配置文件zf2\module\Application\config\module.config.php: array( 'routes' => array( 'home' => array( 'type' => 'Zend\Mvc\Router\Http\Literal', 'options' => array( 'route' => '/url', 'defaults' => array( 'controller' => 'Application\Controller\User', 'action' => 'url', ), ), ), // The following is a route to simplify getting started creating // new controllers and actions without needing to create a new // module. Simply drop new controllers in, and you can access them // using the path /application/:controller/:action 'application' => array( 'type' => 'Literal', 'options' => array( 'route' => '/application', 'defaults' => array( '__NAMESPACE__' => 'Application\Controller', 'controller' => 'Index', 'action' => 'index', ), ), 'may_terminate' => true, 'child_routes' => array( 'default' => array( 'type' => 'Segment', 'options' => array( 'route' => '/[:controller[/:action]]', 'constraints' => array( 'controller' => '[a-zA-Z][a-zA-Z0-9_-]*', 'action' => '[a-zA-Z][a-zA-Z0-9_-]*', ), 'defaults' => array( ), ), ), ), ), 'user_list' => array( 'type' => 'Segment', 'options' => array( 'route' => '/user[/]', 'defaults' => array( '__NAMESPACE__' => 'Application\Controller', 'controller' => 'User', 'action' => 'index', ), ), 'may_terminate' => true, 'child_routes' => array( 'user' => array( 'type' => 'Segment', 'options' => array( 'route' => '[:name][/]', 'constraints' => array( 'name' => '[a-zA-Z0-9_-]+', ), 'defaults' => array( 'action' => 'show', ), ), 'may_terminate' => true, 'child_routes' => array( 'blog_list' => array( 'type' => 'Segment', 'options' => array( 'route' => 'blog[/]', 'constraints' => array( ), 'defaults' => array( 'controller' => 'Blog', 'action' => 'index', ) ), 'may_terminate' => true, 'child_routes' => array( 'blog' => array( 'type' => 'Segment', 'options' => array( 'route' => '[:blog_id]', 'constraints' => array( 'blog_id' => '[0-9]+', ), 'defaults' => array( 'action' => 'show', ) ), 'may_terminate' => true, ), ), ), ), ), ), ), ), ), 'service_manager' => array( 'abstract_factories' => array( 'Zend\Cache\Service\StorageCacheAbstractServiceFactory', 'Zend\Log\LoggerAbstractServiceFactory', ), 'aliases' => array( 'translator' => 'MvcTranslator', ), ), 'translator' => array( 'locale' => 'en_US', 'translation_file_patterns' => array( array( 'type' => 'gettext', 'base_dir' => __DIR__ . '/../language', 'pattern' => '%s.mo', ), ), ), 'controllers' => array( 'invokables' => array( 'Application\Controller\Index' => 'Application\Controller\IndexController', 'Application\Controller\User' => 'Application\Controller\UserController', 'Application\Controller\Blog' => 'Application\Controller\BlogController', ), ), 'view_manager' => array( 'display_not_found_reason' => true, 'display_exceptions' => true, 'doctype' => 'HTML5', 'not_found_template' => 'error/404', 'exception_template' => 'error/index', 'template_map' => array( 'layout/layout' => __DIR__ . '/../view/layout/layout.phtml', 'application/index/index' => __DIR__ . '/../view/application/index/index.phtml', 'error/404' => __DIR__ . '/../view/error/404.phtml', 'error/index' => __DIR__ . '/../view/error/index.phtml', ), 'template_path_stack' => array( __DIR__ . '/../view', ), ), // Placeholder for console routes 'console' => array( 'router' => array( 'routes' => array( ), ), ), ); ?> 这个文件是骨架程序中自带的,我只是修改了router部分和controllers部分。要我写这么长的文件,那就太为难我了。这也是ZF官方发布了一个骨架程序的原因。 创建文件zf2\module\Application\src\Application\Controller\UserController.php: url()->fromRoute('user_list'), $this->url()->fromRoute('user_list/user', array('name' => $name)), $this->url()->fromRoute('user_list/user/blog_list', array('name' => $name)), $this->url()->fromRoute('user_list/user/blog_list/blog', array('name' => $name, 'blog_id' => $blogId)), ); $view = new ViewModel(compact('urls')); $view->setTerminal(true); return $view; } public function indexAction() { $view = new ViewModel(); // 禁用布局模板 $view->setTerminal(true); return $view; } public function showAction() { $username = $this->params()->fromRoute('name'); $view = new ViewModel(compact('username')); $view->setTerminal(true); return $view; } } ?> 创建文件zf2\module\Application\src\Application\Controller\BlogController.php: params()->fromRoute('name'); $view = new ViewModel(compact('username')); $view->setTerminal(true); return $view; } public function showAction() { $username = $this->params()->fromRoute('name'); $blogId = $this->params()->fromRoute('blog_id'); $view = new ViewModel(compact('username', 'blogId')); $view->setTerminal(true); return $view; } } ?> zf2不支持Action参数绑定,ThinkPHP不仅支持绑定,还支持2种绑定方式:按变量名绑定和按变量顺序绑定。 zf2中Action必须得返回视图,除非exit()。如果你知道可以禁用视图的办法,请告诉我。 创建文件zf2\module\Application\view\application\user\url.phtml:

创建文件zf2\module\Application\view\application\user\index.phtml: 我是用户列表^_^ 创建文件zf2\module\Application\view\application\user\show.phtml: 欢迎你, 创建文件zf2\module\Application\view\application\blog\index.phtml: 这是的博客列表 创建文件zf2\module\Application\view\application\blog\show.phtml: 复制代码 代码如下: 的这篇博客的id为 Yaf 安装Yaf 使用代码生成工具创建Yaf项目 修改启动文件yaf\application\Bootstrap.php,修改其中的_initRoute方法: $router = Yaf_Dispatcher::getInstance()->getRouter(); $route0 = new Yaf_Route_Rewrite('url', array( 'controller' => 'User', 'action' => 'url', ), array() ); $route1 = new Yaf_Route_Rewrite('user', array( 'controller' => 'User', 'action' => 'index', ), array() ); $route2 = new Yaf_Route_Regex('#user/([a-zA-Z0-9_-]+)#', array( 'controller' => 'User', 'action' => 'show', ), array(1 => 'name',) ); $route3 = new Yaf_Route_Regex('#user/([a-zA-Z0-9_-]+)/blog#', array( 'controller' => 'Blog', 'action' => 'index', ), array(1 => 'name',) ); $route4 = new Yaf_Route_Regex('#user/([a-zA-Z0-9_-]+)/blog/([0-9]+)#', array( 'controller' => 'Blog', 'action' => 'show', ), array(1 => 'name', 2 => 'blogId',) ); $router->addRoute('url', $route0); $router->addRoute('user_list', $route1); $router->addRoute('user', $route2); $router->addRoute("blog_list", $route3); $router->addRoute("blog", $route4); Yaf有路由功能,但是没有根据路由名生成URL的方法。所以我定义了一个项目名,用于拼接URL。 在配置文件中添加配置项yaf\conf\application.ini: 复制代码 代码如下: project.name = 'yaf' 创建文件yaf\application\controllers\User.php: getConfig()->project->name; $urls = array( "/{$projectName}/user", "/{$projectName}/user/{$name}", "/{$projectName}/user/{$name}/blog", "/{$projectName}/user/{$name}/blog/{$blogId}", ); foreach ($urls as $url) { echo "
{$url}
\n"; } return false; } public function indexAction() { echo '我是用户列表^_^'; // 禁用视图模板 return false; } public function showAction($name) { echo "欢迎你,{$name}"; return false; } } 创建文件yaf\application\controllers\Blog.php: 复制代码 代码如下: 'UserController@getUrl')); Route::get('/user', array('uses' => 'UserController@getIndex')); Route::get('/user/{username}', array('uses' => 'UserController@getShow')); Route::get('/user/{username}/blog', array( 'as' => 'blog_list', 'uses' => 'BlogController@getIndex', )); Route::get('/user/{username}/blog/{blogId}', array( 'as' => 'blog', 'uses' => 'BlogController@getShow', ))->where(array('blogId' => '[0-9]+')); 查看路由定义情况: 复制代码 代码如下: php artisan route:list 输出: 复制代码 代码如下: +--------+----------+-------------------------------+-----------+----------------------------------------------+------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+-------------------------------+-----------+----------------------------------------------+------------+ | | GET|HEAD | url | | App\Http\Controllers\UserController@getUrl | | | | GET|HEAD | user | | App\Http\Controllers\UserController@getIndex | | | | GET|HEAD | user/{username} | | App\Http\Controllers\UserController@getShow | | | | GET|HEAD | user/{username}/blog | blog_list | App\Http\Controllers\BlogController@getIndex | | | | GET|HEAD | user/{username}/blog/{blogId} | blog | App\Http\Controllers\BlogController@getShow | | +--------+----------+-------------------------------+-----------+----------------------------------------------+------------+ 定义路由变量全局模式,修改文件laravel\app\Providers\RouteServiceProvider.php中的boot方法: 复制代码 代码如下: public function boot(Router $router) { $router->pattern('username', '[a-zA-Z0-9_-]+'); parent::boot($router); } 创建UserController控制器: 复制代码 代码如下: php artisan make:controller UserController Laravel帮我们在laravel\app\Http\Controllers目录下创建了文件UserController.php,文件中已经为我们写好一部分骨架代码。修改文件laravel\app\Http\Controllers\UserController.php: 复制代码 代码如下: {$url}

\n"; } } public function getIndex() { echo '我是用户列表^_^'; } public function getShow($name) { echo "欢迎你,{$name}"; } } 创建BlogController控制器: 复制代码 代码如下: php artisan make:controller BlogController 修改文件laravel\app\Http\Controllers\BlogController.php: 复制代码 代码如下: