From 7f153fb08d5d2eb53cef044308f86f66aba1a663 Mon Sep 17 00:00:00 2001 From: Ovidiu U Date: Fri, 3 Apr 2026 18:46:53 +0100 Subject: [PATCH] feat: add Station, StationPrice, StationPriceCurrent, StationPriceArchive models and factories --- app/Models/Station.php | 56 +++++++++++++++++++ app/Models/StationPrice.php | 34 +++++++++++ app/Models/StationPriceArchive.php | 29 ++++++++++ app/Models/StationPriceCurrent.php | 41 ++++++++++++++ database/factories/StationFactory.php | 50 +++++++++++++++++ .../factories/StationPriceCurrentFactory.php | 24 ++++++++ database/factories/StationPriceFactory.php | 24 ++++++++ 7 files changed, 258 insertions(+) create mode 100644 app/Models/Station.php create mode 100644 app/Models/StationPrice.php create mode 100644 app/Models/StationPriceArchive.php create mode 100644 app/Models/StationPriceCurrent.php create mode 100644 database/factories/StationFactory.php create mode 100644 database/factories/StationPriceCurrentFactory.php create mode 100644 database/factories/StationPriceFactory.php diff --git a/app/Models/Station.php b/app/Models/Station.php new file mode 100644 index 0000000..e22aef5 --- /dev/null +++ b/app/Models/Station.php @@ -0,0 +1,56 @@ + */ + use HasFactory; + + public $timestamps = false; + protected $primaryKey = 'node_id'; + public $incrementing = false; + protected $keyType = 'string'; + + protected function casts(): array + { + return [ + 'is_same_trading_and_brand' => 'boolean', + 'is_supermarket' => 'boolean', + 'is_motorway_service_station' => 'boolean', + 'is_supermarket_service_station' => 'boolean', + 'temporary_closure' => 'boolean', + 'permanent_closure' => 'boolean', + 'permanent_closure_date' => 'date', + 'amenities' => 'array', + 'opening_times' => 'array', + 'fuel_types' => 'array', + 'last_seen_at' => 'datetime', + ]; + } + + public function currentPrices(): HasMany + { + return $this->hasMany(StationPriceCurrent::class, 'station_id', 'node_id'); + } + + public function prices(): HasMany + { + return $this->hasMany(StationPrice::class, 'station_id', 'node_id'); + } +} diff --git a/app/Models/StationPrice.php b/app/Models/StationPrice.php new file mode 100644 index 0000000..069219d --- /dev/null +++ b/app/Models/StationPrice.php @@ -0,0 +1,34 @@ + */ + use HasFactory; + + public $timestamps = false; + + protected function casts(): array + { + return [ + 'fuel_type' => FuelType::class, + 'price_effective_at' => 'datetime', + 'price_reported_at' => 'datetime', + 'recorded_at' => 'datetime', + ]; + } + + public function station(): BelongsTo + { + return $this->belongsTo(Station::class, 'station_id', 'node_id'); + } +} diff --git a/app/Models/StationPriceArchive.php b/app/Models/StationPriceArchive.php new file mode 100644 index 0000000..7569d0f --- /dev/null +++ b/app/Models/StationPriceArchive.php @@ -0,0 +1,29 @@ + FuelType::class, + 'price_effective_at' => 'datetime', + 'price_reported_at' => 'datetime', + 'recorded_at' => 'datetime', + ]; + } + + public function station(): BelongsTo + { + return $this->belongsTo(Station::class, 'station_id', 'node_id'); + } +} diff --git a/app/Models/StationPriceCurrent.php b/app/Models/StationPriceCurrent.php new file mode 100644 index 0000000..187bb6c --- /dev/null +++ b/app/Models/StationPriceCurrent.php @@ -0,0 +1,41 @@ + */ + use HasFactory; + + public $timestamps = false; + protected $primaryKey = null; + public $incrementing = false; + + protected function casts(): array + { + return [ + 'fuel_type' => FuelType::class, + 'price_effective_at' => 'datetime', + 'price_reported_at' => 'datetime', + 'recorded_at' => 'datetime', + ]; + } + + public function station(): BelongsTo + { + return $this->belongsTo(Station::class, 'station_id', 'node_id'); + } + + public function priceInPence(): float + { + return $this->price_pence / 100; + } +} diff --git a/database/factories/StationFactory.php b/database/factories/StationFactory.php new file mode 100644 index 0000000..9b1f070 --- /dev/null +++ b/database/factories/StationFactory.php @@ -0,0 +1,50 @@ + */ +class StationFactory extends Factory +{ + public function definition(): array + { + $trading = $this->faker->company(); + + return [ + 'node_id' => hash('sha256', $this->faker->unique()->uuid()), + 'trading_name' => $trading, + 'brand_name' => $trading, + 'is_same_trading_and_brand' => true, + 'is_supermarket' => false, + 'is_motorway_service_station' => false, + 'is_supermarket_service_station' => false, + 'temporary_closure' => false, + 'permanent_closure' => false, + 'permanent_closure_date' => null, + 'public_phone_number' => null, + 'address_line_1' => $this->faker->streetAddress(), + 'address_line_2' => null, + 'city' => $this->faker->city(), + 'county' => null, + 'country' => 'England', + 'postcode' => strtoupper($this->faker->postcode()), + 'lat' => $this->faker->latitude(49.9, 60.9), + 'lng' => $this->faker->longitude(-8.2, 1.8), + 'amenities' => [], + 'opening_times' => null, + 'fuel_types' => ['E10', 'E5'], + 'last_seen_at' => now(), + ]; + } + + public function supermarket(): static + { + return $this->state([ + 'trading_name' => 'Tesco', + 'brand_name' => 'Tesco', + 'is_supermarket' => true, + ]); + } +} diff --git a/database/factories/StationPriceCurrentFactory.php b/database/factories/StationPriceCurrentFactory.php new file mode 100644 index 0000000..59aed5f --- /dev/null +++ b/database/factories/StationPriceCurrentFactory.php @@ -0,0 +1,24 @@ + */ +class StationPriceCurrentFactory extends Factory +{ + public function definition(): array + { + return [ + 'station_id' => Station::factory(), + 'fuel_type' => FuelType::E10, + 'price_pence' => $this->faker->numberBetween(12000, 18000), + 'price_effective_at' => now()->subHour(), + 'price_reported_at' => now()->subMinutes(30), + 'recorded_at' => now(), + ]; + } +} diff --git a/database/factories/StationPriceFactory.php b/database/factories/StationPriceFactory.php new file mode 100644 index 0000000..980be0f --- /dev/null +++ b/database/factories/StationPriceFactory.php @@ -0,0 +1,24 @@ + */ +class StationPriceFactory extends Factory +{ + public function definition(): array + { + return [ + 'station_id' => Station::factory(), + 'fuel_type' => FuelType::E10, + 'price_pence' => $this->faker->numberBetween(12000, 18000), + 'price_effective_at' => now()->subDays($this->faker->numberBetween(1, 30)), + 'price_reported_at' => now()->subDays($this->faker->numberBetween(1, 30)), + 'recorded_at' => now(), + ]; + } +}