Certified/app/Console/Commands/SendCertificateReminders.php
Joël van de Wouw ac340d125c
Some checks failed
linter / quality (push) Failing after 13s
tests / ci (8.3) (push) Failing after 2s
tests / ci (8.4) (push) Failing after 2s
tests / ci (8.5) (push) Failing after 3s
Initial commit
Certificate manager built on Laravel 13, Livewire 4, and Flux.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 14:44:58 +02:00

97 lines
3.1 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\Certificate;
use App\Models\User;
use App\Notifications\CertificateExpiredNotification;
use App\Notifications\CertificateExpiringNotification;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
class SendCertificateReminders extends Command
{
protected $signature = 'app:send-certificate-reminders';
protected $description = 'Notify users about certificates that are approaching or reaching their expiry date';
public function handle(): int
{
$today = Carbon::today();
$advanceCount = $this->sendAdvanceReminders($today);
$urgentCount = $this->sendExpiryReminders($today);
$this->info("Sent {$advanceCount} advance reminder(s) and {$urgentCount} expiry reminder(s).");
return self::SUCCESS;
}
/**
* Part 1: for each user, notify about certificates expiring exactly on
* their personal "months before" threshold.
*/
private function sendAdvanceReminders(Carbon $today): int
{
$sent = 0;
User::query()
->whereHas('certificates')
->with('certificates')
->chunkById(100, function ($users) use ($today, &$sent) {
foreach ($users as $user) {
$targetDate = $today->copy()->addMonths($user->reminder_months_before);
$certificates = $user->certificates()
->whereDate('expires_at', $targetDate)
->tap($this->notRemindedToday($today))
->get();
foreach ($certificates as $certificate) {
$user->notify(new CertificateExpiringNotification($certificate));
$certificate->forceFill(['last_reminded_at' => now()])->save();
$sent++;
}
}
});
return $sent;
}
/**
* Part 2: notify about certificates expiring today (across all users).
*/
private function sendExpiryReminders(Carbon $today): int
{
$sent = 0;
Certificate::query()
->whereDate('expires_at', $today)
->tap($this->notRemindedToday($today))
->with('user')
->chunkById(100, function ($certificates) use (&$sent) {
foreach ($certificates as $certificate) {
$certificate->user?->notify(new CertificateExpiredNotification($certificate));
$certificate->forceFill(['last_reminded_at' => now()])->save();
$sent++;
}
});
return $sent;
}
/**
* Skip certificates already reminded today, so a re-run (or a missed day
* followed by a catch-up run) never double-sends.
*/
private function notRemindedToday(Carbon $today): callable
{
return function (Builder $query) use ($today) {
$query->where(function (Builder $query) use ($today) {
$query->whereNull('last_reminded_at')
->orWhereDate('last_reminded_at', '<', $today);
});
};
}
}