古くなってもう要らないDBレコードを削除することを、プルーン(prune)あるいはパージ(purge)などと言います。カスタムのArtisanのコマンドを書いて処理してもいいのだけれど、LaravelではこれがModelの中であるトレイトを使うだけで、組織的できてしまいます。

プルーンの対象は特定のDBモデルのレコードです。ここでは、会員ログインのレコードを貯めているuser_logsのテーブルのモデルUserLogを例とします。以下のようにPrunableのトレイトを追加して、削除したい対象のクエリーの条件をprunable()で定義するだけです。

namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Prunable;
 
class UserLog extends Model
{
    use HasFactory;
    use Prunable;

    const UPDATED_AT = null; // updated_atの項目はない
 
    /**
     * Get the prunable model query.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function prunable()
    {
        return static::where('created_at', '<=', now()->subYear()); // 1年以上昔のレコードが対象
    }
}

そして、以下のコマンドの実行をクロンに設定にします。

$ php artisan model::prune

簡単でしょう。

削除する前に以下のように実際の削除なしの予行も可能です。

$ php artisan model:prune --pretend
8 [App\Models\UserLog] records will be pruned.

8個のレコードが削除されるよ、と知らせてくれます

tinkerで実際にプルーンを実行してみましょう。どのようなSQL文が実行されるか興味あります。

>>> Artisan::call('model:prune');
=> 0
>>> sql();
=> [
     [
       "query" => "select count(*) as aggregate from `user_logs` where `created_at` <= ?",
       "bindings" => [
         Illuminate\Support\Carbon @1616039564 {#3572
           date: 2021-03-18 03:52:44.536379 UTC (+00:00),
         },
       ],
       "time" => 4.94,
     ],
     [
       "query" => "select * from `user_logs` where `created_at` <= ? order by `id` asc limit 1000",
       "bindings" => [
         Illuminate\Support\Carbon @1616039586 {#3546
           date: 2021-03-18 03:53:06.837232 UTC (+00:00),
         },
       ],
       "time" => 1.36,
     ],
     [
       "query" => "delete from `user_logs` where `id` = ?",
       "bindings" => [
         1,
       ],
       "time" => 7.25,
     ],
     [
       "query" => "delete from `user_logs` where `id` = ?",
       "bindings" => [
         2,
       ],
       "time" => 3.11,
     ],
...

なるほど、1個1個レコードは削除されるのですね。これは、モデルのdeletingやdeletedのイベント処理を可能にするためです。

そのイベント処理が必要でないなら、一括実行も可能です。トレイトをPrunableからMassPrunableにするだけです。

namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\MassPrunable;
 
class UserLog extends Model
{
    use HasFactory;
    use MassPrunable;

    const UPDATED_AT = null;
 
    /**
     * Get the prunable model query.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function prunable()
    {
        return static::where('created_at', '<=', now()->subYear()); // 1年以上昔のレコードが対象
    }
}

再度、tinkerでSQL文をチェックすると、

>>> Artisan::call('model:prune');
=> 0
>>> sql();
=> [
     [
       "query" => "delete from `user_logs` where `created_at` <= ? limit 1000",
       "bindings" => [
         Illuminate\Support\Carbon @1616039978 {#3567
           date: 2021-03-18 03:59:38.916448 UTC (+00:00),
         },
       ],
       "time" => 5.61,
     ],
   ]
>>>

削除のSQL文1つだけになっていました。いたれりつくせりです。

By khino