前言
读过一篇关于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。
修改公共配置文件tpApplicationCommonConfconfig.php:
<?phpreturn array( // 开启路由 "URL_ROUTER_ON" => 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", ),);?>
创建文件tpApplicationHomeControllerUserController.class.php:
<?phpnamespace HomeController;use ThinkController;class UserController extends Controller { public function url() { $name = "jing"; $blogId = 1; $urls = array( U("/user"), U("/user/{$name}"), U("/user/{$name}/blog"), U("/user/{$name}/blog/{$blogId}"), ); foreach ($urls as $url) { echo "<a href="{$url}">{$url}<a/><br />"; } } public function index() { echo "我是用户列表^_^"; } public function show($name) { echo "欢迎你,{$name}"; }}?>创建文件tpApplicationHomeControllerBlogController.class.php:
<?phpnamespace HomeController;use ThinkController;class BlogController extends Controller { public function index($name) { echo "这是{$name}的博客列表"; } public function show($blog_id, $name) { echo "{$name}的这篇博客的id为{$blog_id}"; }}?>访问:http://127.0.0.1/tp/url
输出:
复制代码 代码如下:<a href="/tp/user">/tp/user<a/><br /><a href="/tp/user/jing">/tp/user/jing<a/><br /><a href="/tp/user/jing/blog">/tp/user/jing/blog<a/><br /><a href="/tp/user/jing/blog/1">/tp/user/jing/blog/1<a/><br />
访问上面4个链接,依次返回:
我是用户列表^_^欢迎你,jing这是jing的博客列表jing的这篇博客的id为1下面其他框架,也同样输出以上内容。
Zend Framework 2
使用ZF2骨架程序创建一个ZF2项目:
composer create-project --stability="dev" zendframework/skeleton-application zf2
修改默认模块Application的配置文件zf2moduleApplicationconfigmodule.config.php:
<?php/** * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/ZendSkeletonApplication for the canonical source repository * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */return array( "router" => array( "routes" => array( "home" => array( "type" => "ZendMvcRouterHttpLiteral", "options" => array( "route" => "/url", "defaults" => array( "controller" => "ApplicationControllerUser", "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__" => "ApplicationController", "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__" => "ApplicationController", "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( "ZendCacheServiceStorageCacheAbstractServiceFactory", "ZendLogLoggerAbstractServiceFactory", ), "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( "ApplicationControllerIndex" => "ApplicationControllerIndexController", "ApplicationControllerUser" => "ApplicationControllerUserController", "ApplicationControllerBlog" => "ApplicationControllerBlogController", ), ), "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官方发布了一个骨架程序的原因。
创建文件zf2moduleApplicationsrcApplicationControllerUserController.php:
<?phpnamespace ApplicationController;use ZendMvcControllerAbstractActionController;use ZendViewModelViewModel;class UserController extends AbstractActionController { public function urlAction() { $name = "jing"; $blogId = 1; $urls = array( $this->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; }}?>创建文件zf2moduleApplicationsrcApplicationControllerBlogController.php:
<?phpnamespace ApplicationController;use ZendMvcControllerAbstractActionController;use ZendViewModelViewModel;class BlogController extends AbstractActionController { public function indexAction() { $username = $this->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()。如果你知道可以禁用视图的办法,请告诉我。
创建文件zf2moduleApplicationviewapplicationuserurl.phtml:
<?php foreach ($urls as $url): ?><a href="<?php echo $url;?>"><?php echo $url;?><a/><br /><?php endforeach; ?>
创建文件zf2moduleApplicationviewapplicationuserindex.phtml:
我是用户列表^_^创建文件zf2moduleApplicationviewapplicationusershow.phtml:
欢迎你,<?php echo $username; ?>创建文件zf2moduleApplicationviewapplicationlogindex.phtml:
这是<?php echo $username; ?>的博客列表创建文件zf2moduleApplicationviewapplicationlogshow.phtml:
复制代码 代码如下:<?php echo $username; ?>的这篇博客的id为<?php echo $blogId; ?>
Yaf
安装Yaf
使用代码生成工具创建Yaf项目
修改启动文件yafapplicationBootstrap.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。
在配置文件中添加配置项yafconfapplication.ini:
复制代码 代码如下:project.name = "yaf"
创建文件yafapplicationcontrollersUser.php:
<?phpclass UserController extends Yaf_Controller_Abstract { public function urlAction() { $name = "jing"; $blogId = 1; $app = Yaf_Application::app(); $projectName = $app->getConfig()->project->name; $urls = array( "/{$projectName}/user", "/{$projectName}/user/{$name}", "/{$projectName}/user/{$name}/blog", "/{$projectName}/user/{$name}/blog/{$blogId}", ); foreach ($urls as $url) { echo "<a href="{$url}">{$url}<a/><br />"; } return false; } public function indexAction() { echo "我是用户列表^_^"; // 禁用视图模板 return false; } public function showAction($name) { echo "欢迎你,{$name}"; return false; }}创建文件yafapplicationcontrollersBlog.php:
复制代码 代码如下:<?phpclass BlogController extends Yaf_Controller_Abstract { public function indexAction($name) { echo "这是{$name}的博客列表"; return false; } public function showAction($blogId, $name) { echo "{$name}的这篇博客的id为{$blogId}"; return false; }}
Yaf的Action支持参数绑定,是按变量名绑定的。$name、$blogId要和路由中配置的名称一样,而和参数顺序无关。
Laravel
新建Laravel项目:
复制代码 代码如下:composer create-project laravel/laravel --prefer-dist
清除合并文件。在目录laravelvendor下有个文件compiled.php,这个文件是为了减少IO提高框架性能,将很多类文件合并到一个文件中而生存的。在开发环境下,应该删除该文件,否则修改了一些文件发现没有效果,其实是因为文件已经合并缓存了。清除命令:
复制代码 代码如下:php artisan clear-compiled
在生产环境中应该开启,以提升性能:
复制代码 代码如下:php artisan optimize --force
修改路由文件laravelappHttpoutes.php:
复制代码 代码如下:<?phpRoute::get("/url", array("uses" => "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 | | AppHttpControllersUserController@getUrl | || | GET|HEAD | user | | AppHttpControllersUserController@getIndex | || | GET|HEAD | user/{username} | | AppHttpControllersUserController@getShow | || | GET|HEAD | user/{username}/blog | blog_list | AppHttpControllersBlogController@getIndex | || | GET|HEAD | user/{username}/blog/{blogId} | blog | AppHttpControllersBlogController@getShow | |+--------+----------+-------------------------------+-----------+----------------------------------------------+------------+
定义路由变量全局模式,修改文件laravelappProvidersRouteServiceProvider.php中的boot方法:
复制代码 代码如下: public function boot(Router $router) { $router->pattern("username", "[a-zA-Z0-9_-]+");
parent::boot($router); }
创建UserController控制器:
复制代码 代码如下:php artisan make:controller UserController
Laravel帮我们在laravelappHttpControllers目录下创建了文件UserController.php,文件中已经为我们写好一部分骨架代码。修改文件laravelappHttpControllersUserController.php:
复制代码 代码如下:<?phpnamespace AppHttpControllers;use AppHttpControllersController;class UserController extends Controller { public function getUrl() { $name = "jing"; $blogId = 1; $urls = array( url("/user"), action("UserController@getShow", array($name)), route("blog_list", array($name)), route("blog", array($name, $blogId)), ); foreach ($urls as $url) { echo "<a href="{$url}">{$url}<a/><br />"; } } public function getIndex() { echo "我是用户列表^_^"; } public function getShow($name) { echo "欢迎你,{$name}"; }}
创建BlogController控制器:
复制代码 代码如下:php artisan make:controller BlogController
修改文件laravelappHttpControllersBlogController.php:
复制代码 代码如下:<?phpnamespace AppHttpControllers;use AppHttpControllersController;class BlogController extends Controller { public function getIndex($name) { echo "这是{$name}的博客列表"; } public function getShow($name, $blogId) { echo "{$name}的这篇博客的id为{$blogId}"; }}
Laravel的Action也支持参数绑定,是按变量顺序绑定的,和变量名无关。
后语
我是Laravel粉,但是我也没有想黑其他框架的意思,大家有兴趣也可以用自己熟悉的框架来实现这个小例子,写了记得@我,语言不限。
以上所述就是本文的全部内容了,希望大家能够喜欢。
请您花一点时间将文章分享给您的朋友或者留下评论。我们将会由衷感谢您的支持!