欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

模型关联 响应 中间件 登录 表单验证

程序员文章站 2022-03-13 12:51:18
...

模型关联

数据库表通常相互关联。例如,一篇博客文章可能有许多评论,或者一个订单对应一个下单用户。Eloquent 让这些关联的管理和使用变得简单,并支持多种常用的关联类型:

  • [一对一]
  • [一对多]

定义关联

Eloquent 关联在 Eloquent 模型类中以方法的形式呈现。如同 模型本身,关联也可以作为强大的 查询语句构造器 使用,提供了强大的链式调用和查询功能。例如,我们可以在 posts 关联的链式调用中附加一个约束条件:

  1. $user->posts()->where('active', 1)->get();

一对一

一对一是最基本的数据库关系。例如,一个 User 模型可能与一个 Phone 模型相关联。为了定义这个关联关系,我们要在 User 模型中写一个 phone 方法,在 phone 方法中调用 hasOne 方法并返回其结果。hasOne 方法被定义在 Illuminate\Database\Eloquent\Model 这个模型基类中:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class User extends Model
  5. {
  6. /**
  7. * 获取与用户相关的电话记录
  8. */
  9. public function phone()
  10. {
  11. return $this->hasOne(Phone::class);
  12. }
  13. }

hasOne 方法的第一个参数是关联模型的类名。一旦定义了模型关联,我们就可以使用 Eloquent 的动态属性获得相关的记录。动态属性允许你访问该关联方法,就像访问模型中定义的属性一样:

  1. $phone = User::find(1)->phone;

我们来试一下:

  1. php artisan make:migration create_phone_table

先创建迁移文件

  1. Schema::create('phone', function (Blueprint $table) {
  2. $table->id();
  3. $table->bigInteger('user_id')->comment('用户ID');
  4. $table->string('phone',13)->comment('手机号');
  5. $table->timestamps();
  6. });

我们在 phone表里加上条数据,来供我们测试。

接着我们需要创建一个Phone的模型

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Factories\HasFactory;
  4. use Illuminate\Database\Eloquent\Model;
  5. class Phone extends Model
  6. {
  7. use HasFactory;
  8. protected $table = 'phone';
  9. }

来测试一下

  1. User::find('1')->phone;

定义反向关联

我们已经能从 User 模型访问到 Phone 模型了。接下来,让我们再在 Phone 模型上定义一个关联,它能让我们访问到拥有该电话的用户。我们可以使用 belongsTo 方法来定义反向关联, belongsTo 方法与 hasOne 方法相对应:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Factories\HasFactory;
  4. use Illuminate\Database\Eloquent\Model;
  5. class Phone extends Model
  6. {
  7. use HasFactory;
  8. protected $table = 'phone';
  9. /**
  10. * 获取拥有此电话的用户
  11. */
  12. public function user()
  13. {
  14. return $this->belongsTo(User::class);
  15. }
  16. }

接着我们可以来查询

  1. Phone::where('phone','1888888888')->with('user')->first();

在调用 user 方法时,Eloquent 会尝试查找一个 User 模型,该 User 模型上的 id 字段会与 Phone 模型上的 user_id 字段相匹配。

Eloquent 通过关联方法(user)的名称并使用 _id 作为后缀名来确定外键名称。因此,在本例中,Eloquent 会假设 Phone 模型有一个 user_id 字段。但是,如果 Phone 模型的外键不是 user_id,这时你可以给 belongsTo 方法的第二个参数传递一个自定义键名:

  1. /**
  2. * 获取拥有此电话的用户
  3. */
  4. public function user()
  5. {
  6. return $this->belongsTo(User::class, 'foreign_key');
  7. }

一对多

当要定义一个模型是其他 (一个或者多个)模型的父模型这种关系时,可以使用一对多关联。例如,一篇博客可以有很多条评论。和其他模型关联一样,一对多关联也是在 Eloquent 模型文件中用一个方法来定义的:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class Post extends Model
  5. {
  6. /**
  7. * 获取这篇博客的所有评论
  8. */
  9. public function comments()
  10. {
  11. return $this->hasMany(Comment::class);
  12. }
  13. }

注意,Eloquent 将会自动为 Comment 模型选择一个合适的外键。通常,这个外键是通过使用父模型的「蛇形命名」方式,然后再加上 _id 的方式来命名的。因此,在上面这个例子中,Eloquent 将会默认 Comment 模型的外键是 post_id 字段。

如果关联方法被定义,那么我们就可以通过 comments 属性来访问相关的评论 集合。注意,由于 Eloquent 提供了「动态属性」,所以我们就可以像访问模型属性一样来访问关联方法:

  1. use App\Models\Post;
  2. $comments = Post::find(1)->comments;
  3. foreach ($comments as $comment) {
  4. //
  5. }

由于所有的关系都可以看成是查询构造器,所以您也可以通过链式调用的方式,在 comments 方法中继续添加条件约束:

  1. $comment = Post::find(1)->comments()
  2. ->where('title', 'foo')
  3. ->first();

hasOne 方法一样,hasMany 方法中也可以接受额外的参数,从而来覆盖外键和本地键:

  1. return $this->hasMany(Comment::class, 'foreign_key');
  2. return $this->hasMany(Comment::class, 'foreign_key', 'local_key');

一对多 (反向) / Belongs To

目前我们可以访问一篇博客的所有评论,下面我们可以定义一个关联关系,从而让我们可以通过一条评论来获取到它所属的博客。这个关联关系是 hasMany 的反向,可以子模型中通过 belongsTo 方法来定义这种关联关系:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class Comment extends Model
  5. {
  6. /**
  7. * 获取这条评论所属的博客。
  8. */
  9. public function post()
  10. {
  11. return $this->belongsTo(Post::class);
  12. }
  13. }

如果定义了这种关联关系,那么我们就可以通过 Comment 模型中的 post 「动态属性」来获取到这条评论所属的博客:

  1. use App\Models\Comment;
  2. $comment = Comment::find(1);
  3. return $comment->post->title;

在上面这个例子中,Eloquent 将会尝试寻找 Post 模型中的 id 字段与 Comment 模型中的 post_id 字段相匹配。

Eloquent 通过检查关联方法的名称,从而在关联方法名称后面加上 _ ,然后再加上父模型 (Post)的主键名称,以此来作为默认的外键名。因此,在上面这个例子中,Eloquent 将会默认 Post 模型在 comments 表中的外键是 post_id

但是,如果您的外键不遵循这种约定的话,那么您可以传递一个自定义的外键名来作为 belongsTo 方法的第二个参数:

  1. /**
  2. * 获取这条评论所属的博客。
  3. */
  4. public function post()
  5. {
  6. return $this->belongsTo(Post::class, 'foreign_key');
  7. }

如果您的父表(Post 表)不使用 id 来作为它的主键的话,或者您希望通过其他列来关联相关模型的话,那么您可以传递一个参数来作为 belongsTo 方法的第三个参数,这个参数是父表(Post 表)中想要作为关联关系的字段的名称。

  1. /**
  2. * 获取这条评论所属的博客。
  3. */
  4. public function post()
  5. {
  6. return $this->belongsTo(Post::class, 'foreign_key', 'owner_key');
  7. }

响应

参考手册 响应

我们前面学习了,路由、控制器、模型、视图,现在我们在来学习响应,整个请求的生命周期,我们就学习完了。

掌握内容

  • 字符串 & 数组
  • 添加响应头
  • 重定向
  • 重定向并使用闪存的 Session 数据
  • 视图响应
  • JSON响应

创建响应

字符串 & 数组

所有路由和控制器处理完业务逻辑之后都会返回响应到用户的浏览器,Laravel 提供了多种不同的响应方式,其中最基本就是从路由或控制器返回一个简单的字符串,框架会自动将这个字符串转化为一个完整的 HTTP 响应:

  1. Route::get('/', function () {
  2. return 'Hello World';
  3. });

除了从路由或控制器返回字符串之外,还可以返回数组。框架会自动将数组转化为一个 JSON 响应:

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

Response 对象

通常情况下会只返回简单的字符串或数组,大多数时候,需要返回一个完整的 Illuminate\Http\Response 实例或是 视图

返回一个完整的 Response 实例允许你自定义返回的 HTTP 状态码和返回头信息。Response 实例继承自 Symfony\Component\HttpFoundation\Response 类,该类提供了各种构建 HTTP 响应的方法:

  1. Route::get('/home', function () {
  2. return response('Hello World', 200)
  3. ->header('Content-Type', 'text/plain');
  4. });

Eloquent 模型 和 集合

你也可以直接从你的路由和控制器返回 Eloquent ORM 模型和集合。当你这样做时,Laravel 将自动将模型和集合转换为 JSON 响应,同时遵循模型的 隐藏属性

  1. use App\Models\User;
  2. Route::get('/user/{user}', function (User $user) {
  3. return $user;
  4. });

重定向

重定向响应是 Illuminate\Http\RedirectResponse 类的实例,包含将用户重定向到另一个 URL 所需的适当 HTTP 头。Laravel 有几种方法可以生成 RedirectResponse 实例。最简单的方法是使用全局 redirect 助手函数

  1. Route::get('/dashboard', function () {
  2. return redirect('home/dashboard');
  3. });

有时你可能希望将用户重定向到以前的位置,例如当提交的表单无效时。你可以使用全局 back 助手函数来执行此操作。由于此功能使用 session,请确保调用 back 函数的路由使用的是 web 中间件组:

  1. Route::post('/user/profile', function () {
  2. // 验证请求参数
  3. return back()->withInput();
  4. });

重定向到指定名称的路由

当你在没有传递参数的情况下调用 redirect 助手函数时,将返回 illighte\Routing\Redirector 的实例,允许你调用 Redirector 实例上的任何方法。例如,要对命名路由生成 RedirectResponse ,可以使用 route 方法:

  1. return redirect()->route('login');

如果路由中有参数,可以将其作为第二个参数传递给 route 方法:

  1. // 对于具有以下URI的路由:profile/{id}
  2. return redirect()->route('profile', ['id' => 1]);

通过 Eloquent 模型填充参数

如果你要重定向到使用从 Eloquent 模型填充「ID」参数的路由,可以直接传递模型本身。ID 将会被自动提取:

  1. // 对于具有以下URI的路由: profile/{id}
  2. return redirect()->route('profile', [$user]);

如果你想要自定义路由参数,你可以指定路由参数 (/profile/{id:slug}) 或者重写 Eloquent 模型上的 getRouteKey 方法:

  1. /**
  2. * 获取模型的路由键值
  3. *
  4. * @return mixed
  5. */
  6. public function getRouteKey()
  7. {
  8. return $this->slug;
  9. }

重定向到控制器行为

也可以生成重定向到 controller actions。只要把控制器和 action 的名称传递给 action 方法:

  1. use App\Http\Controllers\UserController;
  2. return redirect()->action([UserController::class, 'index']);

如果控制器路由有参数,可以将其作为第二个参数传递给 route 方法:

  1. return redirect()->action(
  2. [UserController::class, 'profile'], ['id' => 1]
  3. );

重定向到外部域名

有时候你需要重定向到应用外的域名。可以通过调用 away 方法,它会创建一个不带有任何额外的 URL 编码、有效性校验和检查 RedirectResponse 实例:

  1. return redirect()->away('https://www.google.com');

重定向并使用闪存的 Session 数据

重定向到新的 URL 的同时 传送数据给 seesion 是很常见的。 通常这是在你将消息发送到 session 后成功执行操作后完成的。为了方便,你可以创建一个 RedirectResponse 实例并在链式方法调用中将数据传送给 session :

  1. Route::post('/user/profile', function () {
  2. // ...
  3. return redirect('dashboard')->with('status', 'Profile updated!');
  4. });

在用户重定向之后,你可以显示 session 中的传送数据。例如,你可以使用 Blade 模板语法:

  1. @if (session('status'))
  2. <div class="alert alert-success">
  3. {{ session('status') }}
  4. </div>
  5. @endif

视图响应

如果需要控制响应的 HTTP 状态和 HTTP 头,但还需要将 视图 作为响应的内容返回,则应使用 view 方法:

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

当然,如果不需要传递自定义 HTTP 状态码或自定义头,你可以使用全局 view 助手函数。

JSON 响应

json 方法将自动将 Content Type 头设置为 application/json ,并使用 json_encode PHP 函数将给定数组转换为 json :

  1. return response()->json([
  2. 'name' => 'Abigail',
  3. 'state' => 'CA',
  4. ]);

中间件

在做用户信息修改的时候,只有当用户登录之后才能修改,如果没登录的情况下,直接利用地址访问修改信息页面,应当将该用户的请求重定向到登录页面,在这种情况下,可以使用框架中的中间件的概念来达到我们的目的。

中间件可以对请求进行过滤,这里可以利用中间件来验证用户是否登录,如果用户登录则可以继续执行原先想执行的操作,如果没登录则重定向到登录页面,让用户先登录。(类似于门口保安,可以对进入车辆做检查拦截)

定义中间件

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

  1. php artisan make:middleware EnsureTokenIsValid

此命令将在 app/Http/Middleware 目录中放置一个新的 EnsureTokenIsValid 类。在这个中间件中,我们只允许在提供的 token 输入与指定值匹配时访问路由。否则将重定向到 home 页面:

  1. <?php
  2. namespace App\Http\Middleware;
  3. use Closure;
  4. class EnsureTokenIsValid
  5. {
  6. /**
  7. * Handle an incoming request.
  8. *
  9. * @param \Illuminate\Http\Request $request
  10. * @param \Closure $next
  11. * @return mixed
  12. */
  13. public function handle($request, Closure $next)
  14. {
  15. if ($request->input('token') !== 'my-secret-token') {
  16. return redirect('home');
  17. }
  18. return $next($request);
  19. }
  20. }

正如你所见,如果给定的令牌与我们的秘密令牌不匹配,中间件将返回一个 HTTP 重定向给客户端。否则这个请求将会通过,进一步传递到应用层中。要让请求继续传到到应用层中 (即允许「通过」中间件验证), 只需要将 $request 作为参数来调用函数 $next 即可。

最好将中间件想象成一系列层次,HTTP 请求必须通过它们才能进入你的应用层。每一层都会检查请求(是否符合中间件要求),而后决定通过或拒绝访问应用。

前置 & 后置 中间件

中间件是在请求之前或之后执行,取决于中间件本身。例如,下面的中间件将在应用处理请求之前执行一些任务:

  1. <?php
  2. namespace App\Http\Middleware;
  3. use Closure;
  4. class BeforeMiddleware
  5. {
  6. public function handle($request, Closure $next)
  7. {
  8. // Perform action
  9. return $next($request);
  10. }
  11. }

然而,下面中间件是在应用请求之后执行一些任务:

  1. <?php
  2. namespace App\Http\Middleware;
  3. use Closure;
  4. class AfterMiddleware
  5. {
  6. public function handle($request, Closure $next)
  7. {
  8. $response = $next($request);
  9. // Perform action
  10. return $response;
  11. }
  12. }

注册中间件

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

为路由分配中间件

假设你想为指定的路由分配中间件,首先应该在 app/Http/Kernel.php 文件内为该中间件分配一个键。默认情况下,该类中的 $routeMiddleware 属性下包含了 Laravel 内置的中间件。若要加入自定义的中间件,只需把它附加到列表后并为其分配一个自定义键。例如:

  1. // Within App\Http\Kernel class...
  2. protected $routeMiddleware = [
  3. 'auth' => \App\Http\Middleware\Authenticate::class,
  4. 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
  5. 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
  6. 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
  7. 'can' => \Illuminate\Auth\Middleware\Authorize::class,
  8. 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
  9. 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
  10. 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
  11. 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
  12. ];

在 HTTP 内核中定义了中间件后,您可以使用 middleware 方法将中间件分配给路由:

  1. Route::get('/profile', function () {
  2. //
  3. })->middleware('auth');

您还可以将多个中间件分配给路由:

  1. Route::get('/', function () {
  2. //
  3. })->middleware(['first', 'second']);

中间件组

有时,您可能希望将多个中间件归为一个键,以使其更易于分配给路由。 您可以使用 HTTP 内核的 $middlewareGroups 属性来实现。
Laravel 开箱即用,带有 webapi 中间件组,其中包含您可能要应用于 Web UI 和 API 路由的通用中间件,请记住,这些中间件组由应用程序的 App\Providers\RouteServiceProvider 服务提供商自动应用于相应的 webapi 路由文件中的路由:

  1. /**
  2. * The application's route middleware groups.
  3. *
  4. * @var array
  5. */
  6. protected $middlewareGroups = [
  7. 'web' => [
  8. \App\Http\Middleware\EncryptCookies::class,
  9. \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
  10. \Illuminate\Session\Middleware\StartSession::class,
  11. // \Illuminate\Session\Middleware\AuthenticateSession::class,
  12. \Illuminate\View\Middleware\ShareErrorsFromSession::class,
  13. \App\Http\Middleware\VerifyCsrfToken::class,
  14. \Illuminate\Routing\Middleware\SubstituteBindings::class,
  15. ],
  16. 'api' => [
  17. 'throttle:api',
  18. \Illuminate\Routing\Middleware\SubstituteBindings::class,
  19. ],
  20. ];

中间件组可以使用与单个中间件相同的语法将自身分配给路由和控制器动作。同样,中间件组使得一次将多个中间件分配给一个路由更加方便:

  1. Route::get('/', function () {
  2. //
  3. })->middleware('web');
  4. Route::middleware(['web'])->group(function () {
  5. //
  6. });

技巧:RouteServiceProvider 默认将 webapi 中间件组自动应用到 routes/web.phproutes/api.php

用户登录实战

Laravel的入门套件已经帮我们完成了登录所需的所有步骤,接下来我们使用Laravel的入门套件来快速实现登录功能。

  1. composer require laravel/breeze --dev

安装好 Laravel Breeze 扩展包后,可以运行 breeze:installArtisan 命令,该命令会发布认证视图、路由和控制器等资源到项目目录,这样一来,就可以完全接管这些认证代码的功能实现和自定义了。此外,还需要编译前端资源让 JavaScript 和 CSS 代码生效:

  1. php artisan breeze:install
  2. npm install
  3. npm run dev
  4. php artisan migrate

接下来,你就可以在浏览器中访问 /login 或者 /register 了。所有的 Breeze 路由都定义在 routes/auth.php 文件中。

既然我们已经完成了登录功能,那么我们来尝试一下 只有登录以后才能访问的路由。

  1. Route::middleware('auth')->group(function () {
  2. Route::get('/', function () {
  3. return view('welcome');
  4. });
  5. });

表单验证

参考手册 表单验证

Laravel 提供了几种不同的方法来验证传入应用程序的数据。最常见的做法是在所有传入的 HTTP 请求中使用 validate 方法。但是,我们还将讨论其他验证方法。

快速验证

为了了解 Laravel 强大的验证功能,我们来看一个表单验证并将错误消息展示给用户的完整示例。通过阅读概述,这将会对你如何使用 Laravel 验证传入的请求数据有一个很好的理解:

定义路由

首先,假设我们在 routes/web.php 路由文件中定义了下面这些路由:

  1. use App\Http\Controllers\PostController;
  2. Route::get('/post/create', [PostController::class, 'create']);
  3. Route::post('/post', [PostController::class, 'store']);

GET 路由会显示一个供用户创建新博客文章的表单,而 POST 路由会将新的博客文章存储到数据库中。

创建控制器

接下来,让我们一起来看看处理这些路由的简单控制器。我们暂时留空了 store 方法:

  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Controllers\Controller;
  4. use Illuminate\Http\Request;
  5. class PostController extends Controller
  6. {
  7. /**
  8. * 显示创建博客文章的表单。
  9. *
  10. * @return \Illuminate\View\View
  11. */
  12. public function create()
  13. {
  14. return view('post.create');
  15. }
  16. /**
  17. * 存储一篇新的博客文章。
  18. *
  19. * @param \Illuminate\Http\Request $request
  20. * @return \Illuminate\Http\Response
  21. */
  22. public function store(Request $request)
  23. {
  24. // 验证并存储博客文章...
  25. }
  26. }

简单编写一个模板

  1. <x-layout>
  2. <div>
  3. <form method="post" action="/post">
  4. <input type="text" name="title">
  5. <textarea name="body"></textarea>
  6. <input type="submit">
  7. </form>
  8. </div>
  9. </x-layout>

编写验证逻辑

现在我们开始在 store 方法中编写用来验证新的博客文章的逻辑代码。为此,我们将使用 Illuminate\Http\Request 类提供的 validate 方法。如果验证通过,你的代码会继续正常运行。如果验证失败,则会抛出异常,并自动将对应的错误响应返回给用户。

为了深入理解 validate 方法,让我们接着回到 store 方法中:

  1. /**
  2. * 存储一篇新的博客文章。
  3. *
  4. * @param \Illuminate\Http\Request $request
  5. * @return \Illuminate\Http\Response
  6. */
  7. public function store(Request $request)
  8. {
  9. $validated = $request->validate([
  10. 'title' => 'required|unique:posts|max:255',
  11. 'body' => 'required',
  12. ]);
  13. // 博客文章验证通过...
  14. }

unique:posts 在我们 posts表中这个标题只能存在一次,因为我们还没有表,所以这个可以先删除掉。

如果验证失败,会自动生成一个对应的响应,如果验证通过,那我们的控制器会继续正常运行。

验证规则可以使用数组而不是单个 | 分隔的字符串:

  1. $validatedData = $request->validate([
  2. 'title' => ['required', 'unique:posts', 'max:255'],
  3. 'body' => ['required'],
  4. ]);

显示验证错误信息

那么,如果传入的请求参数未通过给定的验证规则呢?正如前面所提到的,Laravel 会自动将用户重定向到之前的位置。所以,在如下的例子中,当表单验证失败时,用户将被重定向到控制器的 create 方法中,我们可在视图中显示错误信息:

  1. @if ($errors->any())
  2. <div class="alert alert-danger" style="width:100%">
  3. <ul>
  4. @foreach ($errors->all() as $error)
  5. <li>{{ $error }}</li>
  6. @endforeach
  7. </ul>
  8. </div>
  9. @endif

为了提高用户体验,另外,所有的验证错误信息和 请求输入 都将自动存储到 闪存 session 中。我们可以将用户的上次的输入现实到出入框中。

  1. <form method="post" action="/post">
  2. <input type="text" name="title" value="{{request()->old('title')}}">
  3. <textarea name="body">{{old('body')}}</textarea>
  4. <input type="submit">
  5. </form>

自定义错误消息

你会发现错误消息的提示是英文的,Laravel 的内置验证规则每个都有一条错误消息,位于应用程序的 resources/lang/en/validation.php 文件中。在此文件中,你将找到每个验证规则的翻译条目。你可以根据应用程序的需求随意更改或修改这些消息。

已经有开发者帮我们写好了扩展包,我们来安装使用一下。

  1. composer require overtrue/laravel-lang:~5.0

然后我们修改config/app.php

  1. 'locale' => 'zh_CN',

这样我们就完成了,再去让他显示一下错误消息。

运行这个命令可以将对应的语言包发布出来。

  1. php artisan lang:publish zh_CN

验证表单请求

创建表单请求验证

​ 面对更复杂的情况,你可以创建一个「表单请求」来应对更复杂的验证逻辑。表单请求是一个包含了验证逻辑的自定义请求类。要创建一个表单请求类,请使用 make:request Artisan CLI 命令:

  1. php artisan make:request StorePostRequest

该命令生成的类将被置于 app/Http/Requests 目录中。如果这个目录不存在,在您运行 make:request 命令后将会创建这个目录。Laravel 生成的每个表单请求都有两种方法:authorizerules

你可能已经猜到了,authorize 方法负责确定当前经过身份验证的用户是否可以执行请求操作,而 rules 方法则返回适用于请求数据的验证规则:

  1. <?php
  2. namespace App\Http\Requests;
  3. use Illuminate\Foundation\Http\FormRequest;
  4. class StorePostRequest extends FormRequest
  5. {
  6. /**
  7. * 确定用户是否有权提出此请求。
  8. *
  9. * @return bool
  10. */
  11. public function authorize()
  12. {
  13. return true;
  14. }
  15. /**
  16. * 获取应用于请求的验证规则。
  17. *
  18. * @return array
  19. */
  20. public function rules()
  21. {
  22. return [
  23. 'title' => 'required|max:255',
  24. 'body' => 'required',
  25. ];
  26. }
  27. }

现在那我们需要去修改控制器,在控制器中使用我们刚刚创建的验证类,

  1. /**
  2. * 存储新的博客文章。
  3. *
  4. * @param \App\Http\Requests\StorePostRequest $request
  5. * @return Illuminate\Http\Response
  6. */
  7. public function store(StorePostRequest $request)
  8. {
  9. // 传入的请求通过验证...
  10. // 获取通过验证的数据...
  11. $validated = $request->validated();
  12. }

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

自定义错误消息

你可以通过重写表单请求的 messages 方法来自定义错误消息。此方法应返回属性 / 规则对及其对应错误消息的数组:

  1. /**
  2. * 获取已定义验证规则的错误消息。
  3. *
  4. * @return array
  5. */
  6. public function messages()
  7. {
  8. return [
  9. 'title.required' => 'A title is required',
  10. 'body.required' => 'A message is required',
  11. 'title.max' => '兄弟你太长了。'
  12. ];
  13. }

可用的验证规则

连接

自定义验证规则

尽管 Laravel 提供了多种多样有用的校验规则;但你依然可以自定义。注册自定义校验规则的方法之一便是使用规则对象。你可以使用 make:rule 生成新的规则对象。接下来,让我们使用该命令生成一个校验字符串是否是大写的规则, Laravel 会将新规则置于 app/Rules 目录中。如果该目录不存在,则在你执行 Artisan 命令创建规则时,Laravel 将创建该目录:

  1. php artisan make:rule Uppercase

当规则创建成功后,我们便可定义其行为。规则对象包含两个方法:passesmessagepasses 方法接收属性值及其名称,它应该返回以 truefalse 表示的属性值是否通过验证的结果。message 方法应该返回验证失败时使用的错误信息:

  1. <?php
  2. namespace App\Rules;
  3. use Illuminate\Contracts\Validation\Rule;
  4. class Uppercase implements Rule
  5. {
  6. /**
  7. * Create a new rule instance.
  8. *
  9. * @return void
  10. */
  11. public function __construct()
  12. {
  13. //
  14. }
  15. /**
  16. * Determine if the validation rule passes.
  17. *
  18. * @param string $attribute
  19. * @param mixed $value
  20. * @return bool
  21. */
  22. public function passes($attribute, $value)
  23. {
  24. return strtoupper($value) === $value;
  25. }
  26. /**
  27. * Get the validation error message.
  28. *
  29. * @return string
  30. */
  31. public function message()
  32. {
  33. return 'The :attribute must be uppercase.';
  34. }
  35. }

一旦规则定义好,我们就可以把它传给验证规则

  1. public function rules()
  2. {
  3. return [
  4. 'title' => ['required','alpha','max:20',new Uppercase],
  5. 'body' => 'required',
  6. ];
  7. }

这时候你输入的内容必须全部大写,不然将会报错误。

如果你想要从你的翻译文件中获取错误信息,你可以在你的 message 中使用 trans 助手方法:

  1. /**
  2. * Get the validation error message.
  3. *
  4. * @return string
  5. */
  6. public function message()
  7. {
  8. return trans('validation.uppercase');
  9. }

接着我们就需要到语言包中 添加一行,

因为我们使用的中文语言包,需要在路径resources/lang/zh_CN下的validation.php中添加。

  1. return [
  2. 'uppercase' => '输入 :attribute 必须大写.',

修改完成以后,如果有错误出现,将会显示 输入 :attribute 必须大写. :attribute将会被替换为字段名称,因为我们用的是title 这个字段名称能否修改成中文那?

你只需要在 validation.phpattributes 下添加即可

  1. 'attributes' => [
  2. 'title' => '标题'
  3. ],

使用闭包

如果你的规则在应用中仅仅使用一次,那你便可使用闭包来代替规则对象。闭包函数接收属性的方法,属性的值以及在校验失败时的回调函数 $fail

  1. validator = Validator::make($request->all(), [
  2. 'title' => [
  3. 'required',
  4. 'max:255',
  5. function ($attribute, $value, $fail) {
  6. if ($value === 'foo') {
  7. $fail('The '.$attribute.' is invalid.');
  8. }
  9. },
  10. ],
  11. ]);

掌握内容

  • 快速验证
  • 自定义验证消息
  • 表单请求验证
  • 常用验证规则
  • 自定义验证规则