Files
ladill-transfer/app/Services/Qr/QrAnalyticsService.php
T
isaaccladandCursor c1e3d8b3ac Initial Ladill Transfer app — file sharing with QR links.
Scaffolded from the Ladill mini/events extraction pattern: multi-file transfers,
retention controls, public landing pages, analytics, and Gitea deploy workflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 09:25:30 +00:00

92 lines
2.7 KiB
PHP

<?php
namespace App\Services\Qr;
use App\Models\QrCode;
use App\Models\QrScanEvent;
use App\Models\QrWallet;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class QrAnalyticsService
{
/**
* @return array<string, mixed>
*/
public function summaryFor(QrCode $qrCode): array
{
$now = now();
$events = QrScanEvent::query()->where('qr_code_id', $qrCode->id);
return [
'total_scans' => (int) $qrCode->scans_total,
'unique_scans' => (int) $qrCode->unique_scans_total,
'scans_7d' => (clone $events)->where('scanned_at', '>=', $now->copy()->subDays(7))->count(),
'scans_30d' => (clone $events)->where('scanned_at', '>=', $now->copy()->subDays(30))->count(),
'last_scanned_at' => $qrCode->last_scanned_at,
];
}
/**
* @return Collection<int, object{date: string, total: int}>
*/
public function dailyScans(QrCode $qrCode, int $days = 30): Collection
{
$start = now()->subDays($days - 1)->startOfDay();
$rows = QrScanEvent::query()
->selectRaw('DATE(scanned_at) as scan_date, COUNT(*) as total')
->where('qr_code_id', $qrCode->id)
->where('scanned_at', '>=', $start)
->groupBy('scan_date')
->orderBy('scan_date')
->get()
->keyBy('scan_date');
$series = collect();
for ($i = 0; $i < $days; $i++) {
$date = $start->copy()->addDays($i)->toDateString();
$series->push((object) [
'date' => $date,
'total' => (int) ($rows->get($date)?->total ?? 0),
]);
}
return $series;
}
/**
* @return array<int, array{label: string, total: int}>
*/
public function breakdown(QrCode $qrCode, string $column, int $limit = 5): array
{
return QrScanEvent::query()
->select($column, DB::raw('COUNT(*) as total'))
->where('qr_code_id', $qrCode->id)
->whereNotNull($column)
->groupBy($column)
->orderByDesc('total')
->limit($limit)
->get()
->map(fn ($row) => [
'label' => (string) $row->{$column},
'total' => (int) $row->total,
])
->all();
}
/**
* @return \Illuminate\Database\Eloquent\Collection<int, QrScanEvent>
*/
public function recentScans(QrCode $qrCode, int $limit = 20)
{
return QrScanEvent::query()
->where('qr_code_id', $qrCode->id)
->latest('scanned_at')
->limit($limit)
->get();
}
}