# PreRegister — Cursor Rules ## Project Context PreRegister is a Laravel 11 application for festival ticket pre-registration. Visitors can sign up on branded landing pages to get early/discounted ticket access. Subscriber data is stored locally and optionally synced to Mailwizz (email marketing platform). ## Tech Stack (strict — no alternatives) - **Backend:** PHP 8.2+ / Laravel 11 / MySQL 8 - **Frontend:** Blade + Tailwind CSS 3 + Alpine.js 3 - **Auth:** Laravel Breeze (Blade stack) - **No:** React, Vue, Livewire, Inertia, or any JS framework ## PHP Coding Standards - Every file starts with `declare(strict_types=1);` - Type hints on all method parameters, return types, and properties - Follow PSR-12 coding standards - Use `$fillable` (never `$guarded = []`) on every model - Use Form Request classes for validation — never validate inline in controllers - Controllers are thin: business logic goes in Service classes or Jobs - Use Laravel's `encrypted` cast for sensitive data (API keys) - Use `DB::transaction()` for multi-step database operations - No `dd()` in committed code — use `Log::` facade instead - Comments explain *why*, not *what* ## Laravel Conventions - Route model binding with explicit column: `Route::get('/r/{page:slug}', ...)` - Policies for authorization (not inline Gate checks) - Blade components (``) for reusable UI - Form Requests in `app/Http/Requests/` - Service classes in `app/Services/` - Queued Jobs for external API calls (Mailwizz) - Config values via `config()` helper, never `env()` outside config files ## Database - Docker MySQL on localhost:3306 (db: preregister, user: preregister, pass: preregister) - Use `$table->id()` (unsigned big int) for all PKs - UUID slugs for public-facing URLs - Always run `php artisan migrate` after creating/changing migrations - Run `php artisan migrate:fresh --seed` to reset ## Frontend Guidelines - Tailwind utility classes — no custom CSS unless absolutely necessary - Alpine.js for interactivity (countdown timers, form submissions, dynamic wizard steps) - AJAX form submissions on public pages (return JSON, no page reloads) - Mobile-first responsive design - Public pages: full-screen background image + dark overlay + centered content card ## Mailwizz API - Base URL: https://www.mailwizz.nl/api - Auth: `X-Api-Key` header - Send data as form-data (`Http::asForm()`) - Checkboxlist fields: read as comma-separated string, send as array - Always use `app/Services/MailwizzService.php` for API calls - Sync subscribers via queued job `SyncSubscriberToMailwizz` ## File Structure - Controllers in `app/Http/Controllers/Admin/` (backend) and `app/Http/Controllers/` (public) - Views in `resources/views/admin/` (backend) and `resources/views/public/` (frontend) - Shared form partials as `_form.blade.php` ## Security - CSRF on all forms - Rate limiting on public endpoints (`throttle:10,1`) - Never expose API keys in frontend, logs, or responses - Validate and restrict file uploads (image types, max size) - UUID slugs prevent URL enumeration ## Git Commits Use conventional commits: - `feat: description` for new features - `fix: description` for bug fixes - `refactor: description` for code improvements - Commit after each logical unit, not after entire features ## When Unsure - Check `PreRegister-Development-Prompt.md` in the project root — it has the full specification - Ask for clarification rather than guessing - Prefer explicit over clever