46.重构
- 本系列文章为
laracasts.com
的系列视频教程——Let's Build A Forum with Laravel and TDD 的学习笔记。若喜欢该系列视频,可去该网站订阅后下载该系列视频, 支持正版 ;- 视频源码地址:github.com/laracasts/Lets-Build-a-...;
- 本项目为一个 forum(论坛)项目,与本站的第二本实战教程 《Laravel 教程 - Web 开发实战进阶》 类似,可互相参照。
本节说明
- 对应视频教程第 46 小节:Refactoring For Better or Worse
本节内容
本节我们来对上一节的内容进行一些重构。首选看一下我们要重构的部分:
forum\app\Thread.php
.
.
public function addReply($reply)
{
$reply = $this->replies()->create($reply);
// Prepare notifications for all subscribers
$this->subscriptions
->filter (function ($sub) use ($reply){
return $sub->user_id != $reply->user_id;
})
->each
->notify($reply);
return $reply;
}
.
.
我们使用filter
函数过滤回复自己创建的话题的通知,但是我们可以更简单一点:
.
.
public function addReply($reply)
{
$reply = $this->replies()->create($reply);
// Prepare notifications for all subscribers
$this->subscriptions
->where('user_id','!=',$reply->user_id)
->each
->notify($reply);
return $reply;
}
.
.
运行一下全部的功能测试:
虽然我们的功能测试通过,但是你仔细思考一下上面的代码就会发现:我们的添加回复和发送消息的代码耦合在一起。这非常不利于后期维护跟功能重构,所以我们要进行解耦。我们将利用 Laravel 的事件系统 来进行解耦:
事件机制是一种很好的应用解耦方式,因为一个事件可以拥有多个互不依赖的监听器。例如,如果你希望每次订单发货时向用户发送一个 Slack 通知。你可以简单地发起一个 OrderShipped 事件,让监听器接收之后转化成一个 Slack 通知,这样你就可以不用把订单的业务代码跟 Slack 通知的代码耦合在一起了。
Laravel 应用中的 EventServiceProvider
有个 listen
数组包含所有的事件(键)以及事件对应的监听器(值)来注册所有的事件监听器,可以灵活地根据需求来添加事件。现在,让我们增加一个 ThreadHasNewReply
事件:
forum\app\Providers\EventServiceProvider.php
.
.
protected $listen = [
'App\Events\ThreadHasNewReply' => [
'App\Listeners\NotifyThreadSubscribers',
],
];
.
.
我们再使用event:generate
命令为事件和监听器创建文件:
$ php artisan event:generate
接着修改我们新创建的两个文件内容为以下:
forum\app\Events\ThreadHasNewReply.php
<?php
namespace App\Events;
use Illuminate\Queue\SerializesModels;
class ThreadHasNewReply
{
use SerializesModels;
public $thread;
public $reply;
/**
* Create a new event instance.
*
* @param \App\Thread $thread
* @param \App\Reply $reply
*/
public function __construct($thread,$reply)
{
//
$this->thread = $thread;
$this->reply = $reply;
}
}
forum\app\Listeners\NotifyThreadSubscribers.php
<?php
namespace App\Listeners;
use App\Events\ThreadHasNewReply;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class NotifyThreadSubscribers
{
/**
* Handle the event.
*
* @param ThreadHasNewReply $event
* @return void
*/
public function handle(ThreadHasNewReply $event)
{
// Prepare notifications for all subscribers
$event->thread->subscriptions
->where('user_id','!=',$event->reply->user_id)
->each
->notify($event->reply);
}
}
接下来我们改为用事件来触发通知:
forum\app\Thread.php
<?php
namespace App;
use App\Events\ThreadHasNewReply;
.
.
class Thread extends Model
{
.
.
public function addReply($reply)
{
$reply = $this->replies()->create($reply);
event(new ThreadHasNewReply($this,$reply));
return $reply;
}
.
.
}
再次测试:
看上去很酷,是不是?但是,我们在重构的时候,需要思考我们是否真的有必要进行这样的重构。在我们当前的情形中,消息通知的动作我们可以很简单地抽取成一个独立的方法就能满足我们的需求。也就是说,在当前我们并不需要用到事件系统。我们将新建的两个文件删除,并添加我们需要的方法:
forum\app\Thread.php
.
.
public function addReply($reply)
{
$reply = $this->replies()->create($reply);
$this->notifySubscribers($reply);
return $reply;
}
public function notifySubscribers($reply)
{
$this->subscriptions
->where('user_id','!=',$reply->user_id)
->each
->notify($reply);
}
.
.
再次测试:
本节我们学的是,不要为了重构而重构。Good Luck!