框架的内容非常多,如果仔细学一遍的话,一天时间是不够用的。所以我针对性的选择学习我们需要的内容,大致有路由、中间件、控制器这三个核心部分。即使只有这三部分,也绝不是只需要花3个小时就能掌握的,所以要有所准备。多花些时间来学习。
我们要明白,框架能实现的,编程语言一样能实现。编程语言学不好,你根本就不知道框架都在做什么。框架存在的目的使用一种统一的方式来快速开发和管理我们的应用。千万别让框架成为你学习编程语言的阻力。
在我短暂的职业生涯中,遇到过只会用Spring而不会Java的Java工程师,也遇到过只会用jQuery而不会JavaScript的前端工程师。我希望我们都不会成为这种人。
关于框架和编程语言的争论,一直没有停下过。我不管其他人怎么想,我对自己的要求始终都是编程语言第一,框架思想第二。至于框架的API我从不死记硬背,常用的会形成肌肉记忆,不常用的背下来浪费脑空间,不如直接去翻官方文档。
一、路由
什么是路由?
wikipedia上给出的答案:路由是为网络中或多个网络之间或之间选择路径的过程。
在实际的web应用中,路由通过路由配置表,决定了你从浏览器输入的地址,对应到执行哪些事情,返回哪些资源。
最简单的是windows的hosts文件,就是一个路由配置表。
laravel中的路由内容非常多,我把大多数常用的写了Demo。
laravel的路由配置分两种,一种是api配置,一种是页面配置。我们不需要页面配置,所以只关注api的配置。
在上一章中讲到过,路由配置表在项目根目录的routes目录下,api.php文件代表api路由配置。我们的代码都在这个文件中。
你可以使用postman之类的工具对路由进行测试。
基础路由
基础路由是使用Illuminate\Support\Facades\Route
这个类的静态方法创建的。这个方法接受2个参数,第一个是路由名字,第2个是回调函数。
Route::get('route1', function () { return 'hello route1'; });
上面这段代码就是基础路由的基本操作。访问http://homestead.test/api/route24
就能得到内容。
这里为什么带有一个api前缀呢?目的是为了和页面路由进行区分。
我们可以修改这个前缀,比如加一个版本号。
在app/providers/RouteServiceProvider.php中,找到mapApiRoutes
方法,这里面定义了api路由的前缀。我们可以加一个v1表示版本1,这样就需要通过http://homestead.test/api/v1/route24
来访问。
protected function mapApiRoutes() { Route::prefix('api/v1') ->middleware('api') ->namespace($this->namespace) ->group(base_path('routes/api.php')); }
上面的route1路由只支持get方式请求,你可以尝试使用post方式请求,会得到一个错误。
The POST method is not supported for this route. Supported methods: GET, HEAD.
意思是这个路由不支持post方法,只支持get方法请求。
那我们可以写一个post的路由。
Route::post('route2', function () { return 'hello route2'; });
上面这个路由支持了post方法的请求。
laravel共支持6种方式的请求,这些方式都有对应的Route类静态方法:
-
get
-
post
-
put
-
patch
-
delete
-
options
如果你想创建一个同时支持get和post的路由,可以使用match方法,接受3个参数,第1个是字符串数组,包含支持的请求方式,第2个是路由名称,第3个是回调函数。
Route::match(['get', 'post'], "route3", function () { return 'hello route3'; });
如果你想创建一个同时支持所有方式的路由,可以使用any方法。
Route::any('route4', function () { return "hello route4"; });
重定向
使用redirect方法。这样能让route5重定向到route4.
Route::redirect('route5', 'route4');
重定向的默认状态码是302.我们可以使用第3个参数自己指定,如301
Route::redirect('route6', 'route4', 301);
除此之外,还可以使用permanentRedirect方法,和上面的代码作用相同。
Route::permanentRedirect('route7', 'route4');
路由参数
必选参数
使用{}包裹,这个参数会自动注入到回调函数中。
Route::get('/route8/{id}', function ($id) { return 'id:' . $id; });
可选参数
在参数名后面加?。必须有默认值,不然会报错
Route::get('/route9/{id?}', function ($id = 1) { return 'id:' . $id; });
使用正则约束参数
Route::get('route10/{id}', function ($id) { return 'id:' . $id; })->where('id', '[A-Za-z]+');
只有route10/a这种字母参数路由能请求响应,route10/1这种数字参数路由就无法匹配。
如果你想进行全局约束,在app/Providers/RouteServiceProvider中的boot方法中设置。比如所有名为id的路由参数只支持数字。
public function boot() { Route::pattern('id', '[0-9]+'); parent::boot(); }
如果全局有设置,同时又存在where方法,where会覆盖全局设置。
路由名命
链式调用name方法。
Route::get('route11/info', function () { return 'route 11 info'; })->name('info');
这样route11/info
这个路由的名字就是info
.
再通过route方法就能得到这个路由。$url = route('info');
回退路由
用于处理其它所有路由都匹配不到的路由,使用fallback方法。在这里适合做一些异常处理,比如返回404.
Route::fallback(function () { return "404 啦"; });
除了上述的这几种,还有路由组、路由模型绑定、访问控制等其它API。但这需要理解其它的几个概念才能继续下去。所以路由的内容先到此为止。
二、中间件
什么是中间件?
简单地说,a要访问b,中间件在中间进行拦截,a和b就没有直接关系。a访问到中间件,中间件再访问b。
这里的中间件,其实和Java中的过滤器/拦截器是一回事,都属于面向切面编程的范畴,只是叫法不一样。
使用一个中间件大约分3步。
1.定义中间件
假设我们定义一个过滤年龄的中间件。
在命令行输入php artisan make:middleware CheckAge
,会自动在App/Http/Middleware文件夹中创建CheckAge.php文件。我们也可以手动创建。
这个文件中有一个和文件同名的类,类中只有一个handle
方法。我们可以在里面写一个拦截逻辑。
<?php namespace App\Http\Middleware; use Closure; // 定义中间件, 执行命令 php artisan make:middleware CheckAge,也可以自己手动创建 class CheckAge { public function handle($request, Closure $next) { if ($request->age < 18) { return response('未成年不可观看'); } // 继续传递请求,调用 next,参数为 request return $next($request); } }
2.注册中间件
在 app/Http/Kernel.php
中的 $middleware
属性中列出这个中间件。
// 在对象中添加这个中间件 'checkAge' => \App\Http\Middleware\CheckAge::class,
3.使用中间件
创建一个路由,使用middleware方法设置中间件。middleware方法接受不定个数的参数,如果需要多个中间件,就传递多个参数进去。
Route::middleware('checkAge')->get('route14/{age}', function ($age) { return 'route14'; });
这样访问http://homestead.test/api/v1/route14/12
就会遭到拦截,而http://homestead.test/api/v1/route14/22
则可以得到正确的资源。
中间件生效了。
上面的3步是中间件的基本用法,更高级的用法还有几个,但不常用,这里简单说下。
全局注册中间件
在 app/Http/Kernel.php
中的 $middleware
属性中列出这个中间件。
中间件群组
修改$middlewareGroups
属性。
中间件排序
$middlewarePriority
属性指定中间件优先级。
中间件参数
在调用middleware方法时,以"中间件名:参数"
的方式设置参数。而中间件的handle方法可以多接受一个参数。
三、控制器
控制器不是一个新的概念。
控制器的本质,其实就是路由中的回调函数。
控制器能将相关的请求处理逻辑组成一个单独的类。 控制器被存放在 app/Http/Controllers
目录中。
创建控制器
在app/Http/Controller
目录中新建一个文件,里面创建类和方法,并模拟一些数据。
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Route; class UserController { private $users = [ ['id' => 1, 'name' => '小明'], ['id' => 2, 'name' => '小红'], ['id' => 3, 'name' => '小张'], ]; public function searchUserById($id) { $result = ""; foreach ($this->users as $user) { if ($user['id'] = $id) { $result = $user; break; }; } return $result; } }
使用控制器
创建一个路由,第二个参数替换成命名空间\class名@function名
格式的字符串。
Route::get('user/{id}', 'UserController@searchUserById');
请求http://homestead.test/api/v1/user/1,就能拿到小明的数据。
单个行为控制器
如果你想定义一个只处理单个行为的控制器,你可以在控制器中放置一个 __invoke
方法。
比如上面的例子中,你只想查询用户。
修改方法名:
public function __invoke ($id) { // 仅修改方法名,内容不变 // ... }
修改路由:
Route::get('user/{id}', 'UserController');
控制器中间件
除了在路由中使用middleware方法以外,在控制器的构造函数中指定中间件更为方便。但这样必须继承Controller类,因为它提供了middleware方法。
class UserController extends Controller { public function __construct() { // 表示这个控制器会使用chackAge中间件。 $this->middleware('checkAge'); } }