From b5f898def4b9e3d65aee668439f49ec04fa23ff4 Mon Sep 17 00:00:00 2001 From: Ollie Date: Tue, 23 Jun 2026 00:04:21 +0200 Subject: [PATCH 01/13] Create schemas and models for implementing generic policies Bug: T428175 --- app/Policy.php | 55 +++++++++++++++++++ app/PolicyAcceptance.php | 55 +++++++++++++++++++ ...026_06_22_083853_create_policies_table.php | 36 ++++++++++++ ...083910_create_policy_acceptances_table.php | 38 +++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 app/Policy.php create mode 100644 app/PolicyAcceptance.php create mode 100644 database/migrations/2026_06_22_083853_create_policies_table.php create mode 100644 database/migrations/2026_06_22_083910_create_policy_acceptances_table.php diff --git a/app/Policy.php b/app/Policy.php new file mode 100644 index 00000000..59304b1a --- /dev/null +++ b/app/Policy.php @@ -0,0 +1,55 @@ +|Policy newModelQuery() + * @method static Builder|Policy newQuery() + * @method static Builder|Policy query() + * @method static Builder|Policy whereActiveFrom($value) + * @method static Builder|Policy whereContentVueFile($value) + * @method static Builder|Policy whereCreatedAt($value) + * @method static Builder|Policy whereId($value) + * @method static Builder|Policy wherePolicyType($value) + * @method static Builder|Policy whereUpdatedAt($value) + * + * @mixin Eloquent + */ +class Policy extends Model { + // TODO: also create a factory (and seeder)? + + // define which attributes are mass assignable + protected $fillable = [ + 'policy_type', + 'active_from', + 'content_vue_file', + ]; + + // define the default value of model attributes when a new instance is created + protected $attributes = [ + 'active_from' => null, + ]; + + protected function casts(): array { + return [ + // cast `active_from` to a CarbonImmutable instance rather than a string + 'active_from' => 'immutable_date', + + // TODO: should we make Laravel use CarbonImmutable globally instead of casting in models? + 'created_at' => 'immutable_datetime', + 'updated_at' => 'immutable_datetime', + ]; + } +} diff --git a/app/PolicyAcceptance.php b/app/PolicyAcceptance.php new file mode 100644 index 00000000..be140a92 --- /dev/null +++ b/app/PolicyAcceptance.php @@ -0,0 +1,55 @@ +|PolicyAcceptance newModelQuery() + * @method static Builder|PolicyAcceptance newQuery() + * @method static Builder|PolicyAcceptance query() + * @method static Builder|PolicyAcceptance whereAcceptedAt($value) + * @method static Builder|PolicyAcceptance whereCreatedAt($value) + * @method static Builder|PolicyAcceptance whereId($value) + * @method static Builder|PolicyAcceptance wherePolicyId($value) + * @method static Builder|PolicyAcceptance whereUpdatedAt($value) + * @method static Builder|PolicyAcceptance whereUserId($value) + * + * @mixin Eloquent + */ +class PolicyAcceptance extends Model { + // TODO: also create a factory (and seeder)? + + // define which attributes are mass assignable + protected $fillable = [ + 'user_id', + 'policy_id', + // Don't allow `accepted_at` to be mass assigned? Most of the time this will be set to the current timestamp by the database. + // 'accepted_at', + ]; + + protected function casts(): array { + return [ + // cast `accepted_at` to a `CarbonImmutable` instance rather than a string + 'accepted_at' => 'immutable_datetime', + + // TODO: should we make Laravel use CarbonImmutable globally instead of casting in models? + 'created_at' => 'immutable_datetime', + 'updated_at' => 'immutable_datetime', + ]; + } +} diff --git a/database/migrations/2026_06_22_083853_create_policies_table.php b/database/migrations/2026_06_22_083853_create_policies_table.php new file mode 100644 index 00000000..004dc1e3 --- /dev/null +++ b/database/migrations/2026_06_22_083853_create_policies_table.php @@ -0,0 +1,36 @@ +id(); + // TODO: or should this column name just be `type`? + $table->enum('policy_type', ['terms-of-use', 'hosting-policy']); + $table->date('active_from')->nullable()->default(null); + // TODO: or `content_reference`? + $table->string('content_vue_file', 255); + + // explicitly define the default timestamp columns so that they are not nullable + $table->timestamp('created_at'); + $table->timestamp('updated_at'); + + // TODO: won't be able to create two upcoming policies of the same type with `active_from` set to `null`, + // but that seems like a reasonable restriction + $table->unique(['policy_type', 'active_from']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::dropIfExists('policies'); + } +}; diff --git a/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php b/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php new file mode 100644 index 00000000..11c119a8 --- /dev/null +++ b/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php @@ -0,0 +1,38 @@ +id(); + + // Can't use the `foreignId()` method because the `users.id` column isn't an unsigned big integer + $table->unsignedInteger('user_id'); + $table->foreign('user_id')->references('id')->on('users')->restrictOnUpdate()->restrictOnDelete(); + + $table->foreignId('policy_id')->constrained()->restrictOnUpdate()->restrictOnDelete(); + + // Using a separate `accepted_at` column rather than renaming the default `created_at` column because: + // * it reduces confuses by remaining consistent with other tables that use the default columns + // * `accepted_at` will be before `created_at` when backfilling the terms-of-use acceptances + $table->timestamp('accepted_at')->useCurrent(); + + // explicitly define the default timestamp columns so that they are not nullable + $table->timestamp('created_at'); + $table->timestamp('updated_at'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::dropIfExists('policy_acceptances'); + } +}; From 695a643edd47719d776ed4ad1cab6841274b6b14 Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Wed, 24 Jun 2026 11:03:24 +0100 Subject: [PATCH 02/13] Use Eloquent built in timestamps We are using the built in Eqloquent timestamp method to introduce nullable timestamp columns. The framework seems to have discussed these issues at length[1] and determined that this is the best setup to prevent issues with ON UPDATE CURRENT_TIMESTAMP behaviour. [1] https://github.com/laravel/framework/issues/12490 --- .../2026_06_22_083853_create_policies_table.php | 6 +++--- .../2026_06_22_083910_create_policy_acceptances_table.php | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/database/migrations/2026_06_22_083853_create_policies_table.php b/database/migrations/2026_06_22_083853_create_policies_table.php index 004dc1e3..878da1d3 100644 --- a/database/migrations/2026_06_22_083853_create_policies_table.php +++ b/database/migrations/2026_06_22_083853_create_policies_table.php @@ -17,9 +17,9 @@ public function up(): void { // TODO: or `content_reference`? $table->string('content_vue_file', 255); - // explicitly define the default timestamp columns so that they are not nullable - $table->timestamp('created_at'); - $table->timestamp('updated_at'); + // Use Eloquent built in to create nullable `created_at` and `updated_at` + // timestamp fields + $table->timestamps(); // TODO: won't be able to create two upcoming policies of the same type with `active_from` set to `null`, // but that seems like a reasonable restriction diff --git a/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php b/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php index 11c119a8..f408638f 100644 --- a/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php +++ b/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php @@ -18,14 +18,14 @@ public function up(): void { $table->foreignId('policy_id')->constrained()->restrictOnUpdate()->restrictOnDelete(); + // Use Eloquent built in to create nullable `created_at` and `updated_at` + // timestamp fields + $table->timestamps(); + // Using a separate `accepted_at` column rather than renaming the default `created_at` column because: // * it reduces confuses by remaining consistent with other tables that use the default columns // * `accepted_at` will be before `created_at` when backfilling the terms-of-use acceptances $table->timestamp('accepted_at')->useCurrent(); - - // explicitly define the default timestamp columns so that they are not nullable - $table->timestamp('created_at'); - $table->timestamp('updated_at'); }); } From 3c34946587e1a2a973042c36a9568b92ce5ece2f Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Wed, 24 Jun 2026 11:16:35 +0100 Subject: [PATCH 03/13] typo fix --- .../2026_06_22_083910_create_policy_acceptances_table.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php b/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php index f408638f..791af1bb 100644 --- a/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php +++ b/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php @@ -23,7 +23,7 @@ public function up(): void { $table->timestamps(); // Using a separate `accepted_at` column rather than renaming the default `created_at` column because: - // * it reduces confuses by remaining consistent with other tables that use the default columns + // * it reduces confusion by remaining consistent with other tables that use the default columns // * `accepted_at` will be before `created_at` when backfilling the terms-of-use acceptances $table->timestamp('accepted_at')->useCurrent(); }); From 3077811982f4693a8e771702a61a2e3f059e61b9 Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Wed, 24 Jun 2026 12:14:14 +0100 Subject: [PATCH 04/13] Add small test for Policy --- tests/PolicyTest.php | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/PolicyTest.php diff --git a/tests/PolicyTest.php b/tests/PolicyTest.php new file mode 100644 index 00000000..875081d5 --- /dev/null +++ b/tests/PolicyTest.php @@ -0,0 +1,29 @@ + 'terms-of-use', + 'active_from' => $yesterday, + 'content_vue_file' => 'terms-of-use/example.vue', + ] + ); + $policy->save(); + + $this->assertDatabaseHas('policies', [ + 'policy_type' => 'terms-of-use', + 'active_from' => $yesterday, + 'content_vue_file' => 'terms-of-use/example.vue', + ]); + } +} From 68e1d548511ebbc19e1df63f4afbf7dd2257d5e4 Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Wed, 24 Jun 2026 12:33:28 +0100 Subject: [PATCH 05/13] Add policy acceptance test --- tests/PolicyAcceptanceTest.php | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 tests/PolicyAcceptanceTest.php diff --git a/tests/PolicyAcceptanceTest.php b/tests/PolicyAcceptanceTest.php new file mode 100644 index 00000000..9a3cdc8d --- /dev/null +++ b/tests/PolicyAcceptanceTest.php @@ -0,0 +1,49 @@ +create(); + $this->user_id = $user->id; + $policy = Policy::create( + [ + 'policy_type' => 'terms-of-use', + 'active_from' => CarbonImmutable::yesterday(), + 'content_vue_file' => 'terms-of-use/example.vue', + ]); + $policy->save(); + $this->policy_id = $policy->id; + } + + public function testCreatesAndSavesSuccessfully(): void { + $policyAcceptance = new PolicyAcceptance( + [ + 'user_id' => $this->user_id, + 'policy_id' => $this->policy_id, + ] + ); + $policyAcceptance->save(); + $policyAcceptance->refresh(); + + $this->assertDatabaseHas('policy_acceptances', [ + 'user_id' => $this->user_id, + 'policy_id' => $this->policy_id, + ]); + + $this->assertNotEmpty($policyAcceptance->accepted_at); + } +} From 19db0ceffbfd40038f2c1c36432ff1b979b2f36f Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Wed, 24 Jun 2026 15:13:24 +0100 Subject: [PATCH 06/13] Remove most TODO For nice to have's like Factory and Seeder we should do in a follow-up. Still need to come to a decision on casting the timestamps; particularly the built in. --- app/Policy.php | 2 -- app/PolicyAcceptance.php | 13 ++++++++----- .../2026_06_22_083853_create_policies_table.php | 5 +---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/app/Policy.php b/app/Policy.php index 59304b1a..8e539f22 100644 --- a/app/Policy.php +++ b/app/Policy.php @@ -28,8 +28,6 @@ * @mixin Eloquent */ class Policy extends Model { - // TODO: also create a factory (and seeder)? - // define which attributes are mass assignable protected $fillable = [ 'policy_type', diff --git a/app/PolicyAcceptance.php b/app/PolicyAcceptance.php index be140a92..b0535be3 100644 --- a/app/PolicyAcceptance.php +++ b/app/PolicyAcceptance.php @@ -32,14 +32,16 @@ * @mixin Eloquent */ class PolicyAcceptance extends Model { - // TODO: also create a factory (and seeder)? - - // define which attributes are mass assignable protected $fillable = [ 'user_id', 'policy_id', - // Don't allow `accepted_at` to be mass assigned? Most of the time this will be set to the current timestamp by the database. - // 'accepted_at', + + ]; + + protected $guarded = [ + // Don't allow `accepted_at` to be mass assigned. + // Most of the time this will be set to the current timestamp by the database. + 'accepted_at', ]; protected function casts(): array { @@ -48,6 +50,7 @@ protected function casts(): array { 'accepted_at' => 'immutable_datetime', // TODO: should we make Laravel use CarbonImmutable globally instead of casting in models? + // TODO: should we do any casting with these built ins? 'created_at' => 'immutable_datetime', 'updated_at' => 'immutable_datetime', ]; diff --git a/database/migrations/2026_06_22_083853_create_policies_table.php b/database/migrations/2026_06_22_083853_create_policies_table.php index 878da1d3..197902a9 100644 --- a/database/migrations/2026_06_22_083853_create_policies_table.php +++ b/database/migrations/2026_06_22_083853_create_policies_table.php @@ -11,18 +11,15 @@ public function up(): void { Schema::create('policies', function (Blueprint $table) { $table->id(); - // TODO: or should this column name just be `type`? $table->enum('policy_type', ['terms-of-use', 'hosting-policy']); $table->date('active_from')->nullable()->default(null); - // TODO: or `content_reference`? $table->string('content_vue_file', 255); // Use Eloquent built in to create nullable `created_at` and `updated_at` // timestamp fields $table->timestamps(); - // TODO: won't be able to create two upcoming policies of the same type with `active_from` set to `null`, - // but that seems like a reasonable restriction + // This prevents two upcoming policies of the same type with `active_from` set to `null`, $table->unique(['policy_type', 'active_from']); }); } From f57f71cf6458b6e3166ba892564293ab179d96f7 Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Wed, 24 Jun 2026 20:32:10 +0100 Subject: [PATCH 07/13] Rerun ide-helper Run php artisan ide-helper:models --reset "App\PolicyAcceptance" "App\Policy" --- app/Policy.php | 6 +++--- app/PolicyAcceptance.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Policy.php b/app/Policy.php index 8e539f22..fe81e5e0 100644 --- a/app/Policy.php +++ b/app/Policy.php @@ -8,12 +8,12 @@ use Illuminate\Database\Eloquent\Model; /** - * @property-read int $id + * @property int $id * @property string $policy_type * @property CarbonImmutable|null $active_from * @property string $content_vue_file - * @property CarbonImmutable $created_at - * @property CarbonImmutable $updated_at + * @property CarbonImmutable|null $created_at + * @property CarbonImmutable|null $updated_at * * @method static Builder|Policy newModelQuery() * @method static Builder|Policy newQuery() diff --git a/app/PolicyAcceptance.php b/app/PolicyAcceptance.php index b0535be3..228f6a7c 100644 --- a/app/PolicyAcceptance.php +++ b/app/PolicyAcceptance.php @@ -12,12 +12,12 @@ * - it reduces confuses by remaining consistent with other models that use the default timestamps * - `accepted_at` will be before `created_at` when backfilling the terms-of-use acceptances * - * @property-read int $id + * @property int $id * @property int $user_id * @property int $policy_id + * @property CarbonImmutable|null $created_at + * @property CarbonImmutable|null $updated_at * @property CarbonImmutable $accepted_at - * @property CarbonImmutable $created_at - * @property CarbonImmutable $updated_at * * @method static Builder|PolicyAcceptance newModelQuery() * @method static Builder|PolicyAcceptance newQuery() From 4f36d78ed027c9e269bbcfdcc30e768a036019c6 Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Wed, 24 Jun 2026 21:25:44 +0100 Subject: [PATCH 08/13] do not cast globally for now --- app/Policy.php | 1 - app/PolicyAcceptance.php | 2 -- 2 files changed, 3 deletions(-) diff --git a/app/Policy.php b/app/Policy.php index fe81e5e0..9faa6b0d 100644 --- a/app/Policy.php +++ b/app/Policy.php @@ -45,7 +45,6 @@ protected function casts(): array { // cast `active_from` to a CarbonImmutable instance rather than a string 'active_from' => 'immutable_date', - // TODO: should we make Laravel use CarbonImmutable globally instead of casting in models? 'created_at' => 'immutable_datetime', 'updated_at' => 'immutable_datetime', ]; diff --git a/app/PolicyAcceptance.php b/app/PolicyAcceptance.php index 228f6a7c..09793e9c 100644 --- a/app/PolicyAcceptance.php +++ b/app/PolicyAcceptance.php @@ -49,8 +49,6 @@ protected function casts(): array { // cast `accepted_at` to a `CarbonImmutable` instance rather than a string 'accepted_at' => 'immutable_datetime', - // TODO: should we make Laravel use CarbonImmutable globally instead of casting in models? - // TODO: should we do any casting with these built ins? 'created_at' => 'immutable_datetime', 'updated_at' => 'immutable_datetime', ]; From b96b64561940cc3374c5b9c26a7ced4b61c35474 Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Thu, 25 Jun 2026 08:25:23 +0100 Subject: [PATCH 09/13] address Dat's review comments --- app/PolicyAcceptance.php | 3 +-- tests/PolicyAcceptanceTest.php | 17 +++++++++-------- tests/PolicyTest.php | 10 ++++------ 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/app/PolicyAcceptance.php b/app/PolicyAcceptance.php index 09793e9c..fb30e173 100644 --- a/app/PolicyAcceptance.php +++ b/app/PolicyAcceptance.php @@ -9,7 +9,7 @@ /** * This model uses a separate `accepted_at` property rather than renaming the default `created_at` property because: - * - it reduces confuses by remaining consistent with other models that use the default timestamps + * - it remains consistent with other models that use the default timestamps * - `accepted_at` will be before `created_at` when backfilling the terms-of-use acceptances * * @property int $id @@ -35,7 +35,6 @@ class PolicyAcceptance extends Model { protected $fillable = [ 'user_id', 'policy_id', - ]; protected $guarded = [ diff --git a/tests/PolicyAcceptanceTest.php b/tests/PolicyAcceptanceTest.php index 9a3cdc8d..15039dd9 100644 --- a/tests/PolicyAcceptanceTest.php +++ b/tests/PolicyAcceptanceTest.php @@ -11,14 +11,14 @@ class PolicyAcceptanceTest extends TestCase { use RefreshDatabase; - protected int $user_id; + protected int $userId; - protected int $policy_id; + protected int $policyId; protected function setUp(): void { parent::setUp(); $user = User::factory()->create(); - $this->user_id = $user->id; + $this->userId = $user->id; $policy = Policy::create( [ 'policy_type' => 'terms-of-use', @@ -26,24 +26,25 @@ protected function setUp(): void { 'content_vue_file' => 'terms-of-use/example.vue', ]); $policy->save(); - $this->policy_id = $policy->id; + $this->policyId = $policy->id; } public function testCreatesAndSavesSuccessfully(): void { $policyAcceptance = new PolicyAcceptance( [ - 'user_id' => $this->user_id, - 'policy_id' => $this->policy_id, + 'user_id' => $this->userId, + 'policy_id' => $this->policyId, ] ); $policyAcceptance->save(); $policyAcceptance->refresh(); $this->assertDatabaseHas('policy_acceptances', [ - 'user_id' => $this->user_id, - 'policy_id' => $this->policy_id, + 'user_id' => $this->userId, + 'policy_id' => $this->policyId, ]); $this->assertNotEmpty($policyAcceptance->accepted_at); + $this->assertInstanceOf(CarbonImmutable::class, $policyAcceptance->accepted_at); } } diff --git a/tests/PolicyTest.php b/tests/PolicyTest.php index 875081d5..f060b937 100644 --- a/tests/PolicyTest.php +++ b/tests/PolicyTest.php @@ -9,20 +9,18 @@ class PolicyTest extends TestCase { use RefreshDatabase; - public function testCreatesAndSavesSuccessfully(): void { - $yesterday = Carbon::yesterday(); - $policy = Policy::create( + public function testCreatesSuccessfully(): void { + Policy::create( [ 'policy_type' => 'terms-of-use', - 'active_from' => $yesterday, + 'active_from' => Carbon::createFromDate(2025, 4, 1), 'content_vue_file' => 'terms-of-use/example.vue', ] ); - $policy->save(); $this->assertDatabaseHas('policies', [ 'policy_type' => 'terms-of-use', - 'active_from' => $yesterday, + 'active_from' => '2025-04-01', 'content_vue_file' => 'terms-of-use/example.vue', ]); } From 36951424b2ed33e54181d42a3aaeac94ce11fa9e Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Thu, 25 Jun 2026 08:39:07 +0100 Subject: [PATCH 10/13] add PolicyAcceptanceTest::testAcceptedAtIgnoresMassAssignment --- tests/PolicyAcceptanceTest.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/PolicyAcceptanceTest.php b/tests/PolicyAcceptanceTest.php index 15039dd9..01e08518 100644 --- a/tests/PolicyAcceptanceTest.php +++ b/tests/PolicyAcceptanceTest.php @@ -47,4 +47,15 @@ public function testCreatesAndSavesSuccessfully(): void { $this->assertNotEmpty($policyAcceptance->accepted_at); $this->assertInstanceOf(CarbonImmutable::class, $policyAcceptance->accepted_at); } + + public function testAcceptedAtIgnoresMassAssignment(): void { + $policyAcceptance = PolicyAcceptance::create( + [ + 'user_id' => $this->userId, + 'policy_id' => $this->policyId, + 'accepted_at' => CarbonImmutable::createFromDate(2026, 1, 1), + ] + ); + $this->assertNull($policyAcceptance->accepted_at); + } } From 1a90b458d7f8116ef313d3fe705639a5901ed690 Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Fri, 26 Jun 2026 10:40:35 +0100 Subject: [PATCH 11/13] remove another save after create --- tests/PolicyAcceptanceTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/PolicyAcceptanceTest.php b/tests/PolicyAcceptanceTest.php index 01e08518..4396fd7f 100644 --- a/tests/PolicyAcceptanceTest.php +++ b/tests/PolicyAcceptanceTest.php @@ -25,7 +25,6 @@ protected function setUp(): void { 'active_from' => CarbonImmutable::yesterday(), 'content_vue_file' => 'terms-of-use/example.vue', ]); - $policy->save(); $this->policyId = $policy->id; } From eaf38eb7f6a88e4939d61b6783aeb445f5f32334 Mon Sep 17 00:00:00 2001 From: Ollie Date: Tue, 30 Jun 2026 01:18:19 +0100 Subject: [PATCH 12/13] Be consistent in using `CarbonImmutable` everywhere --- tests/PolicyTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/PolicyTest.php b/tests/PolicyTest.php index f060b937..bec66b92 100644 --- a/tests/PolicyTest.php +++ b/tests/PolicyTest.php @@ -3,7 +3,7 @@ namespace Tests; use App\Policy; -use Carbon\Carbon; +use Carbon\CarbonImmutable; use Illuminate\Foundation\Testing\RefreshDatabase; class PolicyTest extends TestCase { @@ -13,7 +13,7 @@ public function testCreatesSuccessfully(): void { Policy::create( [ 'policy_type' => 'terms-of-use', - 'active_from' => Carbon::createFromDate(2025, 4, 1), + 'active_from' => CarbonImmutable::createMidnightDate(2025, 4, 1), 'content_vue_file' => 'terms-of-use/example.vue', ] ); From 19e0549c216db83ed557b27ec64736325bf8dacd Mon Sep 17 00:00:00 2001 From: Ollie Date: Tue, 30 Jun 2026 01:19:25 +0100 Subject: [PATCH 13/13] Update comments in db migrations --- .../migrations/2026_06_22_083853_create_policies_table.php | 3 +-- .../2026_06_22_083910_create_policy_acceptances_table.php | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/database/migrations/2026_06_22_083853_create_policies_table.php b/database/migrations/2026_06_22_083853_create_policies_table.php index 197902a9..d1b3be37 100644 --- a/database/migrations/2026_06_22_083853_create_policies_table.php +++ b/database/migrations/2026_06_22_083853_create_policies_table.php @@ -15,8 +15,7 @@ public function up(): void { $table->date('active_from')->nullable()->default(null); $table->string('content_vue_file', 255); - // Use Eloquent built in to create nullable `created_at` and `updated_at` - // timestamp fields + // Use Eloquent built in to create nullable `created_at` and `updated_at` timestamp fields $table->timestamps(); // This prevents two upcoming policies of the same type with `active_from` set to `null`, diff --git a/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php b/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php index 791af1bb..9abfbd25 100644 --- a/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php +++ b/database/migrations/2026_06_22_083910_create_policy_acceptances_table.php @@ -18,12 +18,11 @@ public function up(): void { $table->foreignId('policy_id')->constrained()->restrictOnUpdate()->restrictOnDelete(); - // Use Eloquent built in to create nullable `created_at` and `updated_at` - // timestamp fields + // Use Eloquent built in to create nullable `created_at` and `updated_at` timestamp fields $table->timestamps(); // Using a separate `accepted_at` column rather than renaming the default `created_at` column because: - // * it reduces confusion by remaining consistent with other tables that use the default columns + // * it reduces confusion by remaining consistent with other tables that use these default columns // * `accepted_at` will be before `created_at` when backfilling the terms-of-use acceptances $table->timestamp('accepted_at')->useCurrent(); });