Laravelで操作ログを保存する
目次
操作ログを保存する理由
システムを構築する際に極力管理画面での操作ログを保存する形を提案しています。
なぜ操作ログを保存するのかというと、
- 知らない間にデータが消えた
- 昨日までちゃんと動いていたのに動かなくなった
- ログインできなくなった
といった連絡があった場合、もちろんバグが原因のこともあるのですが、それ以外にお客様の操作によって必要なデータが削除されたり値が書き換わっていたりすることが往々にしてあるためです。
いつ、どのアカウントがどのテーブルのどのレコードを操作したのかを保存しておくと原因の調査が容易になります。
操作ログテーブル
操作ログテーブルは以下のようなマイグレーションで定義します。
Schema::create('admin_operation_logs', function (Blueprint $table) {
$table->id();
$table->bigInteger('admin_user_id')->unsigned()->comment('管理者ユーザID');
$table->morphs('entity');
$table->string('operation')->comment('操作内容');
$table->string('ip')->comment('IPアドレス');
$table->text('user_agent')->comment('ユーザーエージェント');
$table->timestamps();
$table->foreign('admin_user_id', 'fk_admin_operation_logs_admin_user_id')
->references('id')->on('admin_users')
->onUpdate('cascade')
->onDelete('cascade');
});
操作対象のテーブルは複数存在するため、entityはmorphsとして定義しています。
morphsとして定義しておくと、自動的にentity_idとentity_typeという2つのカラムが作成されます。
操作ログと各テーブルのリレーション設定
ログの詳細表示時に対象となったレコードの情報を取得したいのでリレーションを設定します。
class AdminOperationLog extends Model
{
use HasFactory;
public $model_name = '総合管理画面操作ログ';
// 中略
public function entity()
{
return $this->morphTo()->withTrashed();
}
morphsで定義したものに対してmorphTo()のリレーションを設定しています。
morph_typeに保存されたModelのClass名とmorph_idでリレーション対象のレコードを取得できます。
※削除されている場合があるのでwithTrashed()を指定してます。
※各テーブルは論理削除として実装しておく必要があります。
ログ検索機能
検索条件として対象テーブルを取得したいので各Modelに$model_nameを設定しています。
class AdminUser extends Authenticatable
{
use HasFactory;
use SoftDeletes;
use Notifiable;
public $model_name = '管理者ユーザ';
以下の関数でモデルのClass名一覧と$model_nameを取得しています。
function getModelList(): array
{
// Modelのファイル一覧を取得
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(App::basePath('app/Models/Entities')));
$class_names = [];
while ($iterator->valid()) {
if (!$iterator->isDot() && $iterator->isFile() && $iterator->isReadable() && 'php' === $iterator->current()->getExtension()) {
$class_name = str_replace('.php', '', $iterator->getBasename());
if($class_name){
$class_names[] = $class_name;
}
}
$iterator->next();
}
// ModelのClass名とモデル名の配列を作成
$model_array = [];
foreach ($class_names as $class_name) {
$model = new ('App\Models\Entities\\' . $class_name);
if ($model->model_name) {
$model_array[get_class($model)] = $model->model_name;
}
}
return $model_array;
}
あとは操作対象、操作ユーザなどを検索条件として一覧画面、詳細画面を作成するとログ表示機能を実装できます。
データが消えたという連絡が発生してもこの画面からいつ、誰が消したのかを調べることが出来ます。
なにかエラーが発生した際、バグだと断定せずデータ問題の可能性もあるため、このような機能を実装しておくことがオススメです。



















