From c815597a9872fa5a0e083818e9ec7f654aa89361 Mon Sep 17 00:00:00 2001 From: Ovidiu U Date: Sat, 4 Apr 2026 19:09:19 +0100 Subject: [PATCH] feat: add searches table, model, and factory for API search logging Co-Authored-By: Claude Sonnet 4.6 --- app/Models/Search.php | 24 ++++++++++++++ database/factories/SearchFactory.php | 28 ++++++++++++++++ ...026_04_04_180819_create_searches_table.php | 32 +++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 app/Models/Search.php create mode 100644 database/factories/SearchFactory.php create mode 100644 database/migrations/2026_04_04_180819_create_searches_table.php diff --git a/app/Models/Search.php b/app/Models/Search.php new file mode 100644 index 0000000..e46e96f --- /dev/null +++ b/app/Models/Search.php @@ -0,0 +1,24 @@ + */ + use HasFactory; + + public $timestamps = false; + + protected function casts(): array + { + return [ + 'searched_at' => 'datetime', + ]; + } +} diff --git a/database/factories/SearchFactory.php b/database/factories/SearchFactory.php new file mode 100644 index 0000000..b889997 --- /dev/null +++ b/database/factories/SearchFactory.php @@ -0,0 +1,28 @@ + */ +class SearchFactory extends Factory +{ + public function definition(): array + { + $lowest = fake()->numberBetween(12000, 15000); + $highest = $lowest + fake()->numberBetween(100, 3000); + + return [ + 'lat_bucket' => round(fake()->latitude(49.9, 60.9), 2), + 'lng_bucket' => round(fake()->longitude(-8.2, 1.8), 2), + 'fuel_type' => fake()->randomElement(['b7_standard', 'e10', 'e5']), + 'results_count' => fake()->numberBetween(5, 100), + 'lowest_pence' => $lowest, + 'highest_pence' => $highest, + 'avg_pence' => round(($lowest + $highest) / 2, 2), + 'searched_at' => now(), + 'ip_hash' => hash('sha256', fake()->ipv4()), + ]; + } +} diff --git a/database/migrations/2026_04_04_180819_create_searches_table.php b/database/migrations/2026_04_04_180819_create_searches_table.php new file mode 100644 index 0000000..b17a0b6 --- /dev/null +++ b/database/migrations/2026_04_04_180819_create_searches_table.php @@ -0,0 +1,32 @@ +bigIncrements('id'); + $table->decimal('lat_bucket', 5, 2)->comment('Latitude rounded to 2dp (~1km precision) for privacy'); + $table->decimal('lng_bucket', 5, 2)->comment('Longitude rounded to 2dp for privacy'); + $table->string('fuel_type', 20); + $table->unsignedSmallInteger('results_count'); + $table->unsignedSmallInteger('lowest_pence')->nullable()->comment('Cheapest price found in pence × 100'); + $table->unsignedSmallInteger('highest_pence')->nullable()->comment('Most expensive price found in pence × 100'); + $table->decimal('avg_pence', 8, 2)->nullable()->comment('Mean price across results in pence × 100'); + $table->dateTime('searched_at'); + $table->string('ip_hash', 64)->comment('SHA-256 of requester IP — non-reversible, for unique count only'); + + $table->index(['searched_at', 'fuel_type']); + $table->index('ip_hash'); + }); + } + + public function down(): void + { + Schema::dropIfExists('searches'); + } +};