# Alqove API — Laravel 13 (PHP 8.3)

REST API for the Alqove marketplace. No Blade views — API-only.

## Architecture

### Modules (`app/Modules/`)
Each business domain has its own module with: Controllers, Requests, Resources, Services, Jobs, Events, Listeners, Tests, routes.php, README.md.

Modules: Auth, Users, Stores, StoreSites, Items, Cart, Checkout, Orders, Shipping, Search, Notifications, Admin, Kiosk, Checkin, Pos.

### Models (`app/Models/`)
Eloquent models live outside modules — they are shared across domains. A model used by multiple modules (e.g., Item used by Cart, Checkout, Search) belongs in `app/Models/`. `BuyItem` (intake item: photos + quote + outcome, owned by Pos/Capture, converts to a draft `Item` at finalize) lives here too — note it uses `HasUuid` but accepts a **client-supplied** UUID PK (validate `Str::isUuid` at the controller boundary; the trait only auto-generates when the key is empty).

### Support (`app/Support/`)
Base classes, traits (HasUuid), enums, and helpers shared across the application.

## Conventions

### Files
- All PHP files start with `declare(strict_types=1);`
- One class per file
- Use typed properties and return types everywhere

### Models
- UUID PKs use `HasUuid` trait (except Category which uses auto-increment)
- Money fields: `unsignedInteger` in migration, `'integer'` cast in model
- JSON fields: `'array'` cast in model
- Enums: backed string enums in `app/Support/Enums/`, cast in model
- Relationships defined with return type hints

### Naming
- Migrations: descriptive, timestamp-prefixed
- Controllers: singular (ItemController, not ItemsController)
- Services: domain-specific (CheckoutService, ShippingCalculator)
- Events: past tense (PurchaseCompleted, ItemSold)
- Jobs: imperative (CreateShippingLabels, NotifySellerNewOrder)

### Testing
- Feature tests for API endpoints in `tests/Feature/{Module}/`
- Unit tests for services in `tests/Unit/{Module}/`
- Use factories for test data — never raw DB inserts
- Run: `php artisan test`

## Commands

```bash
# Run via Sail from marketplace root
docker compose exec laravel.test php artisan test
docker compose exec laravel.test php artisan migrate:fresh --seed
docker compose exec laravel.test ./vendor/bin/pint --test
docker compose exec laravel.test ./vendor/bin/phpstan analyse
```

## Migrations

The `laravel.test.migrate` init container runs `php artisan migrate --force` automatically before the app container starts, so a fresh `docker compose up -d` always boots with a migrated DB.

After `git pull` brings new migrations on an already-running stack, either:

```bash
docker compose exec -T laravel.test php artisan migrate
# or to re-trigger the init container:
docker compose up -d --force-recreate laravel.test
```

## Key Packages
- **spatie/laravel-permission** — roles (buyer, seller, admin)
- **spatie/laravel-activitylog** — audit trail
- **laravel/sanctum** — API token auth
- **laravel/scout** — Typesense search
