摘要:本質是將為的請求轉化為追加的組內請求,對應的匿名函數依然是為的請求假如為,則返回優先從設置里面取值,沒有則生成單數形式的字符串,并將字符替換為小結資源類型的構造,實際上會被轉化為構造多個默認資源的路由,本質依然是基本構造
Laravel 路由 路由構造總覽
構造方法有:
Route::get、Route::post、Route::put、Route::patch、Route::delete、Route::options、Route::any、Route::match、Route::resource、Route::resources、Route::group
Route::get("foo", function () { // 基本方式 }); Route::match(["get", "post"], "/", function () { // 基本方式 }); Route::any("foo", function () { // 基本方式 }); Route::get("posts/{post}/comments/{comment}", function ($postId, $commentId) { // 必選路由參數 }); Route::get("user/{name?}", function ($name = "John") { // 可選路由參數 }); Route::get("user/{id}/{name}", function ($id, $name) { // 正則表達式約束 })->where(["id" => "[0-9]+", "name" => "[a-z]+"]); // 全局約束 RouteServiceProvider 的 boot 方法 public function boot() { Route::pattern("id", "[0-9]+"); parent::boot(); } Route::get("user/{id}", function ($id) { // 僅在 {id} 為數字時執行... }); Route::get("user/profile", function () { // 命名路由 })->name("profile"); Route::get("user/profile", "UserController@showProfile")->name("profile"); 為命名路由生成: // 生成 URL... $url = route("profile"); // 生成重定向... return redirect()->route("profile"); // 路由組 Route::group(["middleware" => "auth"], function () { Route::get("/", function () { // 使用 `Auth` 中間件 }); Route::get("user/profile", function () { // 使用 `Auth` 中間件 }); }); 命名空間|子域名路由|路由前綴 Route::group(["namespace" => "Admin","domain" => "{account}.myapp.com","prefix" => "admin"], function () { // 在 "AppHttpControllersAdmin" 命名空間下,子域名為{account}.myapp.com,路由前綴匹配 "/admin" 的控制器 }); Route::resource("photo", "PhotoController", ["except" => ["create", "store", "update", "destroy"], "names" => ["create" => "photo.build"],"middleware" => []); 路由模型綁定 隱式綁定# Laravel 會自動解析定義在路由或控制器方法(方法包含和路由片段匹配的已聲明類型變量)中的 Eloquent 模型 Route::get("api/users/{user}", function (AppUser $user) { return $user->email; }); 顯式綁定 RouteServiceProvider 類中的 boot 方法 public function boot() { parent::boot(); Route::model("user", AppUser::class); } Route::get("profile/{user}", function (AppUser $user) { // }); 自定義解析邏輯 public function boot() { parent::boot(); Route::bind("user", function ($value) { return AppUser::where("name", $value)->first(); }); }
基本有以下幾種形式:uri 分為是否帶有參數, action 分為匿名函數或者 Controller@Method 形式,可能還會帶一些其他的前置操作
基本構造Route::get、Route::post、Route::put、Route::patch、Route::delete、Route::options、Route::any、Route::match
以上的構造方法本質是一樣的,區別在于第一個參數
public function get($uri, $action = null) { return $this->addRoute(["GET", "HEAD"], $uri, $action); } protected function addRoute($methods, $uri, $action) { // 創建 $route(IlluminateRoutingRoute) 對象并加入到集合(IlluminateRoutingRouteCollection 路由集合輔助類)里,再返回 $route return $this->routes->add($this->createRoute($methods, $uri, $action)); } protected function createRoute($methods, $uri, $action) { // $action 若為 Controller@Method|["uses"=>Controller@Method] 形式 if ($this->actionReferencesController($action)) { $action = $this->convertToControllerAction($action); } $route = $this->newRoute( $methods, $this->prefix($uri), $action ); // 如果前綴條件棧不為空,則對 $route 進行相應的設置 if ($this->hasGroupStack()) { $this->mergeGroupAttributesIntoRoute($route); } // 將 where 前置條件注入到 $route 對象 $this->addWhereClausesToRoute($route); return $route; } protected function actionReferencesController($action) { if (! $action instanceof Closure) { return is_string($action) || (isset($action["uses"]) && is_string($action["uses"])); } return false; } protected function convertToControllerAction($action) { if (is_string($action)) { $action = ["uses" => $action]; } // 嘗試加入前置條件 namespace if (! empty($this->groupStack)) { $action["uses"] = $this->prependGroupNamespace($action["uses"]); } // 通過控制器來獲取 action $action["controller"] = $action["uses"]; // 類似:["controller"=>"namespaceController@Method", "uses"=>"namespaceController@Method"] return $action; } // $uri 嘗試增加前置條件 prefix(group 組中的 prefix,對應給下面的所有路由增加) protected function prefix($uri) { return trim(trim($this->getLastGroupPrefix(), "/")."/".trim($uri, "/"), "/") ?: "/"; } public function getLastGroupPrefix() { if (! empty($this->groupStack)) { $last = end($this->groupStack); return isset($last["prefix"]) ? $last["prefix"] : ""; } return ""; } protected function newRoute($methods, $uri, $action) { return (new Route($methods, $uri, $action)) ->setRouter($this) ->setContainer($this->container); } // new Route public function __construct($methods, $uri, $action) { $this->uri = $uri; $this->methods = (array) $methods; $this->action = $this->parseAction($action); if (in_array("GET", $this->methods) && ! in_array("HEAD", $this->methods)) { $this->methods[] = "HEAD"; } // 再嘗試給 uri 多帶帶的加入 prefix 前綴 if (isset($this->action["prefix"])) { $this->prefix($this->action["prefix"]); } } protected function parseAction($action) { // 委托 RouteAction action 輔助類進行解析 return RouteAction::parse($this->uri, $action); } public static function parse($uri, $action) { if (is_null($action)) { return static::missingAction($uri); // 拋異常 } // 匿名函數 if (is_callable($action)) { return ["uses" => $action]; } elseif (! isset($action["uses"])) { $action["uses"] = static::findCallable($action); } // 如果 $action["uses"] 類似 Controller 形式,則嘗試構造為 Controller@__invoke 形式,即沒有指定方法時調用 __invoke 方法 if (is_string($action["uses"]) && ! Str::contains($action["uses"], "@")) { $action["uses"] = static::makeInvokable($action["uses"]); } return $action; } protected static function findCallable(array $action) { // 嘗試從 $action 數組找到第一個滿足可調用且為數字鍵的值作為 $action 返回 return Arr::first($action, function ($value, $key) { return is_callable($value) && is_numeric($key); }); } public function hasGroupStack() { return ! empty($this->groupStack); } protected function mergeGroupAttributesIntoRoute($route) { $route->setAction($this->mergeWithLastGroup($route->getAction())); } public function mergeWithLastGroup($new) { // 使用上一層的 groupStack 設置 return RouteGroup::merge($new, end($this->groupStack)); } protected function addWhereClausesToRoute($route) { $route->where(array_merge( $this->patterns, isset($route->getAction()["where"]) ? $route->getAction()["where"] : [] )); return $route; } // 返回 IlluminateRoutingRoute 對象 public function add(Route $route) { // 設置路由以何種方式放入路由集合,待后續按此種方式來獲取 $this->addToCollections($route); $this->addLookups($route); return $route; } protected function addToCollections($route) { $domainAndUri = $route->domain().$route->uri(); // 表面可以通過 method 和 uri 來獲取路由 foreach ($route->methods() as $method) { $this->routes[$method][$domainAndUri] = $route; } $this->allRoutes[$method.$domainAndUri] = $route; } protected function addLookups($route) { $action = $route->getAction(); // 如果前置條件棧設置了 as ,則將 $route 注入到 $this->nameList,即可以通過名字來獲取路由 if (isset($action["as"])) { $this->nameList[$action["as"]] = $route; } if (isset($action["controller"])) { $this->addToActionList($action, $route); } } protected function addToActionList($action, $route) { // 表示可以通過控制器獲取路由 $this->actionList[trim($action["controller"], "")] = $route; }
流程小結(創建 route ,并將加入到路由集合里進行統一的管理)
根據 action 的形式和前置條件,或轉為數組(["use"=> Clause|namespaceController@Method]),或為匿名函數
根據前置條件,或將組 uri 加前綴
創建 route 對象,并將 action 統一為數組,再進行一些其他設置
若存在前置條件,則加入到 route 對象的 action 數組
route 對象加入 where 條件
其他構造Route::group
public function group(array $attributes, $routes) { $this->updateGroupStack($attributes); $this->loadRoutes($routes); array_pop($this->groupStack); } protected function updateGroupStack(array $attributes) { if (! empty($this->groupStack)) { $attributes = RouteGroup::merge($attributes, end($this->groupStack)); } $this->groupStack[] = $attributes; } protected function loadRoutes($routes) { if ($routes instanceof Closure) { $routes($this); // 注意:每個匿名函數都會有 router 對象 } else { $router = $this; require $routes; } }
小結
主要通過設置前置條件棧($groupStack),然后運用到組內的所有成員,本質還是基本構造
Route::resource、Route::resources
public function resource($name, $controller, array $options = []) { if ($this->container && $this->container->bound(ResourceRegistrar::class)) { $registrar = $this->container->make(ResourceRegistrar::class); } else { $registrar = new ResourceRegistrar($this); } $registrar->register($name, $controller, $options); } public function __construct(Router $router) { $this->router = $router; } public function register($name, $controller, array $options = []) { if (isset($options["parameters"]) && ! isset($this->parameters)) { $this->parameters = $options["parameters"]; } if (Str::contains($name, "/")) { $this->prefixedResource($name, $controller, $options); return; } $base = $this->getResourceWildcard(last(explode(".", $name))); // ["index", "create", "store", "show", "edit", "update", "destroy"] $defaults = $this->resourceDefaults; // 生成相應條件下的路由 foreach ($this->getResourceMethods($defaults, $options) as $m) { $this->{"addResource".ucfirst($m)}($name, $base, $controller, $options); } } protected function prefixedResource($name, $controller, array $options) { list($name, $prefix) = $this->getResourcePrefix($name); // $me 為 router 對象。本質是將 $name 為 "xx/yy/zz" 的 resource 請求轉化為 groupStack 追加 ["prefix"=>"xx/yy"] 的 group 組內請求,對應的匿名函數依然是 $name 為 "zz" 的 resource 請求 $callback = function ($me) use ($name, $controller, $options) { $me->resource($name, $controller, $options); }; return $this->router->group(compact("prefix"), $callback); } protected function getResourcePrefix($name) { $segments = explode("/", $name); $prefix = implode("/", array_slice($segments, 0, -1)); // 假如 $name 為 "xx/yy/zz", 則返回 ["zz", "xx/yy"] return [end($segments), $prefix]; } // 優先從設置里面取值,沒有則生成單數形式的字符串,并將字符 "-" 替換為 "_" public function getResourceWildcard($value) { if (isset($this->parameters[$value])) { $value = $this->parameters[$value]; } elseif (isset(static::$parameterMap[$value])) { $value = static::$parameterMap[$value]; } elseif ($this->parameters === "singular" || static::$singularParameters) { $value = Str::singular($value); } return str_replace("-", "_", $value); } protected function getResourceMethods($defaults, $options) { if (isset($options["only"])) { return array_intersect($defaults, (array) $options["only"]); } elseif (isset($options["except"])) { return array_diff($defaults, (array) $options["except"]); } return $defaults; } protected function addResourceIndex($name, $base, $controller, $options) { $uri = $this->getResourceUri($name); $action = $this->getResourceAction($name, $controller, "index", $options); return $this->router->get($uri, $action); } public function getResourceUri($resource) { if (! Str::contains($resource, ".")) { return $resource; } $segments = explode(".", $resource); $uri = $this->getNestedResourceUri($segments); // "xx/{xx}/yy/{yy}/zz" return str_replace("/{".$this->getResourceWildcard(end($segments))."}", "", $uri); } protected function getNestedResourceUri(array $segments) { // ["xx","yy","zz"] => "xx/{xx}/yy/{yy}/zz/{zz}" return implode("/", array_map(function ($s) { return $s."/{".$this->getResourceWildcard($s)."}"; }, $segments)); } protected function getResourceAction($resource, $controller, $method, $options) { $name = $this->getResourceRouteName($resource, $method, $options); $action = ["as" => $name, "uses" => $controller."@".$method]; if (isset($options["middleware"])) { $action["middleware"] = $options["middleware"]; } return $action; } protected function getResourceRouteName($resource, $method, $options) { $name = $resource; if (isset($options["names"])) { if (is_string($options["names"])) { $name = $options["names"]; } elseif (isset($options["names"][$method])) { return $options["names"][$method]; } } $prefix = isset($options["as"]) ? $options["as"]."." : ""; return trim(sprintf("%s%s.%s", $prefix, $name, $method), "."); }
小結
資源類型的構造,實際上會被轉化為構造多個默認資源的路由,本質依然是基本構造
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/22598.html
摘要:而我的新輪子也并不是專門解決它的問題的,而是順便解決而已。概述這個包,支持在所有的項目中使用。一旦出現成員,代表允許全部。列出允許跨域請求的方法列表,默認是代表所有方法。信息地址嗯,新輪子,求一波。 showImg(https://segmentfault.com/img/bV5VxN?w=844&h=656); 是的,可能了解 Laravel 的都知道,在 Laravel 中簡單的設...
摘要:請求周期加載自動加載器獲取應用對象實例化應用解析此對象貫穿全文主要過程設置基礎路徑基礎綁定注冊全局基礎服務核心容器別名設置注冊三個單例獲取對象實例化此對象為應用的樞紐,將會協調各部分之間的工作,完成請求主要過程注入應用對象注入事件對象注入 Laravel 請求周期 加載 composer 自動加載器 require __DIR__./../bootstrap/autoload.php;...
摘要:關于路由中的在多域名下的說明首先,我們需要知道決定了路由會綁定到哪個控制器,還有一點需要注意,路由中的屬性,決定了輔助函數生成的。 材料準備 一份干凈的laravel 兩份Nginx配置文件,主要配置如下: server_name *.amor_laravel_test_1.amor; root /var/www/amor_laravel_test/public; index in...
摘要:關于路由中的在多域名下的說明首先,我們需要知道決定了路由會綁定到哪個控制器,還有一點需要注意,路由中的屬性,決定了輔助函數生成的。 材料準備 一份干凈的laravel 兩份Nginx配置文件,主要配置如下: server_name *.amor_laravel_test_1.amor; root /var/www/amor_laravel_test/public; index in...
摘要:可以通過來直接設置路由前綴給添加前綴通過,還是通過就可以了匹配包含的匹配包含的好了,這兩個框架的路由基本比較和應用就這些了,還有一些比如控制器路由和如何自定義中間件等在后續再寫吧,或者請自行查閱文檔,以上內容如有錯誤請指出。 Laravel是我最喜歡的PHP Web開發框架,所以也希望可以在Go的Web框架中選擇一個類似Laravel這樣的好用又全棧的框架,刷了一下Beego, Ech...
摘要:框架關鍵技術解析讀書筆記二第五章框架應用程序根目錄版本默認的框架應用程序是符合規范的,所以相應的目錄結構也是基本固定的,不同的目錄加載了功能文件,如果添加了新的目錄,需要在文件中添加規范的自動加載部分并執行命令。 Laravel 框架關鍵技術解析·讀書筆記(二) 第五章 框架應用程序根目錄(5.1版本) 默認的Laravel框架應用程序是符合PSR規范的,所以相應的目錄結構也是基本...
閱讀 2889·2021-11-17 09:33
閱讀 3662·2021-11-16 11:42
閱讀 3488·2021-10-26 09:50
閱讀 1319·2021-09-22 15:49
閱讀 3046·2021-08-10 09:44
閱讀 3670·2019-08-29 18:36
閱讀 3925·2019-08-29 16:43
閱讀 2208·2019-08-29 14:10