概念
定期的に不要になったレコードをDBから物理的に削除したい場合があるとおもいます。今までこの処理をバッチやコマンドで作る必要がありましたがLaravel 8.50のリリースに含んでいるPrunable
かMassPrunable
のトレイトをモデルクラスに追加することで、この処理が簡単に実装できます。
セットアップ
変更履歴のログや調査のため、ユーザーの名前などの情報が変更されるときにDBのUserLog
のテーブルに保存する事を例とします。UserLog
情報を6ヶ月間保存する必要があり、定期的に6ヶ月間前のレコードをDBから物理的に削除します。
使い方
モデルの周り
モデルにPrunable
のトレイトを追加して、prunable
の関数に削除対象のレコードをEloquentのクエリビルダで返す機能を実装します。Prunable
とMassPrunable
を使うとき論理削除のSoftDeletes
を利用しているモデルでもクエリに一致した場合、物理的にforceDelete()
で削除されますので気を付けてください。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Prunable;
class UserLog extends Model
{
use Prunable;
/**
* 削除対処のモデルを取得
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function prunable()
{
// 6ヶ月前のレコードを取得する
return static::where('created_at', '<=', now()->subMonths(6));
}
}
もしそのモデルと関連する処理(たとえば、古いプロファイルの写真を削除することや、関連するモデル削除など)が必要な場合、モデルが削除する前にpruning
という関数で処理が実装できます。
/**
* モデルの削除の準備
*
* @return void
*/
protected function pruning()
{
// ここでモデルが削除する前に必要な処理を実装します
}
スケジュール
Prunable
を利用しているモデルの削除処理が行われるため、アプリケーションのApp\Console\Kernel
クラスでmodel:prune
のArtisanコマンドを登録する必要があります。そのコマンドが実行されるとapp/Models
ディレクトリ内にあるPrunable
とMassPrunable
を利用しているモデルの削除処理が行われます。もしモデルが別のダイレクトリーにあるなら、--model
のオプションを使って、モデルのクラス名を配列として指定できます。下記の例だと毎日の03:00
に削除処理が実行されます。
protected function schedule(Schedule $schedule)
{
// `app/Models`ディレクトリ内の場合
$schedule->command('model:prune')
->daily()
->at('03:00');
// `app/Models`ディレクトリ以外の場合
$schedule->command('model:prune', [
'--model' => [UserLog::class],
])->daily()
->at('03:00');
}
MassPrunable VS Prunable
MassPrunable
というのはの複数削除処理が行われ、Prunable
と違って削除前にレコードを取得せず、複数レコード削除クエリが実行されます。そのためMassPrunable
の方がPrunable
より、処理が効率的です。しかし、削除する前にレコードが取得されないため、Eloquentのdeleted・deleting・pruning
のイベントは発火しません。それ以外は、Prunableの実装と同じです。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\MassPrunable;
class UserLog extends Model
{
use MassPrunable;
/**
* 削除対処のモデルを取得
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function prunable()
{
// 6ヶ月前のレコードを取得する
return static::where('created_at', '<=', now()->subMonths(6));
}
/**
* モデルの削除の準備
*
* @return void
*/
protected function pruning()
{
// イベントが発生されないので、この関数の処理が行われない
}
}
参考
https://github.com/laravel/framework/pull/37696
https://laravel.com/docs/8.x/eloquent#pruning-models
https://laravel.com/api/8.x/Illuminate/Database/Eloquent/Prunable.html
https://laravel.com/api/8.x/Illuminate/Database/Eloquent/MassPrunable.html