From 0d162b284f785b1368ca55a24f74974e52ac7d05 Mon Sep 17 00:00:00 2001 From: Dat Date: Tue, 6 Jan 2026 16:06:34 +0100 Subject: [PATCH 1/2] Add a hook to ensure there is only one active ToU version --- app/TermsOfUseVersion.php | 21 +++++++++++++- tests/Models/TermsOfUseVersionTest.php | 40 ++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 tests/Models/TermsOfUseVersionTest.php diff --git a/app/TermsOfUseVersion.php b/app/TermsOfUseVersion.php index e4472407..64f50eaa 100644 --- a/app/TermsOfUseVersion.php +++ b/app/TermsOfUseVersion.php @@ -29,7 +29,26 @@ class TermsOfUseVersion extends Model { 'active' => 'boolean', ]; + /** + * Get the active ToU version. + */ public static function latestActiveVersion(): ?self { - return self::query()->where('active', true)->latest()->first(); + return self::query()->where('active', true)->first(); + } + + /** + * Ensure only one ToU version remains active after any save operation. + */ + protected static function booted(): void { + static::saving(function (self $model): void { + if (!$model->active) { + return; + } + + self::query() + ->where('version', '!=', $model->version) + ->where('active', true) + ->update(['active' => false]); + }); } } diff --git a/tests/Models/TermsOfUseVersionTest.php b/tests/Models/TermsOfUseVersionTest.php new file mode 100644 index 00000000..bba866e4 --- /dev/null +++ b/tests/Models/TermsOfUseVersionTest.php @@ -0,0 +1,40 @@ + '2024-01-01', + 'active' => true, + ]); + + $second = TermsOfUseVersion::create([ + 'version' => '2025-01-01', + 'active' => true, + ]); + + $this->assertFalse($first->fresh()->active); + $this->assertTrue($second->fresh()->active); + } + + public function testSavingInactiveVersionDoesNotAffectExistingActiveVersion(): void { + $active = TermsOfUseVersion::create([ + 'version' => '2024-03-01', + 'active' => true, + ]); + + TermsOfUseVersion::create([ + 'version' => '2024-04-01', + 'active' => false, + ]); + + $this->assertTrue($active->fresh()->active); + } +} From a2d5a4086cb03ce1c744ee31c7e7a05c7f655f40 Mon Sep 17 00:00:00 2001 From: Dat Date: Tue, 6 Jan 2026 16:47:55 +0100 Subject: [PATCH 2/2] Rename latestActiveVersion to getActiveVersion --- app/Jobs/UserCreateJob.php | 2 +- app/Jobs/UserTouAcceptanceJob.php | 2 +- app/TermsOfUseVersion.php | 2 +- tests/Jobs/UserTermsOfUseAcceptanceTest.php | 4 ++-- tests/Jobs/UserTouAcceptanceJobTest.php | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Jobs/UserCreateJob.php b/app/Jobs/UserCreateJob.php index 42c0742a..e875eda5 100644 --- a/app/Jobs/UserCreateJob.php +++ b/app/Jobs/UserCreateJob.php @@ -33,7 +33,7 @@ public function handle() { 'verified' => $this->verified, ]); - $latest = TermsOfUseVersion::latestActiveVersion(); + $latest = TermsOfUseVersion::getActiveVersion(); if ($latest) { UserTermsOfUseAcceptance::create([ 'user_id' => $user->id, diff --git a/app/Jobs/UserTouAcceptanceJob.php b/app/Jobs/UserTouAcceptanceJob.php index 964d3ff4..6bdcff45 100644 --- a/app/Jobs/UserTouAcceptanceJob.php +++ b/app/Jobs/UserTouAcceptanceJob.php @@ -28,7 +28,7 @@ public function handle(): void { try { UserTermsOfUseAcceptance::create([ 'user_id' => $user->id, - 'tou_version' => TermsOfUseVersion::latestActiveVersion()->version, + 'tou_version' => TermsOfUseVersion::getActiveVersion()->version, 'tou_accepted_at' => $user->created_at, ]); } catch (Throwable $exception) { diff --git a/app/TermsOfUseVersion.php b/app/TermsOfUseVersion.php index 64f50eaa..ada1b1f6 100644 --- a/app/TermsOfUseVersion.php +++ b/app/TermsOfUseVersion.php @@ -32,7 +32,7 @@ class TermsOfUseVersion extends Model { /** * Get the active ToU version. */ - public static function latestActiveVersion(): ?self { + public static function getActiveVersion(): ?self { return self::query()->where('active', true)->first(); } diff --git a/tests/Jobs/UserTermsOfUseAcceptanceTest.php b/tests/Jobs/UserTermsOfUseAcceptanceTest.php index ddbf70ec..cc431d8b 100644 --- a/tests/Jobs/UserTermsOfUseAcceptanceTest.php +++ b/tests/Jobs/UserTermsOfUseAcceptanceTest.php @@ -19,14 +19,14 @@ public function testUserCreationCreatesTouAcceptance(): void { $this->assertDatabaseHas('tou_acceptances', [ 'user_id' => $user->id, - 'tou_version' => TermsOfUseVersion::latestActiveVersion()->version, + 'tou_version' => TermsOfUseVersion::getActiveVersion()->version, ]); $rows = UserTermsOfUseAcceptance::where('user_id', $user->id)->get(); $this->assertCount(1, $rows); $acceptance = $rows->first(); - $this->assertSame(TermsOfUseVersion::latestActiveVersion()->version, $acceptance->tou_version); + $this->assertSame(TermsOfUseVersion::getActiveVersion()->version, $acceptance->tou_version); $this->assertNotNull($acceptance->tou_accepted_at); } } diff --git a/tests/Jobs/UserTouAcceptanceJobTest.php b/tests/Jobs/UserTouAcceptanceJobTest.php index 5f804ca5..e7a846e5 100644 --- a/tests/Jobs/UserTouAcceptanceJobTest.php +++ b/tests/Jobs/UserTouAcceptanceJobTest.php @@ -26,7 +26,7 @@ public function testTouAcceptanceJob(): void { (new CreateFirstTermsOfUseVersionJob)->handle(); (new UserTouAcceptanceJob)->handle(); - $latest = TermsOfUseVersion::latestActiveVersion()->version; + $latest = TermsOfUseVersion::getActiveVersion()->version; $this->assertDatabaseHas('tou_acceptances', ['user_id' => $u1->id, 'tou_version' => $latest]); $this->assertDatabaseHas('tou_acceptances', ['user_id' => $u2->id, 'tou_version' => $latest]);