Observer 觀察者模式創(chuàng)建步驟
- 觀察者
-
監(jiān)聽多個事件
觀察者
-
app\Observers\TopicObserver.php
class TopicObserver
{
// 監(jiān)聽事件
}
-
注冊觀察者
注冊觀察者
app\Providers\AppServiceProviders.php
namespace App\Providers;
use App\Topic;
use App\Observers\TopicObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* 注冊服務(wù)提供.
*
* @return void
*/
public function register()
{
//
}
/**
* 運行所有應(yīng)用.
*
* @return void
*/
public function boot()
{
// 為 Topic 模型注冊觀察者
Topic::observe(TopicObserver::class);
}
}
觀察者中事件的發(fā)生順序
laravel 5.8 版本下對于 Illuminate\Database\Eloquent\Model 源文件的分析,為了方便已去掉注釋
public function save(array $options = [])
{
$query = $this->newModelQuery();
if ($this->fireModelEvent('saving') === false) {
return false;
}
if ($this->exists) {
$saved = $this->isDirty() ?
$this->performUpdate($query) : true;
}
else {
$saved = $this->performInsert($query);
if (! $this->getConnectionName() &&
$connection = $query->getConnection()) {
$this->setConnection($connection->getName());
}
}
if ($saved) {
$this->finishSave($options);
}
return $saved;
}
首先會觸發(fā) saving 事件
$this->fireModelEvent('saving')
- 接著判斷
model是否新創(chuàng)建- 若是新創(chuàng)建,則執(zhí)行
Insert() - 若非新創(chuàng)建,則調(diào)用
isDirty ()判斷此model是否進行了修改。
- 若是新創(chuàng)建,則執(zhí)行
如果進行了修改,則會調(diào)用 $this->performUpdate($query) 進行更新操作,如果未進行修改,則直接返回 True
進入 $this->performUpdate($query)
protected function performUpdate(Builder $query)
{
if ($this->fireModelEvent('updating') === false) {
return false;
}
if ($this->usesTimestamps()) {
$this->updateTimestamps();
}
$dirty = $this->getDirty();
if (count($dirty) > 0) {
$this->setKeysForSaveQuery($query)->update($dirty);
$this->syncChanges();
$this->fireModelEvent('updated', false);
}
return true;
}
可以看到此處觸發(fā) updating 方法:
$this->fireModelEvent('updating')
如果返回的不是 false , 那么繼續(xù)往下走,判斷該模型是否使用了 Timestamp,如果使用了,即先更新自身Timestamp:
if ($this->usesTimestamps()) {
$this->updateTimestamps();
}
緊接著調(diào)用 getDirty,顧名思義,獲取自身臟值,進入該方法查看
public function getDirty()
{
$dirty = [];
foreach ($this->getAttributes() as $key => $value) {
if (! $this->originalIsEquivalent($key, $value)) {
$dirty[$key] = $value;
}
}
return $dirty;
}
果然該方法內(nèi)初始化空數(shù)組$dirty用于存儲自身被修改后的臟值。
你試過打印
Model模型嗎?
舉個栗子
Order {#359
#table: "order"
#guarded: array:1 [
0 => "id"
]
#connection: "mysql"
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:45 [
"id" => 2026
"order_sn" => "2019080617314062621248"
"customer_id" => 62
"order_status" => 0
"shipping_status" => 99
"pay_status" => 1
......
]
#original: array:45 [
"id" => 2026
"order_sn" => "2019080617314062621248"
"customer_id" => 62
"order_status" => 0
"shipping_status" => 99
"pay_status" => 1
......
]
#changes: []
#casts: []
#dates: array:1 [
0 => "deleted_at"
]
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
+timestamps: true
#hidden: []
#visible: []
#fillable: []
#forceDeleting: false
}
這是我打印的一個 order實例,可以看到其中有 original 和 attributes 兩個數(shù)組
-
original保存的是最初始的實例屬性,無法被外部直接修改 -
attributes數(shù)組可以被自有修改,此方法即是由判斷兩個數(shù)組的差異而返回的臟值
ok,繼續(xù)往下看 performUpdate 的代碼,可以看到會進行判斷是否有臟值:
if (count($dirty) > 0) {
$this->setKeysForSaveQuery($query)->update($dirty);
$this->syncChanges();
$this->fireModelEvent('updated', false);
}
存在臟值的情況下進入 if,執(zhí)行 update 操作,
在 update 結(jié)束后會執(zhí)行 syncChanges 同步自身數(shù)據(jù),繼而觸發(fā) updated 事件。
performUpdate 簡單解析完畢
回看
save()方法的接下來操作
if ($saved) {
$this->finishSave($options);
}
if 判斷在 update 成功下,才會觸發(fā) finishSave() 方法,此方法會接受一個參數(shù) $options,即是修改的屬性.
進入該 finishSave():
protected function finishSave(array $options)
{
$this->fireModelEvent('saved', false);
if ($this->isDirty() && ($options['touch'] ?? true)) {
$this->touchOwners();
}
$this->syncOriginal();
}
這里觸發(fā)了 saved 事件
- 當(dāng)模型已存在,非新建時,事件觸發(fā)順序如下:
saving->updating->updated->saved - 當(dāng)模型不存在,即需要新增時,事件觸發(fā)順序如下:
saving->creating->created->saved
觸發(fā)順序知曉,那么又有個疑問
saving,saved 和 updating,updated 有什么不一樣(區(qū)別)?
-
舉的實例中,
Laravel的Eloquent會維護實例的兩個數(shù)組,分別是original,attributes。- 兩者都是在
saved事件觸發(fā)之后,Laravel才會對兩個數(shù)組執(zhí)行syncOriginal操作
- 兩者都是在
saveing和saved會在Eloquent實例的original數(shù)組真值更改前后觸發(fā)updating和updated會在數(shù)據(jù)庫中的真值修改的前后觸發(fā)

