しばしばデータの変更連絡を保存、閲覧できるようにしたいという要望があります。この要望の際は通常のログに加え日本語で見やすい差分を作るようにする必要があります。これの役に立つメソッドと使い方を紹介します。
紹介するメソッドはEloquentのgetOriginalとgetChangesです。それぞれデータベースから取得してきた時点のデータと更新したデータを呼び出すメソッドです。これらは次のように使えます。
// テスト用にその場でデータ生成
$tmp = new Account();
$tmp->name = 'test';
$tmp->password = \Hash::make('test');
$tmp->save();
// ↑で作ったデータを持ってきて更新
$account = Account::query()->orderBy('created_at', 'desc')->first();
$account->name = 'test2';
$account->password = \Hash::make('test2');
$account->save();
// 更新前のデータ全てが $account->getOriginal() に入っている
dump($account->getOriginal());
/*
array:6 [ // xxx.php:00
"account_id" => 130
"name" => "test2"
"password" => "$2y$10$k8YYvQ6ovb30E5HkRu5vi.G5y28CxzE4B9t/UdbpRRXuv.5afgRD2"
"created_at" => Illuminate\Support\Carbon @1701413500^ {#1705 省略 }
"updated_at" => Illuminate\Support\Carbon @1701413500^ {#1713 省略 }
"deleted_at" => null
]
*/
// 更新後のデータの更新部分のみが $account->getChanges() に入っている
dump($account->getChanges());
/*
array:2 [ // xxx.php:00
"name" => "test2"
"password" => "$2y$10$k8YYvQ6ovb30E5HkRu5vi.G5y28CxzE4B9t/UdbpRRXuv.5afgRD2"
]
*/
上記の例のようにデータを取得することによってモデル単位でどの値がどのように更新されたかを調べることができます。これを利用することで次のように更新内容を構造化して好き勝手しやすくできます。この構造化したデータをSQLなりなんなりで永続化しておくと便利です。
$original = $account->getOriginal();
$changes = $account->getChanges();
// $original と $changes を比較して差分を取得
$changeHistory = [];
// 更新されたデータのキーを回す
foreach($changes as $key => $value) {
if(in_array($key, ['password','remember_token'], true)){
// 更新履歴に残したくないキーはスキップ
continue;
}
$changeHistory[$key] = [
// 更新前のデータを引っ張って前後をまとめて保持させる
'before' => $original[$key] ?? null,
'after' => $value,
];
}
dump($changeHistory);
/*
array:1 [ // xxx.php:00
"name" => array:2 [
"before" => "test2"
"after" => "test2"
]
]
*/
ここまでくれば後はフォーマットするだけです。文章にしてもいいですし、表にしてもいいです。使用目的に合わせて適宜加工しましょう。
$attributes = [
'name' => '名前',
'email' => 'メールアドレス',
];
// 純粋な日本語でまとめる
$message = '';
foreach($changeHistory as $key => $value) {
$message .= $attributes[$key].'が'.$value['before'].'から'.$value['after'].'に変更されました。' . PHP_EOL;
}
// 二次元表にする
$header = ['項目', '変更前', '変更後'];
$body = [];
foreach($changeHistory as $key => $value) {
$body[] = [
$attributes[$key],
$value['before'],
$value['after'],
];
}