第3章 应用Laravel8 框架

3.1 上手 Laravel8
3.1.1 介绍
Lavavel 一直 秉承着优雅的原则,很好的支持了 composer,实现了更丰富的扩展,社区文档活跃,相较于TP,Lavavel 更庞大,安全性也更高 ,更适合开发大中型项目,被称为“巨匠型开发框架”。
优点:
Laravel 的设计思想是很先进的,非常适合应用各种开发模式 TDD, DDD 和 BDD。
支持composer包管理工具。
集合了php 比较新的特性,以及各种各样的设计模式, Ioc 容器,依赖注入等。
缺点:
基于组件式的框架,所以比较臃肿。
综合来说:Laravel 框架非常适合大中型的商业项目。Laravel 是以组件化为主的框架。
3.1.2 安装
终端行执行以下命令 :
composer create-project --prefer-dist laravel/laravel blog
此处省略 执行过程 :
注意:前提是符合框架基本要求,要求如下:
PHP >= 7.2.5
BCMath PHP 拓展
Ctype PHP 拓展
Fileinfo PHP 拓展
JSON PHP 拓展
Mbstring PHP 拓展
OpenSSL PHP 拓展
PDO PHP 拓展
Tokenizer PHP 拓展
XML PHP 拓展
安装成功,在浏览器输入 Laravel 框架的地址至 public
目录访问项目 :
3.1.3 目录介绍
|--- app --- 应用目录
| |-- Console -- 命令行目录
| |-- Exceptions -- 异常目录
| |-- Http -- 网络请求目录
| |-- Providers -- 服务提供目录
| |......
|--- bootstrap --- 启动目录
| |-- cache -- 路由和服务缓存目录
| |-- app.php -- 应用启动文件
|--- config --- 应用配置目录
| |-- app.php -- 应用配置文件
| |-- auth.php -- 认证配置文件
| |-- broadcasting.php -- 广播配置文件
| |-- cache.php -- 缓存配置文件
| |-- database.php -- 数据库配置文件
| |......
|--- database --- 数据库目录
| |-- factories -- 模型工厂目录
| |-- migrations -- 数据迁移目录
| |-- seeds -- 种子目录
| |......
|--- public --- 公共目录
| |-- css -- 公共css目录
| |-- js -- 公共js目录
| |-- index.php -- 入口文件
|--- resources --- 资源目录
| |-- lang -- 语言包目录
| |-- sass -- sass目录
| |-- views -- 模板目录
|--- routes --- 路由目录
| |-- api.php -- api路由定义
| |-- console.php -- 控制台路由定义
| |-- web.php -- 网页路由定义
|--- storage --- 存储目录
| |-- app -- 存储应用生成的文件
| |-- framework -- 存储框架生成的文件和缓存
| |-- logs -- 存储日志
|--- tests --- 单元测试目录
|--- vendor --- 第三方类库目录
| |-- composer -- composer目录
| |-- laravel -- laravel框架目录
| |-- symfony -- symfony组件目录
| |-- autoload.php -- 自动加载入口文件
| |......
|--- .env --- 环境变量配置文件
|--- artisan --- artisan工具文件
|--- server.php --- 命令行应用测试文件
|.........
核心目录与文件
.env 文件
这是一个重要文件,为 Laravel 框架主配置文件。
Artisan.php
该文件为 Laravel 提供了 Artisan 命令,artisan是 laravel 中自带的命令行工具的名称。
App 目录
你的大部分应用程序都位于 app
目录中。默认情况下,此目录的命名空间为 App
, 并通过 Composer 使用 自动加载。
Bootstrap 目录
bootstrap
目录包含引导框架的 app.php
文件。该目录还包含了一个 cache
目录, cache
目录下存放着框架生成的用来提升性能的文件,比如路由和服务缓存文件。
Config 目录
config
目录,顾名思义,包含应用程序所有的配置文件。我们鼓励你通读这些文件,以便帮助你熟悉所有可用的选项。
Public 目录
public
目录包含了入口文件 index.php
,它是进入应用程序的所有请求的入口点。此目录还包含了一些你的资源文件(如图片、JavaScript 和 CSS)。
Resources 目录
resources
目录包含了视图和未编译的资源文件(如 LESS、SASS 或 JavaScript)。此目录还包含你所有的语言文件。
Routes 目录
routes
目录包含了应用的所有路由定义,Laravel 默认包含了几个路由文件:web.php
、api.php
、 console.php
和 channels.php
。
Storage 目录
storage
目录包含编译后的 Blade 模板、session 会话生成的文件、缓存文件以及框架生成的其他文件。这个目录被细分成 app
、 framework
和 logs
三个子目录。app
目录可以用来存储应用生成的任何文件。 framework
目录用来存储框架生成的文件和缓存。最后, logs
目录包含应用的日志文件。
3.2 Laravel 8 请求与响应
3.2.1 控制器
在路由文件中我们可以写入很多的处理逻辑,但是当逻辑越来越多单纯的使用路由文件来编写就会使得路由文件越来越大,并且负责的职责也会越来越多。对于面向对象的设计来说是很不合理的,所这里会给予一个控制器组织这些行为。控制器能将相关的请求处理逻辑组成一个单独的类,控制器被存放在 app/Http/Controllers
目录。
1. 基础控制器
控制器都会放在 app\Http\Controllers 目录下面,在其中已经存在了一个 Controller.php 文件,我们可以在其中创建一个 index 方法,通过路由来执行该方法 :
Controller 文件:app\Http\Controllers\Controller.php
<?php
namespace App\Http\Controllers;
# ......
class Controller extends BaseController
{
# ......
public function index()
{
return '我是控制器';
}
}
路由文件:routes\web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Controller;
Route::get('index',[Controller::class,'index']);
结果 :

这个就是控制器,只是一般我们把这个控制器作为父类控制器来使用,接下来就要自力更生创建和使用自定义的控制器了。
2. 自定义控制器
首先可以施展我们的 cv 打法来创建自己的控制器。直接复制 Controller.php 文件到当前目录更名,也可以到子目录,记得修改子目录下的控制器的命名空间。例如,复制 Controller.php 控制器更名为 HelloController.php
HelloController 控制器:app\Http\Controllers\HelloController.php
<?php
namespace App\Http\Controllers;
class HelloController extends Controller
{
public function index()
{
return '我是 HelloController 控制器';
}
}
路由 :
use App\Http\Controllers\HelloController;
Route::get('hello', ,[HelloController::class,'index']);
结果 :

这样我们创建的控制器就能正常使用了,之后我们讲另一种方式来创建控制器并说明命名空间。
3. 命令创建控制器
Laravel 框架给我们提供了命令来创建控制器,命令为 :
php artisan make:controller 控制器名称
创建的控制器默认创建在 Controllers 下,如果需要创建子目录下的控制器呢?我们需要加入子目录名称。例如在 Controllers/Admin 目录下面创建 IndexController 控制器 :
php artisan make:controller Admin/IndexController
注意,目录和控制器中间要使用 \
分割,其目录为 :

在其中创建一个 index 方法【内容任意,这里就不多写了】,创建访问该方法的路由为 :
use app\Http\Controllers\Admin\IndexController;
Route::get('admin', [IndexController::class,'index']);
通过 blog.com/admin 访问可以成功访问到 index
方法。
4. 单行为控制器
以上创建的控制器都是最基础的控制器,加下来我们再来看几个特殊的控制器。首先就是单行为控制器,那么什么是单行为控制器呢?单行为控制器是 一个只处理单个行为的控制器,其创建命令为 :
php artisan make:controller 控制名称 --invokable
根据命令我们来创建一个单行为控制器,该控制器为 Admin/SingleBeController.php ,那么他和普通控制器的区别和特点主要是什么呢?首先来看看生成的控制器 :
SingleBeController控制器:app\Http\Controllers\Admin\SingleBeController.php
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class SingleBeController extends Controller
{
# 默认生成的方法
public function __invoke(Request $request)
{
return '单行为控制器';# 编写其中的唯一存在的方法
}
}
接下来对该方法编写一个路由 :
Route::get('sbc', 'Admin\SingleBeController@__invoke');
访问的结果为 :

如果仅仅是这样的话那么就没有必要使用单一行为控制器了,这时候我们可以这样来创建路由:
Route::get('sbc', 'Admin\SingleBeController');
通过上 URL 来访问能够得到同样的结果,也就是说 单行为控制器只需要 控制器名称 即可默然访问到 __invoke
方法,而这个控制器也只用来实现一个方法。
注意:并不是说单行为控制器中不能创建其他方法或者不能访问,而是作为单一行为控制器其特点是只实现一个功能,体现面向对象的单一职责。
5. 资源控制器
除了单行为控制器之外还有一个 资源控制器,其特点是该控制器会创建一个符合 restful 规范的控制器,并且可以典型的「CURD (增删改查)」路由分配给控制器。说起来会有点懵,我们结合代码来看看其特点。
(1)创建资源控制器
首先我们通过命令创建资源控制器 :
php artisan make:controller Admin\ResourceController --resource
Admin\ResourceController 是控制器名称,自行定义。
(2)资源路由功能测试
创建成功后会出现一个控制器,其中会存在一些已经存在的方法,例如:index,create,store 等,具体的可以自行创建一个资源控制器查看,其中方法是一样的。接下来对其中的方法进行编写:
ResourceController 控制器:app\Http\Controllers\Admin\ResourceController.php
<?php
namespace App\Http\Controllers\Admin;
class ResourceController extends Controller
{
# 其他代码省略 测试使用
public function index()
{
return '资源控制器的 index 方法';
}
public function store(Request $request)
{
return '资源控制器的 store 方法';
}
public function show($id)
{
return '资源控制器的 show 方法';
}
}
(3)路由编写
既然单行为控制器对路由有特殊点,那么资源控制器自然也有,这里就不卖关子了,我们直接来看其特点:
先创建一个该控制器的路由,这里需要注意的是调用的方法不再是 get 等之类的,而需要通过 resource 创建:
use app\Http\Controllers\Admin\ResourceController;
Route::resource('resource', ResourceController::class);
同样,创建出来的路由地址不需要的接具体的某个方法,那么我们是如何访问到具体的方法的呢?继续往下看。
(4)测试
资源控制器:通过不同的方式以及改变访问时添加的参数来进行访问,我们会发现访问到的会是不同的方法 :

以上是通过 get 方式以及添加参数访问的,分别访问到了 index 方法和 show 方法,其他的方法可以自行测试,其具体访问的是什么方法参照下表 :
(5)资源控制器操作处理

单行为控制器主要体现单一职责,一个类只实现一个方法。而资源控制器则是规范了开发的方法并且为预先为我们创建了一系列路由,可以快速访问,提升开发效率。
(6)控制器中间件
在路由中我们初步认识并且创建了中间件,而这里虽然有一个叫做 控制器中间件
的东西存在,但是其本质就是中间件,只是使用的地方放到了控制器当中。接下来就来看看在控制器中如何使用中间件的【这里就不重复对中间件进行说明了】。
(7)在路由文件中分配给控制器的路由
上一篇中我们直接通过路由组进行中间件的分配,而给控制器使用的中间件也可以直接通过路由来给定到某个控制器,这里我们需要在路由的后面借助 middleware
方法来实现,例如 :
Route::get('midd', [IndexController::class,'index'])->middleware('first');# 分配 first 中间件给 IndexController 控制器使用
这种方式分配的中间件对该控制器中的所有方法都会生效。
(8) 在控制器的构造函数中指定中间件
在控制器的构造函数中指定中间件更为方便。在控制器的构造函数中使用 middleware
方法,可以轻松地将中间件分配给控制器的操作,例如 :
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class IndexController extends Controller
{
public function __construct()
{
$this->middleware('first');# 对当前控制器下所有的方法都使用该中间件, 如果使用多个中间件可以使用数组
}
public function index()
{
return 'IndexController 下的 index';
}
public function hello()
{
return 'IndexController 下的 hello';
}
}
如果需要限定中间件分配的方法,还可以在 middleware 方法链式调用 only
或者 except
方法 :
$this->middleware('first')->only('index');# 只对该方法生效
# 或者
$this->middleware('first')->except('index');# 除了该方法,其他方法都生效
这样就能限定中间件生效的方法了。
6. 依赖注入
要通过依赖注入获取当前 HTTP 请求实例,你应该在控制器上引入 Illuminate\Http\Request
类。传入的请求实例将会由服务容器自动注入 :
依赖注入:只要不是由内部生产(比如初始化、构造函数
__construct
中通过自行手动 new 的),而是由外部以参数或其他形式注入的,都属于 依赖注入(DI)。
# 其他代码不变 这里使用依赖注入方式
public function login(Request $request)
{
$user = $request->input();# 获取到的是一个数组,
dd($user);
}
结果 :

从结果我们可以看到,设置的数据通过 request 请求对象成功获取到了。那么为什么依赖注入可以实现数据的获取而自己创建实例则无法获取呢?那么就要具体的看看什么是依赖注入了,我们来 扩展一点东西 :
<?php
# 车类 - 给人类使用
class Car
{
public $color;
public $type;
}
# 人类 - 调用车类
class Person
{
# 1.依赖注入方式
public function drive(Car $car)
{
$color = $car->color;
$type = $car->type;
echo "开 $color 的 $type ";
}
# ---------------------------------
# 2.手动创建方式
public function drive()
{
$car = new Car();
$color = $car->color;
$type = $car->type;
echo "开 $color 的 $type ";
}
}
# 1.依赖注入
$car = new Car();# 创建车类对象并对原有属性赋值
$car->color = '白色';
$car->type = '小轿车';
(new Person())->drive($car);# 传递被修改过的对象
# 结果:开 白色 的 小轿车
# 2.手动创建
(new Person())->drive();# 在人类中创建车类对象,不再外部传递
# 结果:开 的
注意:从上面的结果来看,我们可以看出新创建的对象无法和其他对象共有同一属性值,所以如果我们需要使用之前保存在对象中的内容就需要通过注入的方式传递对象而不能重新创建。
除了通过 依赖注入
以外,我们还可以使用 门面
和 助手函数
。
7. 门面静态调用
要通过门面调用请求数据,你应该在控制器上引入 use Illuminate\Support\Facades\Request
类 :
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Request;# 这里是需要重点注意的地方,和依赖注入所引用的不是同一个
class IndexController extends Controller
{
public function login()
{
$user = Request::input();
dump($user);
}
}
结果就不再展示,同样可以获取到数据。
需要注意的是: 通过门面方式调用时,引入的是
Illuminate\Support\Facades\Request
而通过依赖注入方式引入的则是Illuminate\Http\Request
。
8. 助手函数
Laravel 框架为我们提供了很多助手函数方便我们直接使用,request 函数就是其中之一,它可以帮助我们快速的获取到 request 对象。
<?php
namespace App\Http\Controllers\Admin;
class IndexController extends Controller
{
public function login()
{
$user = \request()->input();
dump($user);
}
}
通过助手函数不需要引入什么类,直接通过 request()
函数调用即可。
请求方法
既然框架给我们提供了一个请求对象,那么在其中也会给我们提供一系列已经准备好的方法给我们来使用,方便我们操作。首先来看看我们上面已经接触到的输入数据的获取。
获取输入
(1)input
上面已经接触到了 input
方法,从结果可以看出 其返回值是一个包含所有数据的键值对数组 - 获取所有数据 ,那么能不能传递键到 input
中直接获取需要的值呢?来尝试一下 :
$name = \request()->input('name');
dump($name);
结果:

可以看出我们是可以直接传递 key
从而获取对应的值的。
(2)all
和 input
方法比较类似的还有一个 all
方法,其作用也是获取所有数据,其不同点就在于 获取单个数据的时候,其返回的结果还是数组 :
$user = \request()->all();
dump($user);
结果:

(3)only 与 except
有时候我们可能 不需要的这么多的数据,只需要其中的几个即可 ,Laravel 框架的请求对象也很贴心的给我们创建了 only 与 except 两个方法来实现 :
$name = \request()->only('name');# 只获取的数据,多个数据可以通过数组传递
dump($name);
$name = \request()->except('name');# 只获取的数据,多个数据可以通过数组传递
dump($name);
结果分别是 :
array:1 [
"name" => "jack"
]
array:1 [
"password" => "123456"
]
另外还有一种 通过属性直接获取的方式,我们可以看看 :
$name = \request()->name;
dump($name);
结果 :

如果需要获取过来的数据,我们可以使用上面的几种方式来获取。
3.2.2 路由
1. 了解路由
路由 (route) 是指根据 url, 分配到对应的处理程序的映射。简单来说,就是一个路径的解析,根据客户端提交的路径,将请求解析到相应的 模块/控制器/方法上。
这里我们先来看一个 Laravel 中已经存在的一个路由 :
Route::get('/', function () {
return view('welcome');
});
设置一个域名来访问该框架,访问到该路由的 URL 为:blog.coms;上面这个路由是以 /
为 标识,无法很好的体现路由标识的特性,我们可以根据该路由来编写一个自己的路由 :
# 路由方法 路由标识 闭包
Route::get('base', function(){
return '第一个路由';
});
# 也可以路由到控制器:路由到 app\Http\Controllers\HelloController 控制器的 Hello 方法
Route::get('Hello', 'HelloController@hello');
无论怎么去定义路由,最基础的就是需要根据路由标识找到对应的路由地址 ,也就是说路由必须存在 路由标识
以及 路由地址
,也就是所有的路由都符合以下方式 :
Route : : 路由方法 ( 路由标识, 路由地址 ) ;
2. 路由方法
路由器允许你注册能响应任何 HTTP 请求的路由,在 Laravel 框架中主要包括以下方法 :
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);
3. 重定向路由
如果要定义重定向到另一个 URI 的路由,可以使用 Route::redirect
方法。这个方法可以快速的实现重定向,而不再需要去定义完整的路由或者控制器 :
Route::redirect('重定向路由标识','路由标识');// 路由重定向
# 重定向路由
Route::redirect('redirect', 'base');
4. 路由参数
有时路由需要一些变量来改变原本固定的内容,我们可以通过捕获一些 URL 片段来实现,而这些可以被我们捕获到的片段就是路由的参数,路由参数可以在原本的 URL 后面加上即可,那么我们如何表示我们的路由是需要参数的呢 ?
如果要表示某个路由需要给定参数,我们可以使用 {参数名称} 来表示,例如 :
# user 是路由标识,{id} 则是该路由需要的参数
Route::get('user/{id}', function ($id) {
return 'User '.$id;
});
3.2.3 CSRF
Laravel 提供应用程序免受
(CSRF)攻击。 跨站点请求伪造是一种恶意攻击,它凭借已通过身份验证的用户身份来运行未经过授权的命令。Laravel 会自动为每个活跃的用户的会话生成一个 CSRF「令牌」。该令牌用于验证经过身份验证的用户是否是向应用程序发出请求的用户。
在应用程序中定义 HTML 表单时,都应该在表单中包含一个隐藏的 CSRF 标记字段,以便 CSRF 保护中间件可以验证该请求,你可以使用 @csrf
Blade 指令来生成令牌字段,如下 :
<form method="POST" action="/profile">
@csrf
...
</form>
CSRF 保护:指向
web
路由文件中定义的POST
、PUT
或DELETE
路由的任何 HTML 表单都应该包含一个 CSRF 令牌字段,否则,这个请求将会被拒绝。
步骤:打开 app\Http\Kernel.php 文件,注释掉其中的 VerifyCsrfToken
中间件 :

接下来再切换方式访问就可以成功访问到了。

需要注意的是 match 方法只能访问到被设定的方式,any 方法能访问到 larave 设置的任意一种方式。
路由白名单
除了上述方式能实现除 非 get 方式 也能被访问以外,我们还可以给路由添加白名单。
首先还原我们刚刚注释的内容。被注释的其实是一个 中间件
【在执行代码得到结果之前运行的类文件,可以起到过滤作用】,根据该类的地址我们可以找到位于 app\Http\Middleware 下的 VerifyCsrfToken.php 文件。而注释掉该中间件则是去掉该中间件的过滤作用,为了安全我们可以只对我们认可的中间件解除过滤,该中间件就提供了一个白名单的功能来实现。
添加白名单
添加白名单的步骤很简单,在该中间件中我们可以看到其中存在一个 $except
属性,把我们信任的路由标识写入其中即可,例如 ,白名单添加 :

通过 post 方式访问 match 标识的结果 :

注意:这里是已经去掉了
app\Http\Kernel.php
文件中该中间件的注释,否则不是白名单起到作用,而是该中间件没有使用 。
3.2.4 视图
1. 理解视图
前面的内容中,我们都会把代码实现的内容都通过浏览器或相关工具显示出来,而框架则给我们提供了视图来对这些数据进行渲染显示。Blade
是 Laravel
提供的一个简单而又强大的 模板引擎
。和其他流行的 PHP 模板引擎不同,Blade 并不限制你在视图中使用原生 PHP 代码。所有 Blade 视图文件都将被编译成原生的 PHP 代码并缓存起来,除非它被修改,否则不会重新编译。
使用视图我们还可以得到以下好处 :
可以制定用户需求的数据,而不用看一些无用的其他数据。
对其他的数据有一定保护左右,当前用户只能查看到自己需要的数据,如果没有权限则无法查看其他数据,起到一定保护作用。
使用户的操作变得简单,复杂的操作只需要视图中的一些按钮或操作就能实现。
对复杂的数据进行分离渲染显示,不用再在众多的数据中查找自己需要的部分。
刚安装的框架给我们准备好一个视图文件,而该文件存放于 resources\views 目录下。路由中返回的 view('welcome') 则是找到该视图文件,welcome
则是视图的名称。
2. 创建视图
resources\views 目录下创建 index.blade.php 的视图文件,输入任意内容,创建该视图对应的路由 :
Route::get('view', function () {
return view('index');
});
3. 访问视图
从控制器访问到视图文件。首先在 View 目录下创建一个 IndexController 控制器 :
namespace App\Http\Controllers\View;
use App\Http\Controllers\Controller;
class IndexController extends Controller
{
public function index()
{
return view('index');# 与路由中使用效果一样
}
}
路由 :
Route::get('index', 'View\IndexController@index');
4. view 函数
从控制器访问到视图文件。首先在 View 目录下创建一个 IndexController 控制器 :
namespace App\Http\Controllers\View;
use App\Http\Controllers\Controller;
class IndexController extends Controller
{
public function index()
{
return view('index');# 与路由中使用效果一样
}
}
5. 视图方法
exists:exists
方法的作用是 判断视图文件是否存在,可以通过 view 对象调用,如果存在,exists
方法会返回 true
。
return view()->exists('index');
因为我们已经创建了 index
视图,所以结果是 true
。
share:share
方法的作用是 与所有视图共享数据。有时候,你可能需要共享一段数据给应用程序的所有视图。 你可以在 app\Providers\AppServiceProvider.php
的 boot
方法中调用视图门面(Facade)的 share
方法。例如 :
AppServiceProvider 文件:app\Providers\AppServiceProvider.php
namespace App\Providers;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
View::share(['color'=>'yellow']);
}
}
3.3 Laravel 8 数据库操作
数据库的配置文件在 config/database.php
文件中,你可以在这个文件中定义所有的数据库连接配置,并指定默认的数据库连接。这个文件中提供了大部分 Laravel 能够支持的数据库配置示例。
基本操作 :
原生语句
#\Illuminate\Support\Facades\DB 下面使用DB
DB::select(原生curd的sql语句);
新增操作
DB::table('表名')->insert(数据);
例:
DB::table('test')->insert(['name'=>'sixsatr','age'=>5]);
改
DB::table('表名')->where(条件)->update(数据);
例:
DB::table('tests')->where('id',1)->update($data);
#返回的是影响的条数。
删
DB::table('表名')->where(条件)->delete();
例:
DB::table('tests')->where('id',3)->delete();
# 返回的是条数
查
#获取所有数据
DB::table('tests')->get();
#获取具体字段
DB::table('tests')->value('name');
#获取一列
DB::table('tests')->pluck('name');
聚合
$users = DB::table('tests')->count();
$users = DB::table('tests')->avg('age');
$users = DB::table('tests')->max('age');
连接查询
DB::table('test')
# 关联表名称 本表主键 比较符 关联表外键
->join('test1','test.id','=','test1.id')
->where('test.name','sixstar')
->get();
3.4 laravel8 核心功能
3.4.1 容器概述
1 . 服务容器
服务容器 以字面意思可以分解为两个部分来理解,分别是服务与容器。
2 . 什么是 服务容器
首先看看什么是 服务,任何一个功能,任务都可以叫做服务 service。所以说功能类对象,就是服务。
再看 容器,我们有许许多多的东西需要放在一起,而装这些东西的就是一个容器。一个容器能够装什么,全部取决于你对该容器的定义。
当我们 容器中存储的不是其他东西而是服务 的话,那么这个容器就称为 服务容器 了。从此来看服务容器就是用来存放 对象、对象的描述(类、接口)或者是提供对象的回调等功能。而通过服务容器可以很好的降低代码间的耦合度。
3 . 服务绑定
你也可以使用 instance
方法将现有对象实例绑定到容器中。给定的实例会始终在随后的调用中返回到容器中 :
$test = new Test();
$this->app->instance('test', $test);
4 . 绑定接口到实现
服务容器有一个很强大的功能,就是支持绑定接口到给定的实现。例如,如果我们有个 EventPusher
接口 和一个 RedisEventPusher
实现。一旦我们写完了 EventPusher
接口的 RedisEventPusher
实现,我们就可以在服务容器中注册它,像这样 :
$this->app->bind(
'App\Contracts\Test',
'App\Services\Test'
);
5. 解析实例
通过上述方式能成功把服务保存到容器中,但是如何从容器中获取存入的服务呢?这时可以使用以下几种方式实现服务对象的获取,可以称之为解析实例。
6. make 方法
你可以使用 make
方法从容器中解析出类实例。 make
方法接收你想要解析的类或接口的名字 :
$api = $this->app->make('test');
7. resolve 方法
如果你的代码处于无法访问 $app
变量的位置,则可用全局辅助函数 resolve
来解析 :
$api = resolve('test');
3.4.2 服务提供者
服务容器以及存在并且我们可以存入和取出这些服务,那么这些服务由谁来管理呢?答案是服务提供者。
1 . 服务提供者作用
服务提供者是所有 Laravel 应用程序的引导中心。你的应用程序,以及 通过服务器引导的 Laravel 核心服务都是通过服务提供器引导【注册】。
但是,「引导」是什么意思呢? 通常,我们可以理解为注册,比如注册服务容器绑定,事件监听器,中间件,甚至是路由。服务提供者是配置应用程序的中心。
当你打开 Laravel 的 config/app.php
文件时,你会看到 providers
数组。数组中的内容是应用程序要加载的所有服务提供者的类。当然,其中有很多 「延迟」 提供者,他们并不会在每次请求的时候都加载,只有他们的服务实际被需要时才会加载。
2 . 编写服务提供者
所有的服务提供者都会继承 Illuminate\Support\ServiceProvider
类。 大多服务提供者都包含一个 register
和一个 boot
方法。在 register
方法中, 你只需要将服务绑定到服务容器。而不要尝试在 register
方法中注册任何监听器,路由,或者其他任何功能。
想要创建一个服务提供者可以通过 make:provider
命令可以生成 :
php artisan make:provider RiakServiceProvider
3 . 注册方法
在 register
方法中,你只需要将服务绑定到服务容器中。让我们来看一个基础的服务提供者。在任何服务提供者方法中,你可以通过 $app
属性来访问服务容器 :
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Utils\Test\Test;
class RiakServiceProvider extends ServiceProvider
{
/**
* 注册应用服务
*
* @return void
*/
public function register()
{
$this->app->singleton(Connection::class, function ($app) {
return new Test();
});
}
}
bindings
和 singletons
的特性
如果你的服务提供器注册了许多简单的绑定,你可能想用 bindings
和 singletons
属性替代手动注册每个容器绑定。当服务提供器被框架加载时,将自动检查这些属性并注册相应的绑定 :
<?php
namespace App\Providers;
use App\Contracts\DowntimeNotifier;
use App\Contracts\ServerProvider;
use App\Services\DigitalOceanServerProvider;
use App\Services\PingdomDowntimeNotifier;
use App\Services\ServerToolsProvider;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* 设定所有的容器绑定的对应关系
*
* @var array
*/
public $bindings = [
ServerProvider::class => DigitalOceanServerProvider::class,
];
/**
* 设定所有的单例模式容器绑定的对应关系
*
* @var array
*/
public $singletons = [
DowntimeNotifier::class => PingdomDowntimeNotifier::class,
ServerToolsProvider::class => ServerToolsProvider::class,
];
}
4 . 注册服务提供者
所有服务提供者都是通过配置文件 config/app.php
进行注册。该文件包含了一个列出所有服务提供者名字的 providers
数组,默认情况下,其中列出了所有核心服务提供者,这些服务提供者启动 Laravel 核心组件,比如邮件、队列、缓存等等。
要注册提供器,只需要将其添加到数组 :
'providers' => [
// 其他服务提供者
App\Providers\TestServiceProvider::class,
],
延迟提供者
如果你的服务提供者只在服务容器中注册,可以选择延迟加载该绑定直到注册绑定的服务真的需要时再加载,延迟加载这样的一个提供者将会提升应用的性能,因为它不会在每次请求时都从文件系统加载。
Laravel 编译并保存延迟服务提供者提供的所有服务的列表,以及其服务提供者类的名称。因此,只有当你在尝试解析其中一项服务时,Laravel 才会加载服务提供者。
要延迟加载提供者,需要实现 \Illuminate\Contracts\Support\DeferrableProvider
接口并置一个 provides
方法。这个 provides
方法返回该提供者注册的服务容器绑定 :
<?php
namespace App\Providers;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
use App\Utils\Test\Test;
class RiakServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* 注册服务提供者。
*
* @return void
*/
public function register()
{
$this->app->singleton(Test::class, function () {
return new Test();
});
}
/**
* 获取由提供者提供的服务。
*
* @return array
*/
public function provides()
{
return [Test::class];
}
}
3.4.3 Facade
Facades 为应用的 服务容器 提供了一个「静态」 接口。Laravel 自带了很多 Facades,可以访问绝大部分功能。Laravel Facades 实际是服务容器中底层类的 「静态代理」 ,相对于传统静态方法,在使用时能够提供更加灵活、更加易于测试、更加优雅的语法。
1 . Facades 使用
所有的 Laravel Facades 都定义在 Illuminate\Support\Facades
命名空间下。所以,我们可以轻松的使用 Facade :
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
2 . Facades 工作原理
在 Laravel 应用中,Facade 就是一个可以从容器访问对象的类。其中核心的部件就是 Facade
类。不管是 Laravel 自带的 Facades,还是自定义的 Facades,都继承自 Illuminate\Support\Facades\Facade
类。
Facade
基类使用了__callStatic()
魔术方法,直到对象从容器中被解析出来后,才会进行调用 :
namespace Illuminate\Support\Facades;
abstract class Facade
{
/**
* Handle dynamic, static calls to the object.
*
* @param string $method
* @param array $args
* @return mixed
*
* @throws \RuntimeException
*/
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}
return $instance->$method(...$args);
}
}
在下面的例子中,调用了 Laravel 的缓存系统。通过浏览这段代码,可以假定在 Cache
类中调用了静态方法 get
:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
class UserController extends Controller
{
/**
* 显示给定用户的信息。
*
* @param int $id
* @return Response
*/
public function showProfile($id)
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}
注意在上面这段代码中,我们『导入』了 Cache
Facade。这个 Facade 作为访问 Illuminate\Contracts\Cache\Factory
接口底层实现的代理。我们使用 Facade 进行的任何调用都将传递给 Laravel 缓存服务的底层实例。
如果我们看一下 Illuminate\Support\Facades\Cache
这个类,你会发现类中根本没有 get
这个静态方法 :
class Cache extends Facade
{
/**
* 获取组件的注册名称。
*
* @return string
*/
protected static function getFacadeAccessor() {
return 'cache';
}
}
Cache
继承了 Facade 类,并且定义了 getFacadeAccessor()
方法。这个方法的作用是返回服务容器绑定的名称。当用户调用 Cache
Facade 中的任何静态方法时,Laravel 会从服务容器中解析 cache
绑定以及该对象运行所请求的方法(在这个例子中就是 get
方法)。
3 . 自定义门面
(1)创建门面要代理的类
<?php
namespace App\Units\Test;
class Test
{
public function index()
{
return 'this is facade';
}
}
(2)创建服务提供者
php artisan make:provider TestcServiceProvider
因为门面代理是从容器中解析处理对象,所以在定义门面开始时,就需要通过服务把需要代理的类注册到容器中。
<?php
namespace App\Providers;
use App\Units\Test\Test;
use Illuminate\Support\ServiceProvider;
class TestcServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('test', function (){
return new Test();
});
}
}
(3)注册服务提供者
在 config\app.php 文件中的 providers 属性里面注册我们刚添加的服务提供者,即添加 :
'providers' => [
App\Providers\TestcServiceProvider::class,
],
(4)创建门面类
app 目录下面创建一个门面类,App\Facade\Index.php 目录随意建立,这只要 在注册的时候一致就可以。代码如下 :
<?php
namespace App\Facade;
use Illuminate\Support\Facades\Facade;
class Test extends Facade
{
protected static function getFacadeAccessor()
{
return 'test';
}
}
当然,如果不想使用服务提供者,在 getFacadeAccessor
方法中也可以直接返回被代理类的路径。
3.4.4 Contracts
契约就是接口,它的使用就是把方法中的参数类型约束设置为接口,但实际传参的时候,用的是实例化接口的类。
1 . 契约使用
Laravel 中的许多类都通过服务容器解析,包括控制器,事件监听器,中间件,队列任务,甚至路由闭包。因此,要获得契约的接口实现,你可以在要解析的类的构造函数中使用「类型提示」接口。例如 :
<?php
namespace App\Listeners;
use App\Events\OrderWasPlaced;
use Illuminate\Contracts\Redis\Factory;
class CacheOrderInformation
{
protected $redis;
public function __construct(Factory $redis)
{
$this->redis = $redis;
}
}
解析事件侦听器后,服务容器将读取类的构造函数上的类型提示,并注入适当的值,而 Factory 则是契约下的 Factory 类。
2 . 自定义契约
(1)创建契约
<?php
namespace App\Contracts\Test;
interface TestContract
{
public function contract();
}
(2)创建契约实现类
<?php
namespace App\Utils\Test;
use App\Contracts\Test\TestContract;
class TestContractUtil implements TestContract
{
public function contract()
{
dump('跟我 TestContractUtil 签订契约吧');
}
}
(3)绑定接口到实现
<?php
namespace App\Providers;
use App\Contracts\Test\TestContract;
use App\Utils\Test\TestContractUtil;
use Illuminate\Support\ServiceProvider;
class TestServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(TestContract::class,TestContractUtil::class);
}
}
注意:没有实现该契约的实现类无法使用。
(4)注册服务提供者
'providers' => [
App\Providers\TestServiceProvider::class,
],
3.4.5 中间件
中间件提供了一种方便的机制过滤进入应用程序的 HTTP 请求。要给一组路由中所有的路由分配中间件,可以在 group 之前调用 middleware
方法,中间件会依照它们在数组中列出的顺序来运行。这里我们以 First 和 Second 作为中间件名称为例。
1. 创建中间件
首先通过命令创建两个中间件分别为 :
php artisan make:middleware FirstMiddleware
php artisan make:middleware SecondMiddleware
执行成功后会在 app\Http\Middleware 目录下生成对应文件 :

中间件主要是执行其中的 handle 方法 :
FirstMiddleware 文件:app\Http\Middleware\FirstMiddleware.php
<?php
namespace App\Http\Middleware;
use Closure;
class FirstMiddleware
{
public function handle($request, Closure $next)
{
echo '中间件 First' . '<br/>';
return $next($request);
}
}
SecondMiddleware 文件:app\Http\Middleware\SecondMiddleware .php
<?php
namespace App\Http\Middleware;
use Closure;
class SecondMiddleware
{
public function handle($request, Closure $next)
{
echo '中间件 Second' . '<br/>';
return $next($request);
}
}
3. 注册中间件
在 app\Http\Kernel.php
文件把创建的中间件进行注册 :
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
# 其他代码省略 ......
protected $routeMiddleware = [
# 自定义中间件注册
'first' => \App\Http\Middleware\FirstMiddleware::class,
'second' => \App\Http\Middleware\SecondMiddleware::class,
];
}
4. 中间件使用
# 路由组中的路由都会使用到组的内容,这里不在重复写路由
# 从注册的地方可以看出,middleware 方法后面的中间件是给的注册时的别名
Route::middleware(['first', 'second'])->group(function () {
Route::get('midd', function () {
return '中间件测试';
});
});
结果 :

可以看到中间件会自动在执行该路由的时候执行。
3.5 Artisan 命令
Artisan 是 Laravel 中自带的命令行工具的名称。它提供了一些对应用开发有帮助的命令。它是由强大的 Symfony Console 组件驱动的。为了查看所有可用的 Artisan 的命令,可以使用 list 命令来列出它们 :
php artisan list
可以查看所有的命令。
Laravel 框架中也可以去执行help命令去查看如: php artisan help migrate
。
1. Artisan 命令使用
(1)利用 Artisan 工具创建控制器
如果手动去创建一个控制器, 需要自己去手写, 命名空间, 继承类等 。那么在 Laravel 中, 还可以利用 artisan 工具去执行, 如创建一个 AuthController :
php artisan make:controller AuthController
执行之后, 'app\http\Controller' 会多出一个 AuthController
如果一个项目中分前台和后台, 相应的控制器也需要放置在不同的目录下, 比如创建一个 Admin/GoodsController
php artisan make:controller Admin/GoodsController
会自动创建 Admin 文件夹, 执行之后会发现, Admin 下多出 GoodsController 的文件, 打开, 发现命名空间也自动整理好。
(2)利用 Artisan 工具查看路由
php artisan route:list
利用这条命令可以查看所有的路由。
(3)利用 Artisan 工具创建中间件
php artisan make:middleware EmailMiddleware
执行会在, middleware 文件夹下找到。
2. 其他 Artisan 命令使用
查看 artisan 命令
php artisan
php artisan list
查看某个帮助命令
php artisan help make:model
查看 laravel 版
php artisan --version
使用 PHP 内置的开发服务器启动应用
php artisan serve
生成一个随机的 key
php artisan key:generate
开启Auth用户功能(开启后需要执行迁移才生效)
php artisan make:auth
开启维护模式和关闭维护模式(显示 503)
php artisan down
php artisan up
进入tinker工具
php artisan tinker
列出所有的路由
php artisan route:list
生成路由缓存以及移除缓存路由文件
php artisan route:cache
php artisan route:clear
创建控制器
php artisan make:controller StudentController
创建 Rest 风格资源控制器(带有index、create、store、edit、update、destroy、show方法)
php artisan make:controllerPhotoController --resource
创建模型
php artisan make:model Student
创建新建表的迁移和修改表的迁移
php artisan make:migration create_users_table --create=students //创建students表
php artisan make:migration add_votes_to_users_table --table=students //给students表增加votes字段
迁移命令
执行迁移
php artisan migrate
创建模型的时候同时生成新建表的迁移
php artisan make:model Student -m
回滚上一次的迁移
php artisan migrate:rollback
回滚所有迁移
php artisan migrate:reset
创建中间件(app/Http/Middleware 下)
php artisan make:middlewareActivity
创建队列(数据库)的表迁移(需要执行迁移才生效)
php artisan queue:table
创建队列类(app/jobs下):
php artisan make:jobSendEmail
创建请求类(app/Http/Requests下)
php artisan make:request CreateArticleRequest
数据迁移(Migration)
创建迁移
php artisan make:migration create_users_table
指定路径
php artisan make:migration --path=app\providers create_users_table
数据填充(Seeder)
创建要填充的数据类
php artisan make:seeder UsersTableSeeder
数据填充(全部表)
php artisan db:seed
指定要填充的表