# Model: Review

**Table**: `clinic_reviews` (new)

## Purpose

The two-part post-booking rating from the brief: step one rates the doctor, step two rates the
clinic — both captured together once, shown in two different places.

## Fields

| Column | Type | Constraints | Notes |
|---|---|---|---|
| `id` | bigint | PK | |
| `booking_id` | bigint | `foreignId('booking_id')->unique()->constrained('clinic_bookings')->cascadeOnDelete()` | 1:1, only creatable once `booking.status = completed` |
| `doctor_rating` | unsignedTinyInteger | 1–5 | |
| `doctor_comment` | text | nullable | |
| `clinic_rating` | unsignedTinyInteger | 1–5 | |
| `clinic_comment` | text | nullable | |
| `created_at` / `updated_at` | timestamp | | |

## Relationships

| Relation | Type | Target |
|---|---|---|
| `booking()` | `belongsTo` | `Booking` |
| doctor / clinic accessed via `booking.doctor` / `booking.branch` | — | no direct FK needed, avoids duplicate denormalization here since a review always has exactly one booking |

## Display

- `doctor_rating`/`doctor_comment` shown on the doctor's public profile page for new patients
  (`Site\Clinic\DoctorProfileController`).
- `clinic_rating`/`clinic_comment` shown on the clinic's main page (`Site\Clinic\SearchController`
  / clinic detail view).
- Both feed `ClinicProfile.avg_rating` (per-clinic average) and a doctor-level average, recomputed
  by `ReviewService` on save — see `flows/06-rating-flow.md`.

## Decisions

- One row per booking, not per rating-target — keeps the "rate after this specific visit" model
  from the brief intact (a doctor's overall rating is an aggregate query over their reviews, not
  a separately-stored running average that could drift).
