Observer
含義
定義對象之間的一對多依賴關(guān)系刺洒,以便當(dāng)一個(gè)對象更改狀態(tài)時(shí),它的所有依賴關(guān)系將被自動(dòng)通知和更新
適用性
當(dāng)希望對目標(biāo)對象的特定事件做出反應(yīng)時(shí)挪鹏,將使用觀察者模式袁辈。什么時(shí)候目標(biāo)對象分發(fā)生變化,就通知觀察者挖滤。其實(shí),PHP 已經(jīng)有了觀察者模式的內(nèi)置接口:SplSubject
和 SplObserver
抽象結(jié)構(gòu)
-
SplSubject
是一個(gè)抽象類或者接口浅役。其內(nèi)部可以定義一個(gè)數(shù)組保存附加的觀察者們.SplSubject
包含三種方法斩松。attach
方法將觀察者添加到目標(biāo)對象,detach
方法分離觀察者觉既。notify
方法通常包含循環(huán)惧盹,遍歷所有附加的觀察者并調(diào)用其更新方法。 -
SplObserver
也是一個(gè)抽象類或者接口奋救。SplObserver
內(nèi)有一個(gè)update
方法岭参,由于SplSubject
觸發(fā)目標(biāo)對象的更新反惕。 -
RealSubject
是SplSubject
接口的實(shí)現(xiàn)尝艘。 包含存儲附加觀察者的對象實(shí)例(數(shù)組/集合等),實(shí)際上是標(biāo)準(zhǔn)里SplObjectStorage
類的實(shí)例姿染,可以看做是一個(gè)整潔的助手類(helper class)背亥,該類提供了一種對象到數(shù)據(jù)的映射,具體參見手冊悬赏。 -
RealObserver
是SplObserver
接口的實(shí)現(xiàn)狡汉。它的update
方法傳遞一個(gè)SplSubject
實(shí)例對象作為參數(shù),任何時(shí)候目標(biāo)對象發(fā)生變動(dòng)闽颇,它就會執(zhí)行相應(yīng)的業(yè)務(wù)邏輯
示例
在這個(gè)例子中盾戴,將使用 PHP 內(nèi)置的 SplObserver
和 SplSubject
來顯示一個(gè)相當(dāng)通用的觀察者模式,之后兵多,探索一下 Laravel Eloquent
模型內(nèi)部結(jié)構(gòu)尖啡,以及如何使用事件來處理附加的觀察者橄仆。
SPL Obeserver
使用通用的 SPL 接口,只需定義兩個(gè)類衅斩,為了演示方面盆顾,不作命名空間區(qū)分
<?php
class RealSubject implements \SplSubject
{
private $observers;
public function __construct($name)
{
$this->name = $name;
$this->observers = new \SplObjectStorage();
}
public function attach(\SplObserver $observer)
{
// TODO: Implement attach() method.
$this->observers->attach($observer);
}
public function detach(\SplObserver $observer)
{
// TODO: Implement detach() method.
$this->observers->detach($observer);
}
public function notify()
{
// TODO: Implement notify() method.
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
}
class RealObserver implements \SplObserver
{
private $name;
public function __construct($name)
{
$this->name = $name;
}
public function update(\SplSubject $subject)
{
// TODO: Implement update() method.
print "{$this->name} was notified by {$this->name}" . PHP_EOL;
}
}
$subject1 = new RealSubject('subject1');
$observer1 = new RealObserver('observer1');
$observer2 = new RealObserver('observer2');
$observer3 = new RealObserver('observer3');
$subject1->attach($observer1);
$subject1->attach($observer2);
$subject1->attach($observer3);
$subject1->notify();
測試結(jié)果:
observer1 was notified by subject1
observer2 was notified by subject1
observer3 was notified by subject1
RealSubject
和 RealObserver
,都是通用的實(shí)現(xiàn)方式畏梆,但是有其很大局限性您宪,這些通用的方法是約束的,你不能在 update 方法里添加任何除了 SplSubject
類的類型提示(type hint)奠涌,你可能也并不總是希望通知的方式是公開的宪巨,而希望是內(nèi)部流程發(fā)生時(shí),觀察者才得到通知溜畅,正是使用通用 SPL 接口的限制揖铜,這里你無法更改。那怎么辦呢达皿?
抽煙的故事
我們都知道人是復(fù)雜的天吓,可以是觀察者也可以是被觀察者,或者說他既是 目標(biāo)對象(Subject) 又是 觀察者(Observer)峦椰,下面通過一個(gè)故事來模擬下:
大學(xué)通宵打 LOL 或 Dota 的時(shí)候龄寞,什么不能少?香煙汤功!沒錯(cuò)物邑,尤其后半夜鬧煙慌了,沒煙抽了滔金,這時(shí)候你只剩唯一一根色解,剛一點(diǎn)著,室友聞到了餐茵,挖槽科阎,不給我來一口,這時(shí)候你咋辦忿族?是我就趕緊跑出去自己偷偷吸起來锣笨,哈哈,開玩笑道批。下面用代碼模擬下這個(gè)情景错英,這里 “你” 可以看作目標(biāo)對象,“室友” 看作觀察者隆豹,我們期望的輸出應(yīng)該是這樣:
--- 你 點(diǎn)著了一根 芙蓉王 ---
小明說: “擦椭岩,我聞到了芙蓉王的味道”
小賤說: “擦,我聞到了芙蓉王的味道”
小王說: “擦,我聞到了芙蓉王的味道”
你說: “握草判哥,一幫畜生氮唯,我先來一口”
--- 你 點(diǎn)著了一個(gè)根 中華 ---
你說: “麻蛋,幸虧還剩一根中華姨伟,哈哈”
接下來用代碼實(shí)現(xiàn):
<?php
// 先定義兩個(gè)接口惩琉,點(diǎn)煙的人,就是你夺荒,聞到香煙的室友
interface CigaretteSmeller
{
public function smell(CigaretteSmoker $smoker, $cigarette);
}
interface CigaretteSmoker
{
public function nearBy(CigaretteSmeller $smeller);
public function noLongerNearBy(CigaretteSmeller $smeller);
public function lightUp($cigarette);
}
// 給故事里的人都起個(gè)名字瞒渠,定義一個(gè)說話的方法
class Person implements CigaretteSmoker, CigaretteSmeller
{
public function __construct($name)
{
$this->name = $name;
$this->observers = new \SplObjectStorage();
}
public function says($phrase)
{
print "{$this->name} 說: \t\"" . $phrase . "\"" . PHP_EOL;
}
// 實(shí)現(xiàn)接口中的 nearBy 方法
public function nearBy(CigaretteSmeller $smeller)
{
$smellers = func_get_args();
foreach ($smellers as $smeller) {
$this->observers->attach($smeller);
}
}
// 實(shí)現(xiàn)接口中的 noLongerNearBy 方法
public function noLongerNearBy(CigaretteSmeller $smeller)
{
$smellers = func_get_args();
foreach ($smellers as $smeller) {
$this->observers->detach($smeller);
}
}
// 實(shí)現(xiàn)接口中的 lightUp 方法,你一點(diǎn)著香煙附近的室友就靠過來
public function lightUp($cigarette)
{
print "--- {$this->name} lightUp {$cigarette} ---" . PHP_EOL;
foreach ($this->observers as $observer) {
$observer->smell($this, $cigarette);
}
}
// 實(shí)現(xiàn)接口中的 smell 方法
public function smell(CigaretteSmoker $smoker, $cigarette)
{
$this->says("擦技扼,我聞到了{(lán)$cigarette}的味道");
}
}
$you = new Person('你');
$wang = new Person('小王');
$li = new Person('小李');
$jian = new Person('小賤');
$you->nearBy($wang, $li, $jian);
$you->lightUp('芙蓉王');
$you->says('握草伍玖,一幫畜生,我先來一口');
$you->noLongerNearBy($wang, $li, $jian);
$you->lightUp('芙蓉王');
$you->says("麻蛋剿吻,幸虧還剩一根中華窍箍,哈哈");
哈哈。是不是稍微理解了 Observers
模式的應(yīng)用呢丽旅。這里與前面通用的 SPL 示例不同椰棘,這段代碼反映了一定的業(yè)務(wù)邏輯,兩者都使用到了觀察者模式榄笙。接下來我們看看 Observer
在 Eloquent
里是如何應(yīng)用的邪狞。
Eloquent Observer:開箱即用
所有的 Eloquent
模型都內(nèi)置了觀察者模式。我們創(chuàng)建一個(gè) Car
模型茅撞,汽車會有些基本屬性:制造商帆卓,車輛識別碼(vin),年代米丘,描述等剑令。
app/Car.php
namespace App;
use Illuminate\Datebase\Eloquent\Model;
class Car extends Model
{
}
怎么在這個(gè) model 上設(shè)置觀察者呢,很簡單拄查,使用 observe
方法吁津。
app/test1.php
\App\Car::observe(new Observers\ObserveEverything);
創(chuàng)建了一個(gè)通用的觀察者,名為 ObserveEverything
靶累,它包含了所有可以在 Eloquent
模型上使用的那些開箱即用的方法腺毫。簡單起見,每個(gè)方法打印一個(gè)語句挣柬,以便了解何時(shí)被調(diào)用的。方法列表如下:
app/Observers/ObserveEverything.php
class ObserveEverything
{
public function creating($model)
{
print "creating model" . PHP_EOL;
}
public function created($model)
{
print "created model" . PHP_EOL;
}
public function updating($model)
{
print "updating model" . PHP_EOL;
}
public function updated($model)
{
print "updated model" . PHP_EOL;
}
public function saving($model)
{
print "saving model" . PHP_EOL;
}
public function saved($model)
{
print "saved model" . PHP_EOL;
}
public function deleting($model)
{
print "deleting model" . PHP_EOL;
}
public function deleted($model)
{
print "deleted model" . PHP_EOL;
}
public function restoring($model)
{
print "restoring model" . PHP_EOL;
}
public function restored($model)
{
print "restored model" . PHP_EOL;
}
}
從每個(gè)方法名當(dāng)中你或許已經(jīng)知道何時(shí)被調(diào)用的睛挚。不過我還是稍微解釋下:
- 當(dāng) model 首次在數(shù)據(jù)庫被創(chuàng)建之前邪蛔,creating 方法被觸發(fā)≡可以通過新建 model 的 save 方法或靜態(tài) create 方法來觸發(fā)侧到。注意勃教,當(dāng)新的 model 已經(jīng)被構(gòu)建或檢索時(shí),不會觸發(fā)該方法匠抗。如果方法返回 false故源,則 model 不會被創(chuàng)建。
- 當(dāng) model 已經(jīng)在數(shù)據(jù)庫創(chuàng)建完成后汞贸,created 方法被觸發(fā)绳军。
- 當(dāng)已經(jīng)存在的 model 在被更新之前,updating 方法會被觸發(fā)矢腻。同理门驾,返回 false ,model 不會被更新多柑。
- 當(dāng)已經(jīng)存在的 model 被更新之后奶是,updated 方法被觸發(fā)。
- 當(dāng) model 被創(chuàng)建或者更新之前竣灌,saving 方法會被觸發(fā)聂沙。同理,返回 false model 不會被保存初嘹。
- 當(dāng) model 被創(chuàng)建或者更新之后逐纬,saved 方法被觸發(fā)
- 當(dāng) model 被刪除之前,deleting 方法被觸發(fā)削樊。同理豁生,返回 false,model 不會被刪除漫贞。
- 當(dāng) model 被刪除之后 deleted 方法被調(diào)用甸箱。
- 當(dāng) model 被還原之前 restoring 方法被調(diào)用,還原適用于應(yīng)用了軟刪除(soft deletes)的模型迅脐。它將刪除當(dāng)前記錄的 deleted_at 列芍殖。同理,返回 false谴蔑,model 不會被刪除豌骏。
- 當(dāng) model 被還原之后 restored 方法被調(diào)用。
接下來隐锭,添加該 Obeserver 到 Car model 上窃躲。
app/test1.php
Car::observe(new Observers\ObserveEverything);
當(dāng) Car 模型發(fā)生一系列改變時(shí)將觸發(fā) ObserveEverything 觀察者的調(diào)用。
app/test1.php
$car1 = Car::find(1);
$car->vin = str_random(32)
print "Saving car #1 to database" . "<br>";
$car1->save();
輸出:
Saving car #1 to database
saving model
updating model
updated model
saved model
app/test1.php
$car2 = new \App\Car();
$car2->description = "cool car description";
$car2->vin = str_random(32);
$car2->manufacturer = "Honda";
$car2->year = '2012';
print "Creating a new car";
$car2->save();
輸出:
Creating new car
saving model
creating model
created model
saved model
app/test1.php
print "Deleting a car that you just made";
$car2->delete();
輸出:
Deleting that new car you just made
deleting model
deleted model
app/test1.php
print "Restoring that car you just deleted";
$car2->restore();
輸出:
Restoring that car you just deleted
restoring model
saving model
updating model
updated model
saved model
restored model
使用觀察者阻止更新
上面我們重現(xiàn)了這些開箱即用的事件模式钦睡。里面的每個(gè)方法都與 Eloquent model 事件相掛鉤蒂窒。前面我們說了在相應(yīng)事件返回 false,則對應(yīng) model 將不會執(zhí)行,這意味著我們就可以在這里做些事情洒琢,來阻止 save秧秉,create,delete衰抑,restore 等象迎。來看一個(gè)例子,假設(shè)說所有的汽車 vin 碼必須包含字母 h 才能保存呛踊,那么不包含字母 h 的模型就不會寫入數(shù)據(jù)庫砾淌。
app/test1.php
Car::observe(new Observers\VinObserver);
$car1 = Car::find(1);
// attempt #1 with no h
$car1->vin = "asdfasdfasdf";
$car1->save() && print "attempt #1 saved\n";
// attempt #2 contains h
$car1->vin = "hasdfasdfasdf";
$car1->save() && print "attempt #2 saved\n";
輸出:
model vin does not contain letter 'h', canceling update...
attempt #2 saved
來看一下怎么實(shí)現(xiàn)
app/Observers/VinObserver.php
namespace App\Observers;
class VinObserver
{
public function updating($model)
{
$origin = $model->getOriginal("vin");
if ($model->vin === $original) {
return true; // ignore unchanged vin
}
if (! str_contains($model->vin, 'h')) {
print "model vin does not contain letter 'h', canceling updating vi \n";
return false;
}
}
}
忽略所有未改變 vin 碼的 model。并且不包含字母 h 的 vin 返回 false恋技,這樣就可以阻止更新拇舀。來看 laravel 底層是如何處理的。
vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
/**
* 執(zhí)行模型更新操作
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return bool
*/
protected function performUpdate(Builder $query)
{
// 如果更新時(shí)間返回 false蜻底,我們將取消更新操作骄崩,以便開發(fā)人員可以將驗(yàn)證系統(tǒng)
// 掛接到其模型中,并在模型未通過驗(yàn)證時(shí)取消此操作薄辅。否則要拂,我們就更新
if ($this->fireModelEvent('updating') === false) {
return false;
}
// 首先,為了開發(fā)者方便站楚,我們需要?jiǎng)?chuàng)建一個(gè)新的查詢實(shí)例脱惰,并接觸我們維護(hù)的
// 模型上的創(chuàng)建和更新時(shí)間戳。然后繼續(xù)保存模型實(shí)例窿春。
if ($this->timestamps) {
$this->updateTimestamps();
}
// 一旦我們執(zhí)行了更新操作拉一,就將觸發(fā)該模型實(shí)例的 update 事件。這將允許開發(fā)者
// 有機(jī)會在模型更新后掛鉤處理的事情旧乞,
$dirty = $this->getDirty();
if (count($dirty) > 0) {
$this->setKeysForSaveQuery($query)->update($dirty);
$this->fireModelEvent('updated', false);
}
return true;
}
當(dāng)執(zhí)行更新操作時(shí)蔚润,其中一件事就是檢查臟字段(dirty fields)。如果模型沒有發(fā)生改變尺栖,就不要更新嫡纠,接下來是 fireModelEvent 方法
,作用就是觸發(fā)模型事件延赌,如果返回 false除盏,就不執(zhí)行更新操作,繼續(xù)看看 fireModelEvent
方法內(nèi)部挫以。
vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
/**
* 觸發(fā)模型上給定的事件
*
* @param string $event
* @param bool $halt
* @return mixed
*/
protected function fireModelEvent($event, $halt = true)
{
if (! isset(static::$dispatcher)) {
return true;
}
// 我們把類的名稱附加到事件中者蠕,以區(qū)別于被觸發(fā)的其他模型事件,從而
// 允許我們單獨(dú)監(jiān)聽每個(gè)模型事件屡贺,而不是捕獲所有模型的事件蠢棱。
$event = "eloquent.{$event}: ".static::class;
$method = $halt ? 'until' : 'fire';
return static::$dispatcher->$method($event, $this);
}
這個(gè)方法內(nèi)部調(diào)用了調(diào)度器(dispatcher)的 until 或者 fire 方法并返回結(jié)果锌杀。你附加到模型上的所有觀察者都放在調(diào)度器中甩栈。這就是為什么你在這個(gè) fireModelEvent 方法中沒有看到觀察者的內(nèi)容泻仙。那么這個(gè)靜態(tài)調(diào)度器到底是個(gè)什么東西呢? Eloquent 模型使用了共享的調(diào)度器量没,尤其是 $app['events']
單例玉转。該事件調(diào)度器是消息總線,它是 Illuminate\Events\Dispatcher
的一個(gè)實(shí)例殴蹄。當(dāng)應(yīng)用程序啟動(dòng)數(shù)據(jù)服務(wù)提供者時(shí)究抓,事件調(diào)度器將被注入到 Eloquent 模型中。
vendor/laravel/framework/src/Illuminate/Database/DatabaseServiceProvider.php
/**
* 啟動(dòng)應(yīng)用程序事件
*
* @return void
*/
public function boot()
{
Model::setConnectionResolver($this->app['db']);
Model::setEventDispatcher($this->app['events']);
}
這里袭灯,我們已經(jīng)發(fā)現(xiàn)模型事件如何被觸發(fā)以及如何阻止模型的更新操作刺下。但是還有一點(diǎn),假設(shè)所有注冊的模型事件都被放在事件調(diào)度器中處理稽荧,你不知道內(nèi)部如何處理的橘茉,接下來看一看觀察者是如何附加到模型上的。
vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
之前版本注冊模型觀察者等一系列方法放在 model.php
姨丈,里畅卓,后面 laravel 將這部分功能以 trait 的方式重構(gòu)了,并命名為 HasEvents
蟋恬。所以路徑是
Illuminate\Database\Eloquent\Concerns\HasEvents.php
/**
* 注冊模型的觀察者
*
* @param object|string $class
* @param int $priority
* @return void
*/
public static function observe($class, $priority = 0)
{
$instance = new static;
$className = is_string($class) ? $class : get_class($class);
// 當(dāng)注冊模型觀察者時(shí)翁潘,我們將遍歷可能的事件并確定此觀察者是否具有該方法
// 如果有,我們將把它掛鉤到模型的事件系統(tǒng)中歼争,以便監(jiān)聽拜马。
foreach ($instance->getObservableEvents() as $event) {
if (method_exists($class, $event)) {
static::registerModelEvent($event, $className.'@'.$event, $priority);
}
}
}
當(dāng)在模型上調(diào)用觀察者方法時(shí),它會遍歷可能出現(xiàn)的事件沐绒,然后使用事件和類名稱做參數(shù)調(diào)用 registerModelEvent 方法俩莽。getObservableEvents 方法返回一個(gè)字符串?dāng)?shù)組。字符串便是前面提及的那些事件(updating, updated, creating, created 等等)洒沦,它還包括模型屬性數(shù)組 $observables 里存放的其他任何可被觀察(監(jiān)聽)的事件豹绪。根據(jù)源碼得知,該方法具有以下參數(shù):
static::registerModelEvent('updating', 'Observers\VinObserver@updating');
那么問題來了申眼,registerModelEvent 方法究竟做了什么瞒津?
/**
* 使用調(diào)度器注冊模型事件
*
* @param string $event
* @param \Closure|string $callback
* @param int $priority
* @return void
*/
protected static function registerModelEvent($event, $callback, $priority = 0)
{
if (isset(static::$dispatcher)) {
$name = static::class;
static::$dispatcher->listen("eloquent.{$event}: {$name}", $callback, $priority);
}
}
共享的調(diào)度器被告知去監(jiān)聽 App\Car
的更新事件,任何時(shí)候在調(diào)度器上觸發(fā)該事件括尸,對應(yīng)回調(diào)也將觸發(fā)巷蚪。這就是觀察者如何附加到模型上的。注意濒翻,每個(gè)模型都沒有得到自己的觀察者數(shù)組屁柏。這將會比附加觀察者數(shù)組到每一個(gè)模型實(shí)例上使用更少的內(nèi)存空間啦膜。這也意味著我們所有的 Car 模型都有相同的觀察者,這是符合預(yù)期的淌喻。如果你就想給 Car 的其中一個(gè)實(shí)例創(chuàng)建觀察者僧家,而其他實(shí)例并不創(chuàng)建,那么就需要做一些不同的處理÷闵荆現(xiàn)在八拱,我們已經(jīng)明白了觀察者是如何附加到 Eloquent 模型上并被觸發(fā)。
我們已經(jīng)得知 laravel 提供的一些列標(biāo)準(zhǔn)事件可以幫助我們實(shí)現(xiàn)好多功能涯塔。然后〖〉荆現(xiàn)實(shí)情況是復(fù)雜的,對應(yīng)的 model 也是復(fù)雜的匕荸,你可能需要好多不同的標(biāo)準(zhǔn)事件以外的事件爹谭。比如你有一個(gè)不同狀態(tài)的 content 模型,對應(yīng)狀態(tài)可能是草稿榛搔,待審核诺凡,已發(fā)布等。如果你想根據(jù)不同狀態(tài)執(zhí)行不能的 model 行為药薯,那么你就需要自定義觀察者附加到模型上绑洛,去監(jiān)聽對應(yīng)事件。
前面已經(jīng)提及到模型屬性數(shù)組 $observable
童本。這里就是存放我們自定義的事件真屯。該數(shù)組允許你去監(jiān)聽其他事件。比如:
<?php
use Illuminate\Database\Eloquent\Model;
class MyContentModel extends Model
{
/**
* Additional observable events.
*/
protected $observables = [
'sentforreview', // 即將發(fā)布進(jìn)入待審核狀態(tài)
'rejected', // 撤銷穷娱,駁回绑蔫,意味著內(nèi)容可能因?yàn)閷徍耸д`而發(fā)布了,馬上下架
];
}
本質(zhì)上泵额,它使用了 Eloquent 內(nèi)部的 getObservableEvents 方法配深,前面已提及,該方法會合并自定義的事件到標(biāo)準(zhǔn)事件數(shù)組中嫁盲。
Illuminate\Database\Eloquent\Concerns\HasEvents
/**
* 獲取能監(jiān)聽到的事件名
*
* @return array
*/
public function getObservableEvents()
{
return array_merge(
[
'creating', 'created', 'updating', 'updated',
'deleting', 'deleted', 'saving', 'saved',
'restoring', 'restored',
],
$this->observables // <--- merge in custom events
);
}
緊接著淹魄,生成對應(yīng)觀察者實(shí)現(xiàn):
<?php
namespace App\Observers;
class MyContentObserver
{
public function sentforreview(MyContent $myContent)
{
// Your logic here.
}
public function rejected(MyContent $myContent)
{
// Your logic here.
}
}
這一步我們就完成了自義定的事件等待觸發(fā)了衍锚,那么什么時(shí)候觸發(fā),怎么觸發(fā)?
<?php
use Illuminate\Database\Eloquent\Model;
class MyContentModel extends Model
{
public function sendForReview()
{
// 對模型作相應(yīng)的處理然后觸發(fā)事件
$this->fireModelEvent('sentforreview', false);
}
public function reject()
{
// 對模型作相應(yīng)的處理然后觸發(fā)事件
$this->fireModelEvent('rejected', false);
}
}
到這里我們就擁有了一個(gè)單一的觀察者來處理自定義事件包蓝。在最新版本 5.4 里始苇,laravel 提供了一種 map 方式搁拙,允許我們將 Eloquent 模型的生命周期的多個(gè)事件映射到專門的事件系統(tǒng)中的事件類(EventServiceProvider)云挟,關(guān)于 laravel 的事件系統(tǒng),請參考文檔哺哼。這將使我們的 model 更輕量佩抹〉鸱纾基本示例:
<?php
namespace App;
use App\Events\UserSaved;
use App\Events\UserDeleted;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* 模型事件映射。
*
* @var array
*/
protected $events = [
'saved' => UserSaved::class,
'deleted' => UserDeleted::class,
];
}
關(guān)于這個(gè)特性棍苹,請參考
https://laracasts.com/series/whats-new-in-laravel-5-4/episodes/10
總結(jié)
事件驅(qū)動(dòng)架構(gòu)(Event-driven architecture)是一種圍繞應(yīng)用程序狀態(tài)而設(shè)計(jì)的軟件架構(gòu)模式无宿。觀察者模式可用于這種類型的軟件架構(gòu)設(shè)計(jì)。 還有其他類似的模式廊勃。比如:
- 中介者模式(mediator pattern)
- 命令總線模式(command bus pattern)
- 訂閱發(fā)布模式((subscribe/publish pattern)laravel 內(nèi)置的事件系統(tǒng)就是用的這種模式
說實(shí)話懈贺,這些內(nèi)在區(qū)別有啥不同经窖,我也搞不清坡垫,總之別太迷戀設(shè)計(jì)模式。画侣。冰悠。。
參考書籍:《Design Patterns in PHP and Laravel》