55.重构提交回复
- 本系列文章为
laracasts.com
的系列视频教程——Let's Build A Forum with Laravel and TDD 的学习笔记。若喜欢该系列视频,可去该网站订阅后下载该系列视频, 支持正版 ;- 视频源码地址:github.com/laracasts/Lets-Build-a-...;
- 本项目为一个 forum(论坛)项目,与本站的第二本实战教程 《Laravel 教程 - Web 开发实战进阶》 类似,可互相参照。
本节说明
- 对应视频教程第 55 小节:Refactoring to Form Requests
本节内容
就现在而言,处理提交新回复的方法看上去仍然有点臃肿,所以在本节中,我们将它重构为一个方法,然后再进行调用。我们将使用 表单请求验证 来进行我们的重构。首先我们创建表单请求类:
$ php artisan make:request CreatePostRequest
修改内容如下:
forum\app\Http\Requests\CreatePostRequest.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CreatePostRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'body' => 'required|spamfree',
];
}
}
注:我们暂时让
authorize()
方法返回true
,并且将验证规则放在了rules()
方法中。
现在我们的代码可以进行如下修改:
forum\app\Http\Controllers\RepliesController.php
.
.
public function store($channelId,Thread $thread,CreatePostRequest $request)
{
if(Gate::denies('create',new Reply)) {
return response(
'You are posting too frequently.Please take a break.:)',422
);
}
$reply = $thread->addReply([
'body' => request('body'),
'user_id' => auth()->id(),
]);
return $reply = $thread->addReply([
'body' => request('body'),
'user_id' => auth()->id(),
])->load('owner');
}
.
.
我们需要修改测试:
forum\tests\Feature\ParticipateInForumTest.php
.
.
/** @test */
public function replies_that_contain_spam_may_not_be_created()
{
$this->withExceptionHandling();
$this->signIn();
$thread = create('App\Thread');
$reply = make('App\Reply',[
'body' => 'something forbidden'
]);
$this->post($thread->path() . '/replies',$reply->toArray())
->assertStatus(422);
}
.
.
运行测试:
我们需要对异常进行处理:
forum\app\Exceptions\Handler.php
.
.
public function render($request, Exception $exception)
{
if($exception instanceof ValidationException){
return response('Validation failed.',422);
}
return parent::render($request, $exception);
}
.
.
如果是ValidationException
异常,我们返回 422。因为我们已经对异常进行了处理,再次运行测试:
接下来我们来整理我们的授权策略:
forum\app\Http\Requests\CreatePostRequest.php
<?php
namespace App\Http\Requests;
use App\Exceptions\ThrottleException;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
class CreatePostRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return Gate::allows('create',new \App\Reply);
}
protected function failedAuthorization()
{
throw new ThrottleException;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'body' => 'required|spamfree',
];
}
}
我们重写failedAuthorization()
,并且定义了一个异常类:ThrottleException
。这个异常类现在还没有建立,我们前往建立:
forum\app\Exceptions\ThrottleException.php
<?php
namespace App\Exceptions;
class ThrottleException extends \Exception
{
}
同样地,我们要为这个异常类做处理:
forum\app\Exceptions\Handler.php
.
.
public function render($request, Exception $exception)
{
if($exception instanceof ValidationException){
return response('Validation failed.',422);
}
if($exception instanceof ThrottleException){
return response('You are posting too frequently.',429);
}
return parent::render($request, $exception);
}
.
.
既然我们已经将授权策略转移了位置,那么我们的控制器代码就可以进行修改了:
forum\app\Http\Controllers\RepliesController.php
.
.
public function store($channelId, Thread $thread, CreatePostRequest $request)
{
return $reply = $thread->addReply([
'body' => request('body'),
'user_id' => auth()->id(),
])->load('owner');
}
.
.
我们还需要修改我们的测试:
forum\tests\Feature\ParticipateInForumTest.php
.
.
/** @test */
public function users_may_only_reply_a_maximum_of_once_per_minute()
{
$this->withExceptionHandling();
$this->signIn();
$thread = create('App\Thread');
$reply = make('App\Reply',[
'body' => 'My simple reply.'
]);
$this->post($thread->path() . '/replies',$reply->toArray())
->assertStatus(200);
$this->post($thread->path() . '/replies',$reply->toArray())
->assertStatus(429);
}
}
.
.
如果你观察得足够仔细,你会发现对授权策略的状态码返回,我们本节用的是 429,而上一节用的是 422。这是上一节我们的一个小错误,我们在本节进行修正。如果我们不清楚状态码的具体含义,我们可以在下面的文件中去确认状态码,例如我们现在需要的状态码 429,表示的是 HTTP_TOO_MANY_REQUESTS:
forum\vendor\symfony\http-foundation\Response.php
现在我们来运行全部测试:
我们的测试已经通过,但是如果你新建一个不合法的话题:
这是因为我们直接抛出了异常,而没有区分应用场合。我们进行下修改:
forum\app\Exceptions\Handler.php
.
.
public function render($request, Exception $exception)
{
if($exception instanceof ValidationException){
if ($request->expectsJson()){
return response('Validation failed.',422);
}
}
if($exception instanceof ThrottleException){
return response('You are posting too frequently.',429);
}
return parent::render($request, $exception);
}
.
.
如果是Ajax
调用,我们直接抛出异常;否则,我们不做处理。我们再次尝试:
我们再次运行全部测试:
修复未通过的测试:
.
.
/** @test */
public function replies_that_contain_spam_may_not_be_created()
{
$this->withExceptionHandling();
$this->signIn();
$thread = create('App\Thread');
$reply = make('App\Reply',[
'body' => 'something forbidden'
]);
$this->json('post',$thread->path() . '/replies',$reply->toArray())
->assertStatus(422);
}
.
.
再次运行全部测试: