Spaces:
Running
Running
| declare(strict_types=1); | |
| namespace App\Domain\Analytics; | |
| use App\Infrastructure\Database\BaseRepository; | |
| use App\Infrastructure\Ids\UuidGenerator; | |
| use Illuminate\Support\Carbon; | |
| use Illuminate\Support\Facades\DB; | |
| final class AnalyticsVisitorRepository extends BaseRepository | |
| { | |
| public function __construct(private readonly UuidGenerator $ids) {} | |
| protected function table(): string | |
| { | |
| return 'analytics_visitors'; | |
| } | |
| protected function hasDeletedAtColumn(): bool | |
| { | |
| return false; | |
| } | |
| public function findByToken(string $token): ?\stdClass | |
| { | |
| return DB::table($this->table())->where('visitor_token', $token)->first(); | |
| } | |
| public function findById(string $id): ?\stdClass | |
| { | |
| return DB::table($this->table())->where('id', $id)->first(); | |
| } | |
| /** | |
| * Find-or-create a visitor by token, updating last_seen_at on every call. | |
| * Returns the visitor id. | |
| */ | |
| public function upsert(string $token, ?string $memberId): string | |
| { | |
| $now = Carbon::now()->toDateTimeString(); | |
| $existing = $this->findByToken($token); | |
| if ($existing) { | |
| $patch = ['last_seen_at' => $now, 'updated_at' => $now]; | |
| if ($memberId !== null && $existing->member_id === null) { | |
| $patch['member_id'] = $memberId; | |
| } | |
| DB::table($this->table())->where('id', $existing->id)->update($patch); | |
| return $existing->id; | |
| } | |
| $id = $this->ids->generate(); | |
| DB::table($this->table())->insert([ | |
| 'id' => $id, | |
| 'visitor_token' => $token, | |
| 'member_id' => $memberId, | |
| 'first_seen_at' => $now, | |
| 'last_seen_at' => $now, | |
| 'created_at' => $now, | |
| 'updated_at' => $now, | |
| ]); | |
| return $id; | |
| } | |
| /** | |
| * Link an anonymous visitor to a known member after login. | |
| * Only writes when member_id is not already set to prevent duplicate stitches. | |
| */ | |
| public function stitch(string $visitorId, string $memberId): void | |
| { | |
| DB::table($this->table()) | |
| ->where('id', $visitorId) | |
| ->whereNull('member_id') | |
| ->update([ | |
| 'member_id' => $memberId, | |
| 'updated_at' => Carbon::now()->toDateTimeString(), | |
| ]); | |
| } | |
| } | |