上篇分析了 Laravel 4 的启动流程 “Laravel 4 在启动的时候到底做了什么?”,读过的朋友应该对 Laravel 4 文件的加载顺序有了一个比较清晰的认识,这样就可以顺利的完成开发前的配置及准备工作。
而开发阶段我们将会面临另一个难点——路由。
通常,在看过官方文档后就可以比较顺利的使用路由,我们知道在 routes.php 文件中可以通过定义 Route::get() Route::post() Route::group() 等方法来注册我们的路由,然而你真的了解这些路由是怎么注册进我们的程序的吗?
除了“RESTful控制器”和“资源控制器”之外,所有的路由注册最终都指向 Illuminate\Routing\Router.php 的 createRoute 方法。而另一个重要的辅助方法则是 group 分组路由。只有理解了这两个方法,才算是完全理解了路由的注册机制。
让我们从 group 方法开始,这个方法可以直接使用,像这样 Route::group(array(), function(){}); 下面是它的源码:
public function group(array $attributes, Closure $callback) { // 存储分组共享属性,对于嵌套使用的路由组,将对传入的共享属性做递归合并处理 $this->updateGroupStack($attributes); // 回调函数中的路由注册最终都指向了 createRoute 方法 call_user_func($callback); // 出栈,移除本次记录的共享属性 array_pop($this->groupStack); }
第4行的作用是将传入的 $attributes 数组存储到 $this->groupStack 这个分组属性的堆栈中,如果出现了嵌套使用分组路由的情况,则对传入的属性数据做递归处理。这些临时存储的属性将在第7行,也就是最终执行到 createRoute 方法时被存储到各个线路中去,与各自的行为参数合并。第10行的出栈操作则是避免嵌套使用的分组路由出现属性存储的错误。
这里有两个重点:
array( 'http','https', // 协议类型,默认 http(任选其一即可) 'domain' => '', // 域名模式字符串 'prefix' => '', // 前缀 'before' => '', // 前置过滤器 'after' => '', // 后置过滤器 );
接下来,让我们来看看 createRoute 方法,像 Route::get('/', function(){}); 这样的调用最终都会指向它,下面是它的源码:
protected function createRoute($method, $pattern, $action) { // We will force the action parameters to be an array just for convenience. // This will let us examine it for other attributes like middlewares or // a specific HTTP schemes the route only responds to, such as HTTPS. // 将“非数组行为参数”格式化为标准的“行为参数数组” // 匿名函数将被解析为 array(Closure) // 字符串将被解析为 array('uses' => 'usesController@usesMethod') if ( ! is_array($action)) { $action = $this->parseAction($action); } $groupCount = count($this->groupStack); // If there are attributes being grouped across routes we will merge those // attributes into the action array so that they will get shared across // the routes. The route can override the attribute by specifying it. // 当存在“分组共享参数”时,将其合并到当前的“行为参数数组” if ($groupCount > 0) { $index = $groupCount - 1; $action = $this->mergeGroup($action, $index); } // Next we will parse the pattern and add any specified prefix to the it so // a common URI prefix may be specified for a group of routes easily and // without having to specify them all for every route that is defined. // 得到正确格式的“模式字符串”及“可选参数数组” list($pattern, $optional) = $this->getOptional($pattern); if (isset($action['prefix'])) { $prefix = $action['prefix']; // 为模式字符串增加前缀 $pattern = $this->addPrefix($pattern, $prefix); } // We will create the routes, setting the Closure callbacks on the instance // so we can easily access it later. If there are other parameters on a // routes we'll also set those requirements as well such as defaults. $route = with(new Route($pattern))->setOptions(array( // 从“行为参数数组”中获取回调函数:匿名函数 或 uses 参数 '_call' => $this->getCallback($action), ))->setRouter($this)->addRequirements($this->patterns); // 设置请求类型 $route->setRequirement('_method', $method); // Once we have created the route, we will add them to our route collection // which contains all the other routes and is used to match on incoming // URL and their appropriate route destination and on URL generation. // 为线路(非路由,注意区分)设置属性及可选参数 $this->setAttributes($route, $action, $optional); // 构造线路名称 $name = $this->getName($method, $pattern, $action); // 将线路加入路由集合实例,注意:同名路由将被覆盖 $this->routes->add($name, $route); // 返回当前线路实例 return $route; }
这部分的注释已经写的相当详细了,需要特别说明的是:
array( 'http','https', // 协议类型,默认 http(任选其一即可) 'domain' => '', // 域名模式字符串(不借助分组路由依然可以针对域名操作) 'as' => '', // 别名(优先作为线路名称,注意线路覆盖) 'prefix' => '', // 前缀(建议在路由分组中设置) 'before' => '', // 前置过滤器 'uses' => '', // 控制器方法字符串(支持简写,与 Closure 任选其一) Closure, // 匿名回调函数(支持简写,与 uses 任选其一) 'after' => '', // 后置过滤器 );
直接使用 匿名函数 相当于 array(Closure) 直接使用 字符串 相当于 array('uses' => 'usesController@usesMethod')