Establishes core rules and conventions for the FuelAlert Laravel application: architecture patterns (fat services, thin controllers), database schema with partitioned station_prices table, multi-tier notification system with Vonage and OneSignal, 4-signal scoring engine for fuel price recommendations, Stripe subscription tiers, Livewire classic component structure, and Pest testing standards.
2.1 KiB
2.1 KiB
Code Style
PHP
- PHP 8.2+ features: constructor property promotion, readonly properties, enums, match expressions
- Type declarations on all method parameters and return types — no untyped signatures
- Named arguments for clarity on calls with 3+ parameters
- Enums over string constants:
FuelType::E10,Recommendation::Wait,NotificationChannel::WhatsApp finalclasses for Services and Jobs — they are not designed for extension
Laravel conventions
- Route model binding over manual
findOrFail()in controllers - Form Requests for any validated input
config()helper overenv()outside of config files — never callenv()in app code- Events + Listeners for side effects (e.g.
PricesUpdatedEventtriggers scoring) - Do not use
DB::transaction()manually — wrap in Service methods where needed
Naming
- Services:
{Noun}Service— e.g.AlertScoringService - Jobs:
{Verb}{Noun}Job— e.g.SendFuelAlertJob,PollFuelPricesJob - Events:
{Noun}{PastTense}Event— e.g.PricesUpdatedEvent - Livewire components:
{Context}/{Feature}— e.g.Dashboard/FuelRecommendation - Migrations: descriptive, e.g.
create_station_prices_table,add_whatsapp_fields_to_users_table
Formatting
- 4-space indentation (PSR-12)
- Max line length: 120 characters
- Run
./vendor/bin/pintbefore committing (Laravel Pint, default ruleset) - No commented-out code committed — use git to recover old code
Security
- Never store raw passwords — Bcrypt via Laravel's Hash facade only
- Validate and sanitise all user input — postcodes, phone numbers, fuel type selections
- Phone numbers: strip non-numeric characters, enforce UK format (07xxx or +447xxx)
- OTP codes: 6 digits, expire in 10 minutes, single use only
- All Stripe webhook payloads verified via Cashier's built-in signature check
Environment
- Local: Laravel Valet or
php artisan serve - Production: IONOS VPS, Nginx + PHP-FPM, MySQL, Redis
- Never commit
.env—.env.examplemust stay up to date with all required keys - Queue driver:
redisin production,syncin testing only