Laravel 5.7 复习笔记

看官请绕道: Laravel 5.7 中文文档,此篇博客是本人复习+学习笔记。

这是我自己对于Laravel文档的一次地毯式学习,时常看文档,都能从文档中学习到或多或少的知识点.

入门指南

安装 Laravel

Laravel 使用 Composer 来管理项目依赖。因此,在使用 Laravel 之前,请确保你的机器已经安装了 Composer。

1
2
3
4
5
// 通过 Laravel 安装器
composer global require "laravel/installer"

// 通过 Composer 创建项目
composer create-project --prefer-dist laravel/laravel blog

配置文件

  • 应用密钥
1
php artisan key:generate

这个字符串长度为 32 个字符。密钥可以在 .env 环境文件中设置。前提是你要将 .env.example 文件重命名为 .env

Web 服务器配置

Apache

如果 Laravel 附带的 .htaccess 文件不起作用,尝试下面的方法替代:

1
2
3
4
5
6
Options +FollowSymLinks -Indexes
RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

Nginx

1
2
3
location / {
try_files $uri $uri/ /index.php?$query_string;
}

配置信息

配置缓存

为了给你的应用程序提升速度,你应该使用 Artisan 命令 config:cache 将所有的配置文件缓存到单个文件中。
这会把你的应用程序中所有的配置选项合并成一个单一的文件,然后框架会快速加载这个文件。

1
php artisan config:cache

这个命令不应在本地开发环境下运行,因为配置选项在应用程序开发过程中是经常需要被更改的.

如果在部署过程中执行 config:cache 命令,那你应该确保只从配置文件内部调用 env 函数。
一旦配置被缓存,.env 文件将不再被加载,所有对 env 函数的调用都将返回 null

维护模式

当应用程序处于维护模式时,所有对应用程序的请求都显示为一个自定义视图。这样可以在更新或执行维护时轻松地「关闭」你的应用程序。

维护模式检查包含在应用程序的默认中间件栈中。如果应用程序处于维护模式,则将抛出一个状态码为 503 的 MaintenanceModeException 异常。

1
php artisan down

你还可以向 down 命令提供 messageretry 选项。
其中 message 选项的值可用于显示或记录自定义消息,而 retry 值可用于设置 HTTP 请求头中 Retry-After 的值:

1
php artisan down --message="Upgrading Databse" --retry=60

即使在维护模式下,也可以使用命令 allow 选项允许特定的 IP 地址或网络访问应用程序:

1
php artisan down --allow=127.0.0.1 --allow=192.168.0.0/16

要关闭维护模式,请使用 up 命令:

1
php artisan up

文件夹结构

  • app 目录

    app 目录包含应用程序的核心代码

    • Broadcasting 目录

      Broadcasting 目录包含应用程序的所有广播频道类。 这些类可以通过使用 make:channel 命令来创建。

    • Console 目录

      Console 目录包含了所有自定义的 Artisan 命令。这些命令可以通过 make:command 来生成。
      这个目录还包含了控制台内核,控制台内核可以用来注册你的自定义 Artisan 命令和你定义的 计划任务 的地方

    • Events 目录

      Events 目录默认是不存在的,它会在你运行 Artisan 命令 event:generatemake:event 时生成。
      Events 目录存放了 事件类。可以使用事件来提醒应用其他部分发生了特定的操作,使应用程序更加的灵活和解耦。

    • Exceptions 目录

      Exceptions 目录包含了应用的异常处理器,也是应用抛出异常的好地方。
      如果想自定义记录或者渲染异常的方式,你就要修改此目录下的 Handler 类。

    • Http 目录

      Http 目录包含了控制器、中间件和表单请求。几乎所有的进入应用的请求的处理逻辑都被放在这里。

    • Jobs 目录

      Jobs 目录默认是不存在的,它会在你运行 Artisan 命令 make:job 时生成。这个目录存放了应用中的 队列任务 。

    • Listeners 目录

      Listeners 目录默认是不存在的,它会在你运行 Artisan 命令 event:generatemake:listener 时生成。
      Listeners 目录包含了用来处理 事件 的类。事件监听器接收事件实例并执行响应该事件被触发的逻辑

    • Mail 目录

      Mail 目录默认不存在,它会在你运行 Artisan 命令 make:mail 时生成。 Mail 目录包含应用所有的邮件发送类。

    • Notifications 目录

      Notifications 目录默认不存在,它会在你运行 Artisan 命令 make:notification 时生成。
      Notifications 目录包含应用发送的所有「业务性」通知,比如关于在应用中发生的事件的简单通知

    • Policies 目录

      Policies 目录默认不存在,它会通过运行 Artisan 命令 make:policy 来创建。
      Policies 目录包含了应用的授权策略类。策略可以用来决定一个用户是否有权限去操作指定资源

    • Providers 目录

      Providers 目录包含了应用的所有 服务提供者 。
      服务提供者通过在服务容器中绑定服务、注册事件、以及执行其他任务来为即将到来的请求做准备来启动应用。

    • Rules 目录

      Rules 目录默认不存在,它会在运行 Artisan 命令 make:rule 命令时被创建。Rules 目录包含应用自定义验证规则对象

  • bootstrap 目录

    bootstrap 目录包含启动框架的 app.php 文件. 该目录还包含了一个 cache 目录,
    cache 目录下存放着框架生成的用来提升性能的文件,比如路由和服务缓存文件

  • config 目录

    config 目录,顾名思义,包含应用程序所有的配置文件

  • database 目录

    database 目录包含数据填充和迁移文件以及模型工厂类。你还可以把它作为 SQLite 数据库存放目录。

  • public 目录

    public 目录包含了入口文件index.php,它是进入应用程序的所有请求的入口点。
    此目录还包含了一些你的资源文件(如图片、JavaScript 和 CSS)。

  • resources 目录

    resources 目录包含了视图和未编译的资源文件(如 LESS、SASS 或 JavaScript)。此目录还包含你所有的语言文件。

  • routes 目录

    routes 目录包含了应用的所有路由定义,Laravel 默认包含了几个路由文件:web.phpapi.phpconsole.phpchannels.php

  • storage 目录

    storage 目录包含编译后的 Blade 模板、session 会话生成的文件、缓存文件以及框架生成的其他文件。
    这个目录被细分成 appframeworklogs 三个子目录

    • app目录

      app 目录可以用来存储应用生成的任何文件。

    • framework 目录

      framework 目录用来存储框架生成的文件和缓存

    • logs 目录

      logs 目录包含应用的日志文件

  • tests 目录

    tests 目录包含自动化测试文件。在 PHPUnit 有现成的范例供你参考. 每个测试类都应该以 Test 作为后缀。
    你可以使用 phpunit 或者 php vendor/bin/phpunit 命令来运行测试。

  • vendor 目录

    vendor 目录包含你所有的 Composer 依赖包。

部署优化

优化自动加载

部署项目到生产环境时,请确保你优化了 Composer 类的自动加载映射,以便 Composer 可以快速找到正确文件为给定类加载:

1
composer install --optimize-autoloader --no-dev

除了优化自动加载之外,还应该确保项目的源代码管理库中包含了 composer.lock 文件。因为当 composer.lock 文件存在时,项目的依赖项可以被更快地安装。

优化配置加载

当你将应用部署到生产环境时,请确保在部署过程中运行 Artisan 命令:

1
php artisan config:cache

这个命令可以将所有 Laravel 的配置文件合并到单个文件中缓存,此举能大大减少框架在加载配置值时必须执行的系统文件的数量。

优化路由加载

如果你构建的是具有许多路由的大型应用程序,那你应该在部署过程中运行 Artisan 命令 route:cache :

1
php artisan route:cache

核心架构

请求周期

第一件事

    1. Laravel 应用的所有请求入口都是 public/index.php 文件。而所有的请求都是经由你的 Web 服务器(Apache/Nginx)通过配置引导到这个文件。
      index.php 文件代码并不多,但是,这里是加载框架其它部分的起点。
    1. index.php 文件加载 Composer 生成的自动加载设置,然后从 bootstrap/app.php 脚本中检索 Laravel 应用程序的实例
    1. Laravel 本身采取的第一个动作是创建一个应用程序 / 服务容器

HTTP / Console内核

接下来, 根据进入应用程序的请求类型来将传入的请求发送到 HTTP 内核或控制台内核。而这两个内核是用来作为所有请求都要通过的中心位置。 现在,我们先看看位于 app/Http/Kernel.php 中的 HTTP 内核。

HTTP 内核继承了 Illuminate\Foundation\Http\Kernel 类,该类定义了一个 bootstrappers 数组。 这个数组中的类在请求被执行前运行,这些 bootstrappers 配置了错误处理, 日志, 检测应用环境,以及其它在请求被处理前需要执行的任务。

HTTP 内核还定义了所有请求被应用程序处理之前必须经过的 HTTP 中间件 ,这些中间件处理 HTTP 会话 读写、判断应用是否处于维护模式、 验证 CSRF 令牌 等等。

HTTP 内核的 handle 方法签名相当简单:获取一个 Request ,返回一个 Response。以把该内核想象作一个代表整个应用的大黑盒子,输入 HTTP 请求,返回 HTTP 响应。

服务提供者

内核启动操作中最重要的便是你应用的 服务提供者 了。所有应用下的服务提供者均配置到了 config/app.php 配置文件中的 providers 数组中。
第一步,所有服务提供者的 register 方法会被调用,然后一旦所有服务提供者均注册后, boot 方法才被调用。

请求调度

一旦启动且所有服务提供者被注册,Request 会被递送给路由。路由将会调度请求,交给绑定的路由或控制器,也当然包括路由绑定的中间件。

服务容器

绑定

绑定基础

几乎所有的服务容器绑定都是在 服务提供器, 所以文档中大多数例子都是使用了在服务提供器中绑定的容器。

简单绑定

在服务提供器中,你总是可以通过 $this->app 属性访问容器。我们可以通过容器的 bind 方法注册绑定,
bind 方法的第一个参数为要绑定的类/接口名,第二个参数是一个返回类实例的 Closure

1
2
3
$this->app->bind('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});

绑定一个单例

singleton 方法将类或接口绑定到只解析一次的容器中。一旦单例绑定被解析,相同的对象实例会在随后的调用中返回到容器中:

1
2
3
$this->app->singleton('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});

绑定实例

你也可以使用 instance 方法将现有对象实例绑定到容器中。给定的实例会始终在随后的调用中返回到容器中:

1
2
3
$api = new HelpSpot\API(new HttpClient);

$this->app->instance('HelpSpot\API', $api);

绑定基本值

当你有一个类不仅需要接受一个注入类,还需要注入一个基本值(比如整数)。你可以使用上下文绑定来轻松注入你的类需要的任何值

1
2
3
$this->app->when('App\Http\Controllers\UserController')
->needs('$variableName')
->give($value);

绑定接口到实现

服务容器有一个很强大的功能,就是支持绑定接口到给定的实现

例如,如果我们有个 EventPusher 接口 和一个 RedisEventPusher 实现。一旦我们写完了 EventPusher 接口的 RedisEventPusher 实现,我们就可以在服务容器中注册它,像这样:

1
2
3
4
$this->app->bind(
'App\Contracts\EventPusher',
'App\Services\RedisEventPusher'
);

这么做相当于告诉容器:当一个类需要实现 EventPusher 时,应该注入 RedisEventPusher
现在我们就可以在构造函数或者任何其他通过服务容器注入依赖项的地方使用类型提示注入 EventPusher 接口:

1
2
3
4
5
6
7
8
9
10
11
12
use App\Contracts\EventPusher;

/**
* 创建新的类实例
*
* @param EventPusher $pusher
* @return void
*/
public function __construct(EventPusher $pusher)
{
$this->pusher = $pusher;
}

上下文绑定

有时你可能有两个类使用了相同的接口,但你希望各自注入不同的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Illuminate\Support\Facades\Storage;
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\VideoController;
use Illuminate\Contracts\Filesystem\Filesystem;

$this->app->when(PhotoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('local');
});

$this->app->when(VideoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('s3');
});

扩展绑定

extend 方法可以修改已解析的服务。例如,当一个服务被解析后,你可以添加额外的代码去修饰或配置这个服务。
extend 方法接受一个闭包,该闭包唯一参数就是这个服务,并返回修改过的服务:

1
2
3
$this->app->extend(Service::class, function($service) {
return new DecoratedService($service);
});

解析实例

make 方法

你可以使用 make 方法从容器中解析出类实例。make 方法接受一个你想要解析的类名或接口名:

1
$api = $this->app->make('HelpSpot\API');

如果你的代码处于无法访问 $app 变量的位置,则可用全局辅助函数 resolve 来解析:

1
$api = resolve('HelpSpot\API');

如果你的类依赖不能通过容器来解析,你可以通过将它们作为关联数组传递到 makeWith 方法来注入它们:

1
$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);

自动注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php

namespace App\Http\Controllers;

use App\Users\Repository as UserRepository;

class UserController extends Controller
{
/**
* user repository 实例
*/
protected $users;

/**
* 创建一个新的控制器实例
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}

/**
* 显示给定ID的user
*
* @param int $id
* @return Response
*/
public function show($id)
{
//
}
}

容器事件

服务容器每次解析对象时会触发一个事件,你可以使用 resolving 方法监听这个事件:

1
2
3
4
5
6
7
$this->app->resolving(function ($object, $app) {
// Called when container resolves object of any type...
});

$this->app->resolving(HelpSpot\API::class, function ($api, $app) {
// Called when container resolves objects of type "HelpSpot\API"...
});

基础功能

路由

这里拿Lumen的路由组展示,因为我一般用Lumen做接口开发.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

$app->group([
'prefix' => 'test', // 路由前缀
'namespace' => 'Test', // 命名空间
'middleware' => [ // 中间件
'TestMiddleware1',
'TestMiddleware2'
],
], function () use ($app) {
$app->get('/', function () {
return response()->json([
'error' => '403'
], 403);
});

// 可用的路由方法
$app->get($uri, $callback);
$app->post($uri, $callback);
$app->put($uri, $callback);
$app->patch($uri, $callback);
$app->delete($uri, $callback);
$app->options($uri, $callback);
});

中间件

定义中间件

通过运行 make:middleware Artisan 命令来创建新的中间件:

1
php artisan make:middleware CheckAge

该命令会在 app/Http/Middleware 目录下创建一个新的CheckAge 类,在这个中间件中,我们仅允许 age 参数大于 200 的请求对此路由进行访问,否则,将重定向到 home

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

namespace App\Http\Middleware;

use Closure;

class CheckAge
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->age <= 200) {
return redirect('home');
}

return $next($request);
}
}

注册中间件

全局中间件

如果你希望中间件在应用处理每个 HTTP 请求期间运行。只需要在 app/Http/Kernel.php 中的 $middleware 属性中列出这个中间件。

为路由分配中间件

假设你想为指定的路由分配中间件,首先应该在 app/Http/Kernel.php 文件内为该中间件分配一个键。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 在 App\Http\Kernel 类中...

protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];

一旦在 HTTP 内核中定义好了中间件,就可以通过 middleware 方法将为路由分配中间件:

1
2
3
4
5
6
7
8
Route::get('admin/profile', function () {
//
})->middleware('auth');

// 你也可以为路由分配多个中间件:
Route::get('/', function () {
//
})->middleware('first', 'second');

分配中间件时,你还可以传递完整的类名

1
2
3
4
5
use App\Http\Middleware\CheckAge;

Route::get('admin/profile', function () {
//
})->middleware(CheckAge::class);

Terminable 中间件

有时中间件可能需要在 HTTP 响应构建完毕之后处理一些工作。比如,Laravel 内置的「session」中间件会在响应构建完毕之后将会话数据写入存储器中。
如果你在中间件中定义了一个 terminate 方法,则会在响应构建完毕后,准备发往浏览器之前自动调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

namespace Illuminate\Session\Middleware;

use Closure;

class StartSession
{
public function handle($request, Closure $next)
{
return $next($request);
}

public function terminate($request, $response)
{
// 储存 session 数据...
}
}

控制器

资源控制器

Laravel 资源路由将典型的「CRUD」路由分配给具有单行代码的控制器。比如,你希望创建一个控制器来处理应用保存的「照片」的所有 HTTP 请求。使用 Artisan 命令 make:controller,我们可以快速创建这样一个控制器:

1
php artisan make:controller PhotoController --resouce

这个命令会生成一个控制器 app/Http/Controllers/PhotoController.php。 其中包含了每个可用资源操作的方法。
接下来,你可以给控制器注册一个资源路由:

1
Route::resource('photos', PhotoController');

依赖注入 & 控制器

构造函数注入

1
2
3
4
public function __construct(UserRepository $users)
{
$this->users = $users;
}

方法注入

1
2
3
4
5
6
7
8
use Illuminate\Http\Request;

public function store(Request $request)
{
$name = $request->name;

//
}

路由缓存

如果你的应用只使用了基于控制器的路由,那么你应该利用路由缓存。 使用路由缓存将极大地减少注册所有应用路由所需的时间。

1
php artisan route:cache

你可以使用 route:clear 命令清除路由缓存:

1
php artisan route:clear

请求

获取请求

1
2
3
4
5
6
7
8
use Illuminate\Http\Request;

public function store(Request $request)
{
$name = $request->input('name');

//
}

通过路由闭包获取请求

1
2
3
4
5
use Illuminate\Http\Request;

Route::get('/', function (Request $request) {
//
});

获取输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 获取所有输入数据
$input = $request->all();

// 获取指定输入值
$name = $request->input('name');
// 给 input 方法的第二个参数传入一个默认值。如果请求的输入值不存在请求上,就返回默认值:
$name = $request->input('name', 'Sally');

// 从查询字符串获取输入
$name = $request->query('name');
$name = $request->query('name', 'Helen');

// 通过动态属性获取输入
$name = $request->name;

// 获取 JSON 输入信息
// 只要请求的 Content-Type 标头正确设置为 application/json,就可以通过 input 方法访问 JSON 数据
$name = $request->input('user.name');

// 获取部分输入数据
$input = $request->only(['username', 'password']);
$input = $request->only('username', 'password');
$input = $request->except(['credit_card']);
$input = $request->except('credit_card');

旧输入

1
2
3
4
5
6
7
8
9
10
11
12
13
// 将输入闪存至 Session
$request->flash();
$request->flashOnly(['username', 'email']);
$request->flashExcept('password');

// 闪存输入后重定向
return redirect('form')->withInput();
return redirect('form')->withInput(
$request->except('password')
);

// 获取旧输入
$username = $request->old('username');

文件

获取上传文件

1
2
3
4
5
6
7
$file = $request->file('photo');
$file = $request->photo;

// 使用 hasFile 方法确认请求中是否存在文件:
if ($request->hasFile('photo')) {
//
}

验证成功上传

1
2
3
if ($request->file('photo')->isValid()) {
//
}

文件路径 & 扩展名

1
2
$path = $request->photo->path();
$extension = $request->photo->extension();

响应

创建响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 字符串 & 数组
Route::get('/', function () {
return 'Hello World';
});

Route::get('/', function () {
return [1, 2, 3];
});

// 响应对象
Route::get('home', function () {
return response('Hello World', 200)
->header('Content-Type', 'text/plain');
});

// 为响应增加头信息
return response($content)
->header('Content-Type', $type)
->header('X-Header-One', 'Header Value')
->header('X-Header-Two', 'Header Value');
// 或者
return response($content)
->withHeaders([
'Content-Type' => $type,
'X-Header-One' => 'Header Value',
'X-Header-Two' => 'Header Value',
]);
// 为响应增加 Cookie
return response($content)
->header('Content-Type', $type)
->cookie('name', 'value', $minutes);

// 重定向至命名路由
return redirect()->route('login');
return redirect()->route('profile', ['id' => 1]);

// 重定向至控制器行为
return redirect()->action('HomeController@index');

// 重定向到外部域
return redirect()->away('https://www.google.com');

// 重定向并使用闪存的 Session 数据
return redirect('dashboard')->with('status', 'Profile updated!');

视图响应

1
2
3
return response()
->view('hello', $data, 200)
->header('Content-Type', $type);

JSON 响应

1
2
3
4
5
6
7
8
9
return response()->json([
'name' => 'Abigail',
'state' => 'CA'
]);

// JSONP 响应
return response()
->json(['name' => 'Abigail', 'state' => 'CA'])
->withCallback($request->input('callback'));

文件下载

download 方法可以用来生成强制用户浏览器下载指定路径文件的响应。
download 方法的第二个参数接受一个文件名,它将作为用户下载的时所看见的文件名。
最后,你可以传递一个 HTTP 响应头数组作为该方法的第三个参数:

1
2
3
return response()->download($pathToFile);
return response()->download($pathToFile, $name, $headers);
return response()->download($pathToFile)->deleteFileAfterSend(true);

流式下载

有时,你可能希望将给定操作的字符串响应转换为可下载的响应,而无需将操作的内容写入磁盘。 你可以在这个场景中使用 streamDownload 方法。 此方法接受回调,文件名和可选的标题数组作为其参数:

1
2
3
4
5
return response()->streamDownload(function () {
echo GitHub::api('repo')
->contents()
->readme('laravel', 'laravel')['contents'];
}, 'laravel-readme.md');

File 响应

file 方法可以直接在用户浏览器中显示文件(不是发起下载),例如图像或者 PDF。此方法接受文件的路径作为其第一个参数和头信息数组作为其第二个参数:

1
2
return response()->file($pathToFile);
return response()->file($pathToFile, $headers);

响应宏

如果要定义可以在各种路由和控制器中重复使用的自定义响应,可以在 Response Facade 上使用 macro 方法。例如,你可以在 服务提供器 的 boot 方法中这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Response;

class ResponseMacroServiceProvider extends ServiceProvider
{
/**
* 注册应用程序的响应宏
*
* @return void
*/
public function boot()
{
Response::macro('caps', function ($value) {
return Response::make(strtoupper($value));
});
}
}

macro 函数接受一个名称作为其第一个参数,闭包作为第二个参数。当宏名称从 ResponseFactory 实现或者辅助函数 response 调用时,其闭包函数才会被执行:

1
return response()->caps('foo');

Session

配置

Session driver 的配置预设了每个请求存储 Session 数据的位置。Laravel 自带了几个不错而且开箱即用的驱动:

  • file - 将 Session 存储在 storage/framework/sessions 中。
  • cookie - Sessions 被存储在安全加密的 cookie 中。
  • database - Sessions 被存储在关系型数据库中。
  • memcached / redis - Sessions 被存储在基于高速缓存的存储系统中。
  • array - Sessions 存储在 PHP 数组中,但不会被持久化。

使用Session

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 获取数据
$value = $request->session()->get('key');

// 全局辅助函数 Session
// 获取 session 中的一条数据...
$value = session('key')

// 指定一个默认值...
$value = session('key', 'default');

// 在 Session 中存储一条数据...
session(['key' => 'value']);

// 获取所有的 Session 数据
$data = $request->session()->all();

// 判断 Session 中是否存在某个值
if ($request->session()->has('users')) {
//
}

// 检索 & 删除一条数据
$value = $request->session()->pull('key', 'default');

// 闪存数据
$request->session()->flash('status', 'Task was successful!');

// 删除数据
$request->session()->forget('key');
$request->session()->flush();

// 重新生成 Session ID
$request->session()->regenerate();

表单验证

编写验证器逻辑

。为此,我们将使用 Illuminate\Http\Request 对象提供的 validate 方法 。
如果验证通过,代码就可以正常的运行。如果验证失败,则会抛出异常,并自动将对应的错误响应返回给用户。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public function store(Request $request)
{
$validatedData = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);

// 博客文章验证通过
}

// 首次验证失败后停止运行
// 希望在某个属性第一次验证失败后停止运行验证规则,你需要附加 bail 规则到该属性:
$request->validate([
'title' => 'bail|required|unique:posts|max:255',
'body' => 'required',
]);

验证表单请求

创建表单请求验证

1
php artisan make:request StoreBlogPost

新生成的类保存在 app/Http/Requests 目录下。如果这个目录不存在,运行 make:request 命令时它会被创建出来。让我们添加一些验证规则到 rules 方法中:

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 获取适用于请求的验证规则。
*
* @return array
*/
public function rules()
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
];
}

你可以向 rules 方法传入所需的任何依赖项。他们会自动被 Laravel 提供的 服务容器 自动解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 存储传入的博客文章。
*
* @param StoreBlogPost $request
* @return Response
*/
public function store(StoreBlogPost $request)
{
// 传入的请求通过验证...

// 获取通过验证的数据...
$validated = $request->validated();
}

如果验证失败,就会生成一个让用户返回到先前的位置的重定向响应。这些错误也会被闪存到 session 中,以便这些错误都可以在页面中显示出来。如果传入的请求是 AJAX,会向用户返回具有 422 状态代码和验证错误信息的 JSON 数据的 HTTP 响应。

添加表单请求后钩子

如果你想在表单请求「之后」添加钩子,可以使用 withValidator 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 配置验证器实例。
*
* @param \Illuminate\Validation\Validator $validator
* @return void
*/
public function withValidator($validator)
{
// 验证后钩子
$validator->after(function ($validator) {
if ($this->somethingElseIsInvalid()) {
$validator->errors()->add('field', 'Something is wrong with this field!');
}
});
}

表单请求授权验证

表单请求类内也包含了 authorize 方法。在这个方法中,你可以检查经过身份验证的用户确定其是否具有更新给定资源的权限。比方说,你可以判断用户是否拥有更新文章评论的权限:

1
2
3
4
5
6
7
8
9
10
11
/**
* 判断用户是否有权限做出此请求。
*
* @return bool
*/
public function authorize()
{
$comment = Comment::find($this->route('comment'));

return $comment && $this->user()->can('update', $comment);
}

如果 authorize 方法返回 false,则会自动返回一个包含 403 状态码的 HTTP 响应,也不会运行控制器的方法。

如果你打算在应用程序的其它部分处理授权逻辑,只需从 authorize 方法返回 true

1
2
3
4
5
6
7
8
9
/**
* 判断用户是否有权限进行此请求。
*
* @return bool
*/
public function authorize()
{
return true;
}

自定义错误消息

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Get the error messages for the defined validation rules.
*
* @return array
*/
public function messages()
{
return [
'title.required' => 'A title is required',
'body.required' => 'A message is required',
];
}

手动创建验证器

如果你不想在请求上使用 validate 方法,你可以通过 Validator facade 手动创建一个验证器示例。
Validator facade 上的 make 方法创建一个验证器示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

namespace App\Http\Controllers;

use Validator;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PostController extends Controller
{
/**
* Store a new blog post.
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);

if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}

// Store the blog post...
}
}

错误

Report 辅助函数

有时你可能需要报告异常,但又不希望终止当前请求的处理。
report 辅助函数允许你使用异常处理器的 report 方法在不显示错误页面的情况下快速报告异常:

1
2
3
4
5
6
7
8
9
10
public function isValid($value)
{
try {
// 验证值...
} catch (Exception $e) {
report($e);

return false;
}
}

Render 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 将异常l转换为 HTTP 响应。
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
if ($exception instanceof CustomException) {
return response()->view('errors.custom', [], 500);
}

return parent::render($request, $exception);
}

Reportable & Renderable 异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php

namespace App\Exceptions;

use Exception;

class RenderException extends Exception
{
/**
* 报告异常
*
* @return void
*/
public function report()
{
//
}

/**
* 转换异常为 HTTP 响应
*
* @param \Illuminate\Http\Request
* @return \Illuminate\Http\Response
*/
public function render($request)
{
return response(...);
}
}

HTTP 异常

1
2
abort(404);
abort(403, 'Unauthorized action.');

日志

记录日志消息

1
2
3
4
5
6
7
8
9
10
use Illuminate\Support\Facades\Log;

Log::emergency($message);
Log::alert($message);
Log::critical($message);
Log::error($message);
Log::warning($message);
Log::notice($message);
Log::info($message);
Log::debug($message);

记录日志到指定通道

你可以使用 Log Facade 中的 channel 方法,将日志记录到应用配置中存在的任何渠道:

1
Log::channel('slack')->info('Something happened!');

如果你想按需创建多个渠道的日志堆栈,你可以使用 stack 方法:

1
Log::stack(['single', 'slack'])->info('Something happened!');

安全相关

综合话题

数据库

Eloquent ORM

测试相关

官方扩展包

Powered by Hexo and Hexo-theme-hiker

Copyright © 2017 - 2021 Keep It Simple And Stupid All Rights Reserved.

访客数 : | 访问量 :