Opinie (Review)¶
Ostatnia aktualizacja dokumentacji: 26 lutego 2026 Stan synchronizacji z kodem:
Zsynchronizowany
1. Opis ogólny¶
Domena Opinii zarządza ocenami i komentarzami klientów po zakończonych rezerwacjach. To jak książka gości w hotelu — po zakończonej rezerwacji klient może wystawić ocenę (1–5 gwiazdek) i napisać komentarz, który pomoże innym w wyborze obiektu lub zajęć. System obsługuje polimorficzne opinie (przypisywane do obiektów Item lub aktywności Activity), moderację przez administratorów oraz automatyczne przypomnienia o wystawieniu opinii.
2. Architektura domeny¶
2.1 Model danych — Review¶
| Właściwość | Wartość |
|---|---|
| Klasa | Domain\Review\Model\Review |
| Tabela | reviews |
| Klucz główny | UUID (HasUuids) |
| Bazuje na | Infrastructure\Vendor\CustomModel |
| Observer | ReviewObserver (atrybut #[ObservedBy]) |
Fillable:
| Pole | Typ (cast) | Opis |
|---|---|---|
reviewable_type | string | Typ polimorficzny (Item::class lub Activity::class) |
reviewable_id | string (UUID) | ID obiektu/aktywności |
organization_id | string (UUID) | ID organizacji |
reservation_id | string (UUID) | ID rezerwacji (unique) |
rating | integer | Ocena 1–5 |
comment | string | Komentarz tekstowy (nullable) |
is_active | boolean | Czy opinia jest widoczna |
disabled_by | string (UUID) | ID użytkownika, który ukrył opinię |
disabled_at | datetime | Data ukrycia opinii |
Filtry publiczne ($publicFilters): id, reviewable_type, reviewable_id, organization_id, reservation_id, rating
Relacje:
| Relacja | Typ | Model docelowy | Opis |
|---|---|---|---|
reviewable() | MorphTo | Item / Activity | Oceniany obiekt lub aktywność |
organization() | BelongsTo | Organization | Organizacja powiązana z opinią |
reservation() | BelongsTo | Reservation | Rezerwacja, z której wynika opinia |
disabler() | BelongsTo (FK: disabled_by) | User | Użytkownik, który ukrył opinię |
Relacje odwrotne w innych modelach:
| Model | Relacja | Typ |
|---|---|---|
Reservation | review() | HasOne |
Item | reviews() | MorphMany |
Activity | reviews() | MorphMany |
Organization | reviews() | HasMany |
2.2 Diagram relacji¶
erDiagram
Review ||--o| Reservation : "reservation_id"
Review }o--|| Organization : "organization_id"
Review }o--o| User : "disabled_by"
Review }o--|| Item : "reviewable (morph)"
Review }o--|| Activity : "reviewable (morph)"
Reservation ||--o| Review : "hasOne"
Item ||--o{ Review : "morphMany"
Activity ||--o{ Review : "morphMany"
Organization ||--o{ Review : "hasMany" 2.3 Struktura tabeli reviews¶
| Kolumna | Typ | Nullable | Default | Opis |
|---|---|---|---|---|
id | uuid | Nie | — | Klucz główny |
reviewable_type | varchar | Nie | — | Typ polimorficzny (MorphTo) |
reviewable_id | uuid | Nie | — | ID obiektu polimorficznego |
organization_id | uuid | Tak | NULL | FK → organizations.id (CASCADE) |
reservation_id | uuid | Tak | NULL | FK → reservations.id (CASCADE), UNIQUE |
rating | unsigned tinyint | Nie | — | Ocena 1–5 |
comment | text | Tak | NULL | Komentarz |
is_active | boolean | Nie | true | Widoczność |
disabled_by | uuid | Tak | NULL | FK → users.id (SET NULL) |
disabled_at | datetime | Tak | NULL | Data dezaktywacji |
created_at | timestamp | Tak | NULL | Utworzenie |
updated_at | timestamp | Tak | NULL | Ostatnia aktualizacja |
Indeksy: organization_id (index), reservation_id (index + unique), reviewable_type + reviewable_id (morph index)
Klucze obce:
organization_id→organizations.idON DELETE CASCADEreservation_id→reservations.idON DELETE CASCADEdisabled_by→users.idON DELETE SET NULL
3. Endpointy API¶
3.1 Endpointy publiczne (bez autoryzacji)¶
GET /api/reviews¶
- Opis: Lista opinii z filtrowaniem i paginacją (publiczny dostęp)
- Route name:
no-auth-reviews.index - Autoryzacja: Brak — endpoint publiczny
- Kontroler:
ReviewController@index→IndexReviewController - Request:
IndexReviewControllerRequest
Query Parameters:
| Parametr | Typ | Reguły walidacji | Opis |
|---|---|---|---|
page | integer | Paginacja domyślna | Numer strony |
per_page | integer | Paginacja domyślna | Rekordów na stronę |
filter[id] | uuid | exact | Filtr po ID |
filter[reviewable_type] | string | exact | Filtr po typie obiektu |
filter[reviewable_id] | uuid | exact | Filtr po ID obiektu |
filter[organization_id] | uuid | exact | Filtr po organizacji |
filter[reservation_id] | uuid | exact | Filtr po rezerwacji |
filter[rating] | integer | exact | Filtr po ocenie |
filter[organization.name] | string | partial | Filtr po nazwie organizacji |
filter[disabler.name] | string | partial | Filtr po nazwie osoby ukrywającej |
filter[reservation.first_name] | string | partial | Filtr po imieniu z rezerwacji |
filter[reservation.last_name] | string | partial | Filtr po nazwisku z rezerwacji |
Eager loading: reviewable, reservation, organization, disabler, reservation.latestNote, reviewable.media
Response (sukces, 200):
Redukcja pól dla niezalogowanych
Dla niezalogowanych użytkowników następujące pola są ukrywane z odpowiedzi: reservation, reservation_id, disabler, disabled_by, disabled_at, can.
{
"data": [
{
"id": "uuid",
"reviewable_type": "Domain\\Item\\Model\\Item",
"reviewable_id": "uuid",
"organization_id": "uuid",
"rating": 4,
"comment": "Świetne wyposażenie",
"can_be_edit": true,
"is_active": true,
"created_at": "2026-02-20 14:30:00",
"updated_at": "2026-02-20 14:30:00",
"reviewable": { "..." },
"organization": { "..." }
}
],
"meta": { "..." }
}
POST /api/reviews¶
- Opis: Dodanie nowej opinii do rezerwacji (publiczny dostęp)
- Route name:
no-auth-reviews.store - Autoryzacja: Brak — endpoint publiczny
- Middleware:
throttle:public-store(rate limiting) - Kontroler:
ReviewController@store→StoreReviewController→CreateReview - Request:
StoreReviewControllerRequest
Body (Request):
| Pole | Typ | Reguły walidacji | Opis |
|---|---|---|---|
reservation_id | string | required\|uuid\|exists:reservations,id + ReservationStillReviewableRule | ID rezerwacji |
reservation_uuid | string | required\|uuid\|max:255 | UUID rezerwacji |
reservation_access_code | string | required\|string\|min:1\|max:20 + VerifyReservationCorrectDataRule | Kod dostępu do rezerwacji |
rating | integer | required\|integer\|min:1\|max:5 | Ocena (1–5 gwiazdek) |
comment | string | nullable\|string\|max:2000\|blasp_check | Komentarz (opcjonalny) |
website | string | sometimes\|max:0 | Honeypot — pole musi być puste |
company_website | string | sometimes\|max:0 | Honeypot — pole musi być puste |
fax_number | string | sometimes\|max:0 | Honeypot — pole musi być puste |
Pola honeypot
Pola website, company_website i fax_number służą do wykrywania botów. Są walidowane tylko dla niezalogowanych użytkowników. Jeśli bot wypełni te ukryte pola, walidacja odrzuci request.
Reguły biznesowe (walidacja):
| Reguła | Klasa | Opis |
|---|---|---|
VerifyReservationCorrectDataRule | Weryfikacja tożsamości | Sprawdza, czy reservation_id + reservation_uuid + reservation_access_code pasują do istniejącej rezerwacji |
ReservationStillReviewableRule | Limit czasowy | Sprawdza: (1) rezerwacja istnieje, (2) nie ma jeszcze opinii, (3) rezerwacja się zakończyła, (4) nie upłynął termin MAX_DAY_TO_ADD_REVIEW |
blasp_check | Filtr treści | Sprawdza komentarz pod kątem nieodpowiednich słów |
Response (sukces, 200):
Response (błędy, 422):
{
"message": "Sorry, the reservation you are trying to access has already been reviewed.",
"errors": {
"reservation_id": ["Sorry, the reservation you are trying to access has already been reviewed."]
}
}
Możliwe błędy walidacji:
| Komunikat | Przyczyna |
|---|---|
| "Sorry, the reservation you are trying to access does not exist." | Nieprawidłowe reservation_id + uuid + access_code |
| "Sorry, the reservation you are trying to access has already been reviewed." | Rezerwacja ma już opinię |
| "Sorry, you can only add a review after the reservation has ended." | Rezerwacja jeszcze trwa |
| "Sorry, the reservation you are trying to access is no longer reviewable." | Upłynął termin na dodanie opinii |
3.2 Endpointy uwierzytelnione¶
GET /api/reviews (auth)¶
- Opis: Lista opinii z filtrowaniem — wersja uwierzytelniona (pełne dane)
- Autoryzacja: Brak wymogu roli (wszyscy zalogowani użytkownicy)
- Kontroler:
ReviewController@index→IndexReviewController - Request:
IndexReviewControllerRequest - Zachowanie: Identyczne jak endpoint publiczny, ale odpowiedź zawiera wszystkie pola (w tym
reservation,disabled_by,disabled_at,can)
GET /api/reviews/{reviewId} (auth)¶
- Opis: Szczegóły pojedynczej opinii
- Autoryzacja:
role:ADMIN|SUPERVISOR|EMPLOYEE - Kontroler:
ReviewController@show→ShowReviewController - Eager loading:
reviewable,reservation,organization,disabler,reservation.latestNote,reviewable.media
Response (sukces, 200):
{
"data": {
"id": "uuid",
"reviewable_type": "Domain\\Item\\Model\\Item",
"reviewable_id": "uuid",
"organization_id": "uuid",
"reservation_id": "uuid",
"rating": 4,
"comment": "Świetne wyposażenie, klimatyzacja działa doskonale",
"can_be_edit": true,
"is_active": true,
"disabled_by": null,
"disabled_at": null,
"created_at": "2026-02-20 14:30:00",
"updated_at": "2026-02-20 14:30:00",
"can": {
"view": true,
"update": true
},
"reviewable": { "..." },
"reservation": { "..." },
"organization": { "..." },
"disabler": null
}
}
Response (błąd, 404): NotFoundModelException — opinia nie istnieje
PATCH /api/reviews/{reviewId}/change-state (auth)¶
- Opis: Zmiana widoczności opinii (aktywacja/dezaktywacja)
- Autoryzacja:
role:ADMIN|SUPERVISOR|EMPLOYEE+ Policyupdate(użytkownik musi należeć do organizacji opinii) - Kontroler:
ReviewController@changeState→changeStateXController - Zachowanie: Przełącza pole
is_activena przeciwną wartość. Wywołuje eventupdatingna modelu (withEvents = true), co triggerujeUpdatingReviewObserver— automatycznie ustawiadisabled_byidisabled_at.
Response (sukces, 200):
POST /api/reviews (auth)¶
- Opis: Dodanie opinii (wersja uwierzytelniona)
- Autoryzacja: Brak wymogu roli — dostępne dla wszystkich zalogowanych
- Zachowanie: Identyczne jak endpoint publiczny, ale bez pól honeypot (walidacja honeypot stosowana tylko dla niezalogowanych)
4. Logika biznesowa¶
4.1 Główne procesy¶
Proces tworzenia opinii¶
sequenceDiagram
participant K as Klient
participant V as Walidacja
participant O as Observer
participant DB as Baza danych
K->>V: POST /api/reviews (dane + kody dostępu)
V->>V: VerifyReservationCorrectDataRule
V->>V: ReservationStillReviewableRule
V->>V: blasp_check (komentarz)
V->>V: Honeypot (jeśli no-auth)
alt Walidacja OK
V->>O: CreatingReviewObserver
O->>O: Ustal reviewable_type/id z rezerwacji
O->>O: Ustal organization_id z rezerwacji
O->>DB: Zapisz opinię
DB-->>K: 200 + komunikat sukcesu
else Walidacja błąd
V-->>K: 422 + komunikat błędu
end - Klient wysyła dane opinii wraz z
reservation_id,reservation_uuidireservation_access_code VerifyReservationCorrectDataRuleweryfikuje, że trójka identyfikatorów odpowiada istniejącej rezerwacjiReservationStillReviewableRulesprawdza:- Rezerwacja nie ma jeszcze opinii
- Rezerwacja się zakończyła (slot
date_time_tow przeszłości) - Nie upłynął limit
MAX_DAY_TO_ADD_REVIEWdni
- Filtr
blasp_checksprawdza komentarz pod kątem nieodpowiednich słów - Pola honeypot (tylko no-auth) — odrzucenie bota
CreatingReviewObserverautomatycznie uzupełnia:reviewable_typeireviewable_id— na podstawiereservationable_typerezerwacji (Item→Item,ActivityItem→Activity)organization_id— z rezerwacji
- Opinia jest tworzona jako
hasOnena rezerwacji ($reservation->review()->create(...))
Proces moderacji opinii¶
sequenceDiagram
participant A as Administrator
participant S as System
participant O as UpdatingReviewObserver
participant DB as Baza danych
A->>S: PATCH /reviews/{id}/change-state
S->>S: Autoryzacja (role + policy)
S->>O: UpdatingReviewObserver
alt Dezaktywacja (is_active: true → false)
O->>DB: disabled_by = Auth::id()
O->>DB: disabled_at = now()
else Aktywacja (is_active: false → true)
O->>DB: disabled_by = null
O->>DB: disabled_at = null
end
DB-->>A: 200 sukces 4.2 Reguły biznesowe¶
- Jedna opinia na rezerwację — kolumna
reservation_idma constraintUNIQUE - Opinia tylko po zakończeniu rezerwacji — wszystkie sloty muszą się zakończyć przed dodaniem opinii
- Limit czasowy — opinia musi zostać dodana w ciągu
MAX_DAY_TO_ADD_REVIEWdni od zakończenia rezerwacji (parametr konfigurowalny) - Weryfikacja tożsamości — wymagane podanie
reservation_id+reservation_uuid+reservation_access_code - Filtr blasfemii — komentarze sprawdzane regułą
blasp_check - Ochrona antyspamowa — honeypot dla niezalogowanych, rate limiting
throttle:public-store - Automatyczne przypisanie obiektu — observer ustala
reviewable_type/idiorganization_idna podstawie rezerwacji - Polimorficzność obiektu —
Itemprzypisywany bezpośrednio,ActivityItem→ przypisywanaActivity(rodzic) - Śledzenie moderacji — przy dezaktywacji automatycznie zapisywany jest moderator (
disabled_by) i czas (disabled_at) - Reaktywacja — przy ponownej aktywacji pola
disabled_byidisabled_atsą czyszczone
4.3 Walidacje¶
StoreReviewControllerRequest¶
| Pole | Reguły |
|---|---|
reservation_id | required\|uuid\|exists:reservations,id + ReservationStillReviewableRule |
reservation_uuid | required\|uuid\|max:255 |
reservation_access_code | required\|string\|min:1\|max:20 + VerifyReservationCorrectDataRule |
rating | required\|integer\|min:1\|max:5 |
comment | nullable\|string\|max:2000\|blasp_check |
website | sometimes\|max:0 (honeypot, tylko no-auth) |
company_website | sometimes\|max:0 (honeypot, tylko no-auth) |
fax_number | sometimes\|max:0 (honeypot, tylko no-auth) |
IndexReviewControllerRequest¶
Standardowe reguły paginacji (paginationDefaultRequestRules()) + filtry publiczne modelu Review (publicFilterRequestRules()).
5. Autoryzacja i uprawnienia¶
5.1 Policy — ReviewPolicy¶
| Metoda | Logika | Opis |
|---|---|---|
before() | Admin → true | Administratorzy mają pełny dostęp (z BasePolicy) |
view() | belongsToOrganization() | Użytkownik musi należeć do organizacji opinii |
create() | return false | Tworzenie zablokowane przez policy — tworzenie odbywa się publicznie |
update() | belongsToOrganization() | Użytkownik musi należeć do organizacji opinii |
5.2 Middleware na endpointach¶
| Endpoint | Middleware roli | Policy |
|---|---|---|
GET /reviews (no-auth) | Brak | Brak |
POST /reviews (no-auth) | Brak + throttle:public-store | Brak |
GET /reviews (auth) | Brak | Brak |
POST /reviews (auth) | Brak | Brak |
GET /reviews/{id} (auth) | ADMIN\|SUPERVISOR\|EMPLOYEE | Brak (ale filtrowane przez UserAccessesGlobalScope) |
PATCH /reviews/{id}/change-state (auth) | ADMIN\|SUPERVISOR\|EMPLOYEE | update (belongsToOrganization) |
5.3 Matryca uprawnień¶
| Akcja | Administrator | Supervisor | Employee | Klient (z kodem) | Gość |
|---|---|---|---|---|---|
| Lista opinii (publiczna) | |||||
| Szczegóły opinii | (swojej org.) | (swojej org.) | |||
| Dodanie opinii | |||||
| Moderacja (zmiana stanu) | (swojej org.) | (swojej org.) |
6. Observery i efekty uboczne¶
6.1 ReviewObserver¶
Plik: Domain\Review\Observer\ReviewObserver Rejestracja: Atrybut #[ObservedBy(ReviewObserver::class)] na modelu
| Hook | Delegacja | Opis |
|---|---|---|
creating | CreatingReviewObserver | Ustala reviewable_type, reviewable_id, organization_id na podstawie rezerwacji |
updating | UpdatingReviewObserver | Przy zmianie is_active ustawia/czyści disabled_by i disabled_at |
6.2 CreatingReviewObserver — szczegóły¶
- Sprawdza, czy opinia ma
reservation_idi czy rezerwacja istnieje - Na podstawie
reservationable_typerezerwacji określareviewable:Item::class→ obiekt bezpośrednioActivityItem::class→$res->reservationable->activity(nadrzędna aktywność)- inne →
null(brak przypisania)
- Ustawia
reviewable_type,reviewable_id,organization_idna modelu opinii
6.3 UpdatingReviewObserver — szczegóły¶
- Sprawdza, czy pole
is_activeuległo zmianie (isDirty) - Jeśli dezaktywacja (
is_active→false):disabled_by= ID zalogowanego użytkownikadisabled_at= aktualny czas
- Jeśli aktywacja (
is_active→true):disabled_by=nulldisabled_at=null
7. Notyfikacje — przypomnienie o opinii¶
Przypomnienie o opinii jest obsługiwane przez domenę Reservation, nie Review:
| Parametr | Wartość |
|---|---|
| Komenda CRON | reservation:send-review-reminders |
| Klasa akcji | SendReviewRemindersCommandAction |
| Klasa notyfikacji | ReservationReviewReminderNotification |
| Opóźnienie | 5 godzin po zakończeniu ostatniego slotu |
| Kanał | Email (przez SimpleSendNotificationJob) |
| Kolejka | notifications |
7.1 Warunki wysłania przypomnienia¶
| Warunek | Opis |
|---|---|
| Status rezerwacji | RESERVATION_STATUS_CONFIRMED |
| Brak poprzedniego przypomnienia | review_reminder_sent_at IS NULL |
| Wszystkie sloty zakończone | Ostatni aktywny slot zakończony ≥ 5h temu |
| Brak opinii | Rezerwacja nie ma jeszcze review |
| Email istnieje | email IS NOT NULL AND email != '' |
| Powiadomienia włączone | send_notifications = true |
7.2 Treść powiadomienia¶
- Temat: "Zaproszenie do wystawienia opinii - rezerwacja nr {uuid}"
- Treść: Podziękowanie za skorzystanie z usług + zachęta do wystawienia opinii
- Dane: Numer rezerwacji, kod dostępu, nazwa usługi, data
- Przycisk CTA: "Wystaw opinię" → link do frontendu
/rezerwacje/{id}?uuid={uuid}&access_code=
7.3 Po wysłaniu¶
Pole review_reminder_sent_at na rezerwacji jest ustawiane na aktualny czas — zapobiega wielokrotnemu wysyłaniu.
8. Powiązania z innymi domenami¶
| Domena | Powiązanie | Opis |
|---|---|---|
| Rezerwacje | BelongsTo (reservation) | Opinia dotyczy konkretnej rezerwacji; weryfikacja uuid + access_code |
| Obiekty | MorphTo (reviewable) | Opinie o obiektach — polimorficzna relacja MorphMany w Item |
| Aktywności | MorphTo (reviewable) | Opinie o zajęciach — polimorficzna relacja MorphMany w Activity |
| Organizacje | BelongsTo (organization) | Opinie wpływają na statystyki organizacji (HasMany) |
| Reklamacje | Współdzielona reguła | VerifyReservationCorrectDataRule jest reużywana w StoreComplaintControllerRequest |
| Statystyki | Odczyt danych | GetReviewStatistics, GetRecentReviews — analiza opinii w dashboardzie |
| Raporty | Generator | ReviewsSummaryGenerator — raport podsumowania opinii per obiekt |
| Support | Atrybuty | GetTotalReviewsAttr, GetAvgReviewsAttr — obliczanie statystyk na modelach |
8.1 Statystyki opinii (domena Statistic)¶
GetReviewStatistics¶
Zwraca: - total — łączna liczba opinii - average_rating — średnia ocena - by_rating — rozkład ocen (1–5) - with_comment — liczba opinii z komentarzem - without_comment — liczba opinii bez komentarza
Filtrowanie: zakres dat (date_from, date_to), lista itemIds.
GetRecentReviews¶
Zwraca ostatnie aktywne opinie (domyślnie 10), z relacjami reservation, reviewable, organization.
8.2 Raport opinii (domena Report)¶
Klucz raportu: reviews.summary
Filtry:
| Filtr | Typ | Opis |
|---|---|---|
period | enum | this_month, last_month, this_year, last_year, custom |
date_from | date | Początek zakresu (wymagany dla custom) |
date_to | date | Koniec zakresu (wymagany dla custom) |
organization_id | uuid | Filtr po organizacji |
item_id | uuid | Filtr po obiekcie |
Kolumny raportu: Nazwa obiektu, Średnia ocena, Liczba opinii, 5 gwiazdek, 4 gwiazdki, 3 gwiazdki, 2 gwiazdki, 1 gwiazdka
8.3 Atrybuty obliczeniowe (Support)¶
GetTotalReviewsAttr¶
Oblicza łączną liczbę aktywnych opinii (is_active = true) dla modelu. Optymalizacja: 1. Wykorzystuje reviews_count jeśli dostępny (agregat) 2. Filtruje załadowaną relację jeśli dostępna 3. W ostateczności wykonuje loadCount z filtrem
GetAvgReviewsAttr¶
Oblicza średnią ocenę aktywnych opinii. Analogiczna optymalizacja jak wyżej, z zaokrągleniem do 2 miejsc po przecinku.
9. Konfiguracja¶
| Parametr | Enum | Opis |
|---|---|---|
MAX_DAY_TO_ADD_REVIEW | ReservationEnumService::MAX_DAY_TO_ADD_REVIEW | Maksymalna liczba dni od zakończenia rezerwacji na dodanie opinii |
Parametr jest odczytywany przez TraitConfiguration::getConfigurationBySlug() w ReservationStillReviewableRule.
10. Zabezpieczenia¶
| Zabezpieczenie | Mechanizm | Opis |
|---|---|---|
| Weryfikacja tożsamości | VerifyReservationCorrectDataRule | Trzy kody: reservation_id + uuid + access_code |
| Filtr treści | blasp_check (custom validation rule) | Sprawdzanie nieodpowiednich słów w komentarzu |
| Honeypot | Ukryte pola website, company_website, fax_number | Tylko dla niezalogowanych — bot wypełniający te pola zostaje odrzucony |
| Rate limiting | throttle:public-store | Ochrona przed masowym tworzeniem opinii |
| Jedna opinia/rezerwację | UNIQUE constraint na reservation_id + walidacja | Nie można dodać dwóch opinii do tej samej rezerwacji |
| Redukcja danych | UNALLOWED_NO_AUTH_KEYS w ReviewResource | Niezalogowani nie widzą wrażliwych pól |
11. Skala ocen¶
| Ocena | Znaczenie |
|---|---|
| 5 | Doskonale |
| 4 | Bardzo dobrze |
| 3 | Dobrze |
| 2 | Slabo |
| 1 | Bardzo slabo |
12. Co mozna oceniac?¶
System obsługuje opinie dla dwóch typów encji (ustalanych automatycznie przez CreatingReviewObserver):
Obiekty (Item)¶
| Przyklad | Opis |
|---|---|
| Sala konferencyjna | "Swietne wyposazenie, klimatyzacja dziala doskonale" |
| Hala sportowa | "Duza przestrzen, ale slabe oswietlenie" |
| Boisko | "Nawierzchnia w dobrym stanie" |
Zajecia (Activity)¶
| Przyklad | Opis |
|---|---|
| Joga | "Trenerka bardzo cierpliwa, polecam poczatkujacym" |
| Aqua aerobik | "Swietna zabawa, woda czysta" |
| Warsztaty ceramiczne | "Duzo sie nauczylam, prowadzacy profesjonalny" |
Automatyczne przypisanie
Klient nie wybiera, czy ocenia obiekt czy zajęcia. System automatycznie ustala typ na podstawie rezerwacji:
- Rezerwacja na obiekt (
Item) → opinia o obiekcie - Rezerwacja na sesję zajęć (
ActivityItem) → opinia o aktywności (nadrzędnaActivity)
13. Dane demo (Seeder)¶
ReservationSeeder automatycznie tworzy opinie dla ~30% zakończonych rezerwacji z losowymi ocenami (1–5) i komentarzami. Observer ReviewObserver automatycznie ustala reviewable_type/id i organization_id na podstawie rezerwacji.
14. Pliki domeny — struktura¶
app/Domain/Review/
├── Action/
│ ├── Controller/
│ │ ├── IndexReviewController.php # Lista opinii z filtrowaniem
│ │ ├── ShowReviewController.php # Szczegóły opinii
│ │ └── StoreReviewController.php # Tworzenie opinii
│ └── Other/
│ ├── CreateReview.php # Logika tworzenia opinii
│ ├── CreatingReviewObserver.php # Observer: ustalanie reviewable/organization
│ └── UpdatingReviewObserver.php # Observer: śledzenie moderacji
├── Controller/
│ └── ReviewController.php # Główny kontroler
├── Model/
│ └── Review.php # Model Eloquent
├── Observer/
│ └── ReviewObserver.php # Dispatcher observerów
├── Policy/
│ └── ReviewPolicy.php # Polityka autoryzacji
├── Request/
│ ├── IndexReviewControllerRequest.php # Walidacja listy
│ ├── StoreReviewControllerRequest.php # Walidacja tworzenia
│ └── Rule/
│ ├── ReservationStillReviewableRule.php # Limit czasowy + deduplikacja
│ └── VerifyReservationCorrectDataRule.php # Weryfikacja kodów rezerwacji
├── Resource/
│ └── ReviewResource.php # Transformacja JSON
└── Service/
├── ReviewEnumService.php # Enum (pusty — brak case'ów)
├── TraitReview.php # Trait serwisowy
└── TraitControllerReview.php # Trait kontrolera
16. Historia zmian¶
| Data | Typ | Opis |
|---|---|---|
| 26 lut 2026 | Dokumentacja | Pełna aktualizacja dokumentacji domeny — synchronizacja ze stanem kodu |
| 18 lut 2026 | Optymalizacja | Eliminacja N+1 query w GET /api/reviews — Dodano 'reservation.latestNote' i 'reviewable.media' do eager loading w IndexReviewController i ShowReviewController |