Багато таблиць ростуть безкінечно: журнали подій, прочитані сповіщення, прострочені токени, м'яко видалені записи. Laravel дає вбудований механізм їх періодично прибирати.
Prunable - описати, які записи застаріли:
use Illuminate\Database\Eloquent\Prunable;
class LoginAttempt extends Model
{
use Prunable;
public function prunable(): Builder
{
return static::where('created_at', '<=', now()->subMonth());
}
protected function pruning(): void
{
// перед видаленням кожної моделі: прибрати пов'язані файли тощо
}
}
Запуск за розкладом:
// routes/console.php
Schedule::command('model:prune')->daily();
Команда знаходить усі моделі з трейтами Prunable / MassPrunable у app/Models і видаляє записи, які повертає prunable().
php artisan model:prune --pretend # скільки буде видалено, без видалення
php artisan model:prune --model="App\Models\LoginAttempt"
Prunable проти MassPrunable:
Prunable |
MassPrunable |
|
|---|---|---|
| як видаляє | завантажує моделі порціями й викликає delete() для кожної |
один DELETE за запитом (порціями) |
події deleting/deleted, спостерігачі |
так | ні |
хук pruning() |
так | ні |
| швидкість на мільйонах рядків | повільно | швидко |
М'яке видалення: якщо модель використовує SoftDeletes, Prunable видаляє записи остаточно (forceDelete). Типова схема - м'яко видалені записи старші за 30 днів прибирати назавжди.
Що важливо:
- індекс на колонці умови (
created_at) - інакше щоденне очищення читає всю таблицю; - перший запуск на таблиці з роками даних може видалити мільйони рядків і довго тримати блокування - краще спершу почистити вручну порціями, а потім ввімкнути розклад;
MassPrunableне викликає подій - пов'язані файли, кеш, пошуковий індекс не очищаються автоматично;- політика зберігання даних (скільки зберігати журнали, персональні дані) - це юридичне питання, а
prunable()- місце, де вона виконується в коді; - тестування: у тесті можна створити старі й нові записи фабрикою, викликати
$this->artisan('model:prune')і перевірити, що лишилися лише нові.