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'); + } +};