用戶活動以及模型變動日志 ——spatie/laravel-activitylog
今天要學習的擴展包是 spatie/laravel-activitylog 它為我們提供了記錄用戶活動日志的功能,同時還提供了記錄模型日志的功能。這個擴展包的開發者你應該比較熟悉了,spatie/laravel-backup, spatie/laravel-responsecache, spatie/laravel-permission 都是他們這個組織開發的。
之前的課程中我們介紹的擴展包 venturecraft/revisionable 也有類似的功能,可以記錄模型的變動日志,這節課我們可以對比看看今天要學習的擴展包是不是更加方便。
安裝
$ composer require spatie/laravel-activitylog

將遷移文件發布出來。
$ php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="migrations"

執行 migrate 創建數據表。
$ php artisan migrate

看一下數據結構:

字段描述
id自增 ID 主鍵
log_name日志名稱,用於歸類
description日志內容
subject_idsubject 模型多態關聯 id
subject_typesubject 模型多態關聯 type
causer_idcauser 模型多態關聯 id
causer_typecauser 模型多態關聯 type
properties屬性,保存為 json
created_at創建時間
updated_at修改時間
最后將配置文件發布出來:
$ php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="config"

使用
記錄使用日志
打開 tinker 測試一下:
activity()->log('hello world');

如果當前有用戶登錄,則會記錄下來登錄的用戶:
$user = User::find(2); auth()->setUser($user); activity()->log('記錄登錄用戶的日志');
命令行中我們想模擬用戶登錄,可以使用 auth()->setUser。

當然我們也可以傳入參數指定相關數據:
- inLog —— 等同於 useLog,記錄日志的名稱,用於分類;
- performedOn —— 影響的模型;
- causedBy —— 由誰引起的日志;
- withProperties —— 其他需要記錄的屬性。
$topic = Topic::first(); $causedUser = User::find(3); activity()->inLog('test')->performedOn($topic)->causedBy($causedUser)->withProperties(['data' => 'test'])->log('測試參數');

查看一下數據:

- 未登錄用戶的,只是記錄了描述日志;
- 登錄用戶,記錄用戶,以及描述日志;
- 修改相關參數后的日志。
使用起來非常簡單,只需要在需要記錄的地方添加上面這樣的代碼即可。
比如我們記錄一下用戶的回復日志。
app/Http/Controllers/RepliesController.php
. . . public function store(ReplyRequest $request, Reply $reply) { $reply->content = $request->content; $reply->user_id = Auth::id(); $reply->topic_id = $request->topic_id; $reply->save(); activity('reply')->performedOn($reply)->log(':causer.name 添加了一條回復: :subject.content'); return redirect()->to($reply->topic->link())->with('sucess', '回復創建成功!'); } public function destroy(Reply $reply) { $this->authorize('destroy', $reply); $reply->delete(); activity('reply')->performedOn($reply)->log(':causer.name 刪除了一條回復: :subject.content'); return redirect()->to($reply->topic->link())->with('success', '成功刪除回復!'); } . . .
在記錄日志的時候,也就是 log 方法的參數中可以使用變量,這些變量會被替換:
:subject—— 表示performedOn的模型,可以使用點(:subject.column)表示模型屬性:causer—— 表示causedBy的模型,可以使用點(:causer.column)表示模型屬性:properties—— 表示withProperties自定義的屬性,可以使用點(:properties.column)表示對應的模型。
添加一個回復:

刪除這個回復:

查看數據庫日志:

查詢相關日志
擴展包使用的日志模型在這里 ——Spatie\Activitylog\Models\Activity。
所以我們可以直接通過這個模型做一些查詢。打開 tinker:
查詢對應名稱的日志,比如我們查詢所有回復相關的日志。
use Spatie\Activitylog\Models\Activity Activity::inLog('reply')->get();

查詢 id 為 2 的用戶相關的日志:
$user = User::find(2); Activity::causedBy($user)->get();

還可以進一步過濾
Activity::causedBy($user)->inLog('default')->get();

查詢某個回復相關的日志:
$topic = Topic::find(1); Activity::forSubject($topic)->get();

我們可以靈活的利用擴展包的模型,進行查詢,總結一下:
- inLog —— 查詢日志名稱;
- forSubject —— 查詢 subject;
- causedBy—— 查詢 causer。
還可以通過模型方便的獲取屬性。
$activity = Activity::forSubject($topic)->first(); $activity->causer; $activity->subject; $activity->getExtraProperty('data');
getExtraProperty 方法可以獲取 Properties 中的值。

記錄模型變化
默認情況下擴展包會通過模型的 created, updated, deleted 事件,記錄模型變化,測試一下。
首先需要給需要記錄的模型增加一個 Trait
app/Models/Topic.php
. . . use Spatie\Activitylog\Traits\LogsActivity; class Topic extends Model { use LogsActivity; . . .
創建一個話題,修改屬性,然后刪除,查看數據庫:

創建,修改,刪除都會觸發日志的記錄,但是默認情況下 description 只記錄了模型的事件名,而且修改日志沒有記錄模型屬性的前后變化,我們需要繼續修改一下。
app/Models/Topic.php
. . . protected static $ignoreChangedAttributes = ['updated_at']; protected static $logAttributes = ['title', 'category_id']; protected static $logOnlyDirty = true; public function getDescriptionForEvent(string $eventName): string { switch ($eventName) { case 'created': $description = '話題被創建'; break; case 'updated': $description = '話題被修改'; break; case 'deleted': $description = '話題被刪除'; break; default: $description = $eventName; break; } return $description; } . . .
重新添加並修改一個話題,查看數據庫:

description 被修改我們定義的樣子,properties 中記錄了模型的屬性,打開 tinker 測試一下,修改了代碼記得重啟 tinker:
$topic = Topic::find(103); $topic->activity
由於我們給 Topic 模型增加了 Trait,所以可以直接通過 activity 關系獲取模型相關的所有日志。

由於我們記錄了 title 以及 category_id 的變化,所以通過 activity 的 changes 方法就可以獲取所有的模型的變化情況。

attributes 是模型當前的屬性日志,old 是變化之前的屬性日志。
擴展包並沒有提供根據單個模型清理日志的功能,提供了一個命令 activitylog:clean 用來清除config('activitylog.delete_records_older_than_days) 之前的日志,我們可以根據需要調整配置,並且增加一個計划任務。
代碼版本控制
$ git add -A $ git commit -m 'spatie/laravel-activitylog'
tags: laravel,activitylog
