Kategorie (Category)¶
Ostatnia aktualizacja dokumentacji: 26 lutego 2026 Stan synchronizacji z kodem: ✅ Zsynchronizowany
1. Opis ogólny¶
Domena Kategorii odpowiada za klasyfikację obiektów i zajęć w systemie zajmij.to. To jak system półek w bibliotece — każdy obiekt i każde zajęcia mają swoje miejsce, które ułatwia mieszkańcom znalezienie tego, czego szukają. Kategorie umożliwiają administratorom logiczną organizację zasobów z wizualną identyfikacją (ikony, kolory), a mieszkańcom — szybkie filtrowanie oferty.
Kategorie są płaskie (bez hierarchii) i dzielą się na dwa typy:
- Kategorie obiektów (
item) — Sale konferencyjne, Boiska, Hale sportowe, Sprzęt - Kategorie aktywności (
activity) — Fitness, Zajęcia dla dzieci, Warsztaty, Kursy
Korzyści dla JST
- Łatwiejsze wyszukiwanie — mieszkaniec od razu wie gdzie szukać
- Spójna prezentacja — wszystkie obiekty sportowe wyglądają podobnie
- Własne ikony i kolory — każda kategoria jest rozpoznawalna
- Automatyzacja — dezaktywacja kategorii ukrywa wszystkie jej obiekty i aktywności
2. Architektura domeny¶
2.1 Modele danych¶
Model Category¶
Plik: app/Domain/Category/Model/Category.php Tabela: categories Klucz główny: UUID (HasUuids) Observer: CategoryObserver (via #[ObservedBy]) Traity: HasUuids, TraitCategory
Pola modelu¶
| Pole | Typ | Opis | Wymagane (store) | Edytowalne (update) |
|---|---|---|---|---|
id | UUID | Identyfikator | Auto | Nie |
scope | string (enum) | Typ kategorii: item lub activity | Tak | Nie |
name | string | Nazwa (max 255, unikalna w obrębie scope) | Tak | Tak |
desc | string? | Opis (max 3000) | Nie | Tak |
lucide_icon | string | Identyfikator ikony Lucide (max 255) | Tak | Tak |
color | string | Kolor HEX (nie czarny) | Tak | Tak |
is_active | bool | Czy aktywna (default: true) | Tak | Nie (tylko przez changeState) |
extra_params | array? | Dane kontekstowe (np. lista modeli do reaktywacji) | — | — |
created_at | datetime | Data utworzenia | Auto | Nie |
updated_at | datetime | Data aktualizacji | Auto | Nie |
Atrybuty wyliczane¶
| Atrybut | Typ | Opis | Klasa |
|---|---|---|---|
total_relations | int | Liczba przypisanych modeli (Items lub Activities, zależnie od scope) | GetTotalRelationsAttr |
is_in_use | bool | Czy total_relations > 0 | — (accessor w modelu) |
can_be_edit | bool | Czy można edytować | Bazowa klasa modelu |
scope_lang | string | Przetłumaczona nazwa typu | CategoryResource |
Zliczanie relacji zależne od scope
Atrybut total_relations zlicza items dla scope=item lub relActivities dla scope=activity. Używa loadCount z lazy-loading jeśli count nie został wcześniej załadowany.
Relacje¶
| Relacja | Typ | Model docelowy | Opis |
|---|---|---|---|
items() | HasMany | Item | Obiekty przypisane do kategorii |
relActivities() | HasMany | Activity | Aktywności przypisane do kategorii |
PublicFilters (dostępne do filtrowania przez API)¶
| Pole | Opis |
|---|---|
id | UUID kategorii |
scope | Typ kategorii |
name | Nazwa |
is_active | Czy aktywna |
Casty¶
'id' => 'string',
'scope' => 'string',
'name' => 'string',
'desc' => 'string',
'lucide_icon' => 'string',
'color' => 'string',
'is_active' => 'boolean',
'extra_params' => 'array',
'created_at' => 'datetime:Y-m-d H:i:s',
'updated_at' => 'datetime:Y-m-d H:i:s',
2.2 Diagram relacji¶
erDiagram
Category ||--o{ Item : "items (scope=item)"
Category ||--o{ Activity : "relActivities (scope=activity)"
Category {
uuid id PK
enum scope "item | activity"
string name
text desc
string lucide_icon
string color
boolean is_active
timestamp created_at
timestamp updated_at
}
Item {
uuid id PK
uuid category_id FK
string name
boolean is_active
}
Activity {
uuid id PK
uuid category_id FK
string name
boolean is_active
} 2.3 Struktura tabeli w bazie danych¶
Migracja: database/migrations/0001_01_01_000012_create_categories.php
| Kolumna | Typ | Nullable | Default | Opis |
|---|---|---|---|---|
id | UUID | Nie | Auto (HasUuids) | Klucz główny |
scope | ENUM(item, activity) | Nie | — | Typ kategorii |
name | VARCHAR(255) | Nie | — | Nazwa kategorii |
desc | TEXT | Tak | NULL | Opis kategorii |
lucide_icon | VARCHAR(255) | Nie | — | Identyfikator ikony Lucide |
color | VARCHAR(255) | Nie | — | Kolor HEX |
is_active | BOOLEAN | Nie | true | Czy aktywna |
created_at | TIMESTAMP | Tak | Auto | Data utworzenia |
updated_at | TIMESTAMP | Tak | Auto | Data aktualizacji |
Indeksy:
| Nazwa | Kolumny | Typ |
|---|---|---|
categories_name_index | name | INDEX |
idx_categories_org_scope_active | scope, is_active | INDEX (złożony) |
3. Endpointy API¶
3.1 Endpointy publiczne (bez autoryzacji)¶
Plik routów: routes/api/noAuth/category.php Prefix: /no-auth/categories
GET /no-auth/categories¶
- Opis: Lista kategorii (publiczna — bez pola
can) - Autoryzacja: Brak (dostępne bez logowania)
- Query Parameters:
| Parametr | Typ | Opis |
|---|---|---|
filter[id] | UUID | Filtruj po ID |
filter[scope] | string | Filtruj po typie: item lub activity |
filter[is_active] | boolean | Filtruj po statusie aktywności |
per_page | int | Liczba wyników na stronę |
page | int | Numer strony |
- Response (sukces, 200):
{ "data": [ { "id": "9e1a2b3c-4d5e-6f7a-8b9c-0d1e2f3a4b5c", "scope": "item", "scope_lang": "Item", "name": "Sale konferencyjne", "desc": "Sale do spotkań i konferencji", "lucide_icon": "door-open", "color": "#3B82F6", "is_active": true, "can_be_edit": true, "created_at": "2026-01-15 10:30:00", "updated_at": "2026-01-15 10:30:00", "total_relations": 5, "is_in_use": true } ], "links": { "...": "paginacja" }, "meta": { "...": "metadane" } }
Pole can ukryte
W odpowiedzi publicznej (no-auth) pole can z uprawnieniami jest usuwane przez reduceResourceForNoAuth().
GET /no-auth/categories/{categoryId}¶
- Opis: Szczegóły pojedynczej kategorii (publiczne)
- Autoryzacja: Brak
- Parametry URL:
categoryId(UUID) - Response (sukces, 200):
{ "data": { "id": "9e1a2b3c-4d5e-6f7a-8b9c-0d1e2f3a4b5c", "scope": "item", "scope_lang": "Item", "name": "Sale konferencyjne", "desc": "Sale do spotkań i konferencji", "lucide_icon": "door-open", "color": "#3B82F6", "is_active": true, "can_be_edit": true, "created_at": "2026-01-15 10:30:00", "updated_at": "2026-01-15 10:30:00", "total_relations": 5, "is_in_use": true } } - Response (błąd, 404): Kategoria nie znaleziona (
NotFoundModelException)
3.2 Endpointy dla zalogowanych użytkowników¶
Plik routów: routes/api/auth/category.php Prefix: /categories Middleware: auth:api + globalne middleware (patrz architektura)
GET /categories¶
- Opis: Lista kategorii (z polem
canz uprawnieniami) - Autoryzacja: Dowolny zalogowany użytkownik
- Form Request:
IndexCategoryControllerRequest - Query Parameters: Identyczne jak endpoint publiczny
- Response (sukces, 200):
{ "data": [ { "id": "9e1a2b3c-4d5e-6f7a-8b9c-0d1e2f3a4b5c", "scope": "item", "scope_lang": "Item", "name": "Sale konferencyjne", "desc": "Sale do spotkań i konferencji", "lucide_icon": "door-open", "color": "#3B82F6", "is_active": true, "can_be_edit": true, "created_at": "2026-01-15 10:30:00", "updated_at": "2026-01-15 10:30:00", "total_relations": 5, "is_in_use": true, "can": { "view": true, "create": false, "update": false } } ] }
GET /categories/{categoryId}¶
- Opis: Szczegóły kategorii (z uprawnieniami)
- Autoryzacja: Dowolny zalogowany użytkownik
- Parametry URL:
categoryId(UUID) - Response: Jak wyżej, pojedynczy obiekt
POST /categories¶
- Opis: Tworzenie nowej kategorii
- Autoryzacja: Admin lub Supervisor (middleware
role:admin|supervisor+CategoryPolicy::create) - Form Request:
StoreCategoryControllerRequest - Body (Request):
| Pole | Typ | Reguły walidacji |
|---|---|---|
scope | string | required\|string\|max:255\|in:item,activity |
name | string | required\|string\|max:255 + UniqueScopeCategoryNameRule |
desc | string? | nullable\|string\|max:3000 |
lucide_icon | string | required\|string\|max:255 |
color | string | required\|regex:HEX\|not_in:#000,#000000 |
is_active | bool | required\|boolean |
- Response (sukces, 200):
- Response (błąd, 422): Błąd walidacji (duplikat nazwy, nieprawidłowy scope, czarny kolor)
- Response (błąd, 403): Brak uprawnień
PATCH /categories/{categoryId}¶
- Opis: Aktualizacja istniejącej kategorii
- Autoryzacja: Admin lub Supervisor (middleware
role:admin|supervisor+CategoryPolicy::update) - Form Request:
UpdateCategoryControllerRequest - Parametry URL:
categoryId(UUID) - Body (Request):
| Pole | Typ | Reguły walidacji |
|---|---|---|
name | string? | sometimes\|nullable\|string\|max:255 + UniqueScopeCategoryNameRule (z excludeId) |
desc | string? | sometimes\|nullable\|string\|max:3000 |
lucide_icon | string? | sometimes\|nullable\|string\|max:255 |
color | string? | sometimes\|nullable\|regex:HEX\|not_in:#000,#000000 |
Ograniczenia aktualizacji
Przy aktualizacji nie można zmienić pól scope ani is_active. Zmiana stanu aktywności odbywa się wyłącznie przez endpoint changeState.
- Response (sukces, 200):
- Response (błąd, 404): Kategoria nie znaleziona (
NotFoundModelException) - Response (błąd, 422): Błąd walidacji
- Response (błąd, 403): Brak uprawnień
PATCH /categories/{categoryId}/change-state¶
- Opis: Zmiana stanu aktywności kategorii (aktywacja/dezaktywacja) z opcjonalną selektywną reaktywacją modeli
- Autoryzacja: Admin lub Supervisor (middleware
role:admin|supervisor+CategoryPolicy::update) - Form Request:
ChangeStateCategoryControllerRequest - Parametry URL:
categoryId(UUID) - Body (Request):
| Pole | Typ | Reguły walidacji |
|---|---|---|
models | array? | sometimes\|nullable\|array |
models.* | UUID | required\|uuid + ModelBelongsToCategoryRule |
- Response (sukces, 200): Zmiana stanu z kaskadowym efektem na powiązane modele
- Response (błąd, 422): Model nie należy do tej kategorii
- Response (błąd, 403): Brak uprawnień
4. Logika biznesowa¶
4.1 Główne procesy¶
Tworzenie kategorii¶
flowchart LR
A[Request POST /categories] --> B[StoreCategoryControllerRequest]
B --> C[Walidacja: scope, name, icon, color]
C --> D[CategoryPolicy::create]
D --> E[CreateCategory → Category::create]
E --> F[Response 200: sukces] - Administrator wysyła żądanie z danymi kategorii
StoreCategoryControllerRequestwaliduje dane (scope, unikalna nazwa, format koloru)CategoryPolicy::createsprawdza uprawnienia (isSupervisor)StoreCategoryControllerdeleguje doCreateCategoryactionCategory::create()tworzy rekord w bazie
Aktualizacja kategorii¶
flowchart LR
A[Request PATCH /categories/id] --> B[UpdateCategoryControllerRequest]
B --> C[Walidacja: name unique, color format]
C --> D[CategoryPolicy::update]
D --> E[fillModelData → update]
E --> F[CategoryObserver::updated]
F --> G{is_active changed?}
G -->|Tak| H[Kaskadowa zmiana stanu]
G -->|Nie| I[Response 200: sukces]
H --> I Zmiana stanu kategorii (change-state)¶
flowchart TD
A[PATCH /categories/id/change-state] --> B[Walidacja models]
B --> C[changeStateXController]
C --> D[Toggle is_active]
D --> E[CategoryObserver::updated]
E --> F{is_active zmienione?}
F -->|Nie| G[Koniec]
F -->|Tak| H{Aktywacja czy dezaktywacja?}
H -->|Dezaktywacja| I[Dezaktywuj powiązane modele]
H -->|Aktywacja| J[Odczytaj models z ResolveModelContext]
J --> K[Aktywuj wybrane modele]
I --> L{total_relations > 50?}
K --> L
L -->|Tak| M[Async: ChangeStateAllObjectModelsJob]
L -->|Nie| N[Sync: changeStateAllObjectModels] 4.2 Reguły biznesowe¶
- Kategoria musi mieć typ (
scope):itemlubactivity - Nazwa kategorii musi być unikalna w obrębie scope — nie mogą istnieć dwie kategorie "Sale" w typie "item"
- Typ (
scope) nie może być zmieniony po utworzeniu kategorii - Pole
is_activenie może być zmienione przez endpointupdate— tylko przezchangeState - Kolor musi być w formacie HEX:
#RGB,#RGBA,#RRGGBBlub#RRGGBBAA - Kolor czarny (
#000,#000000) jest zabroniony ze względu na czytelność interfejsu - Dezaktywacja kategorii kaskadowo dezaktywuje wszystkie przypisane obiekty (Items) lub aktywności (Activities)
- Aktywacja kategorii umożliwia selektywną reaktywację — podanie listy
models(UUID) określa które modele reaktywować - Modele podane w
changeState.modelsmuszą należeć do tej kategorii (ModelBelongsToCategoryRule) - Przy tworzeniu/edycji Item lub Activity system waliduje czy podana kategoria jest aktywna i ma odpowiedni scope (
ActiveCategoryRule) - Jeśli kategoria ma więcej niż 50 powiązanych modeli, kaskadowa zmiana stanu wykonywana jest asynchronicznie (kolejka
domain) - Endpointy publiczne (no-auth) zwracają dane bez pola
can(uprawnienia)
4.3 Walidacje¶
Tworzenie kategorii (StoreCategoryControllerRequest)¶
| Pole | Reguły |
|---|---|
scope | required\|string\|max:255\|in:item,activity |
name | required\|string\|max:255 + UniqueScopeCategoryNameRule($scope) |
desc | nullable\|string\|max:3000 |
lucide_icon | required\|string\|max:255 |
color | required\|regex:/^#(?:[0-9A-Fa-f]{3}\|[0-9A-Fa-f]{4}\|[0-9A-Fa-f]{6}\|[0-9A-Fa-f]{8})$/\|not_in:#000,#000000 |
is_active | required\|boolean |
Aktualizacja kategorii (UpdateCategoryControllerRequest)¶
| Pole | Reguły |
|---|---|
name | sometimes\|nullable\|string\|max:255 + UniqueScopeCategoryNameRule($scope, $excludeId) |
desc | sometimes\|nullable\|string\|max:3000 |
lucide_icon | sometimes\|nullable\|string\|max:255 |
color | sometimes\|nullable\|regex:HEX\|not_in:#000,#000000 |
Zmiana stanu (ChangeStateCategoryControllerRequest)¶
| Pole | Reguły |
|---|---|
models | sometimes\|nullable\|array |
models.* | required\|uuid + ModelBelongsToCategoryRule($categoryId) |
Lista kategorii (IndexCategoryControllerRequest)¶
Używa paginationDefaultRequestRules() + publicFilterRequestRules(Category::class).
5. Autoryzacja i uprawnienia¶
Middleware¶
Kontroler CategoryController stosuje middleware role:admin|supervisor dla metod: store, update, changeState.
Policy (CategoryPolicy)¶
Plik: app/Domain/Category/Policy/CategoryPolicy.php Bazowa klasa: BasePolicy
| Metoda | Logika | Opis |
|---|---|---|
view(User, Category) | return true | Każdy zalogowany użytkownik może przeglądać |
create(User) | isSupervisor($user) | Tylko Supervisor lub wyżej |
update(User, Category) | isSupervisor($user) | Tylko Supervisor lub wyżej |
Macierz uprawnień¶
| Rola | Podgląd | Tworzenie | Edycja | Zmiana stanu |
|---|---|---|---|---|
| Administrator | Tak | Tak | Tak | Tak |
| Supervisor | Tak | Tak | Tak | Tak |
| Employee | Tak | Nie | Nie | Nie |
| Klient (no-auth) | Publiczne | Nie | Nie | Nie |
6. Eventy i efekty uboczne¶
CategoryObserver¶
Plik: app/Domain/Category/Observer/CategoryObserver.php Rejestracja: Atrybut #[ObservedBy(CategoryObserver::class)] na modelu
| Zdarzenie | Klasa akcji | Opis |
|---|---|---|
updated | UpdatedCategoryObserver → UpdatedIsActiveCategoryObserver | Kaskadowa zmiana stanu powiązanych modeli gdy is_active się zmieni |
Łańcuch wywołań observera¶
CategoryObserver::updated()
└─ UpdatedCategoryObserver::__invoke()
└─ UpdatedIsActiveCategoryObserver::__invoke()
├─ Sprawdza: $model->wasChanged('is_active')
├─ (aktywacja) odczytuje models z ResolveModelContext
├─ (> 50 modeli) → ChangeStateAllObjectModelsJob::dispatch() (kolejka: domain)
└─ (≤ 50 modeli) → changeStateAllObjectModels() (sync)
Kaskadowa dezaktywacja¶
flowchart TD
A[Dezaktywacja kategorii] --> B{total_relations > 50?}
B -->|Tak| C[Async: ChangeStateAllObjectModelsJob]
B -->|Nie| D[Sync: changeStateAllObjectModels]
C --> E[Dezaktywuj Items/Activities z category_id]
D --> E Kaskadowa aktywacja (selektywna)¶
flowchart TD
A[Aktywacja kategorii] --> B{Podano listę models?}
B -->|Tak| C[Odczytaj z ResolveModelContext]
C --> D[Aktywuj tylko wybrane modele]
B -->|Nie| E[Brak selektywnej reaktywacji]
D --> F{total_relations > 50?}
F -->|Tak| G[Async job]
F -->|Nie| H[Sync] Job: ChangeStateAllObjectModelsJob¶
Plik: app/Support/Job/ChangeStateAllObjectModelsJob.php Kolejka: domain (SupportEnumService::ON_QUEUE_DOMAIN) Cel: Masowa zmiana stanu is_active dla powiązanych modeli (Items/Activities) gdy kategoria ma > 50 relacji
7. Notyfikacje¶
Domena Kategorii nie generuje własnych notyfikacji. Kaskadowa zmiana stanu modeli (Items/Activities) może jednak pośrednio triggerować notyfikacje w tych domenach, jeśli ich observery je obsługują.
8. Powiązania z innymi domenami¶
| Domena | Powiązanie | Typ relacji | Opis |
|---|---|---|---|
| Zasoby/Obiekty (Item) | Item → Category | BelongsTo | Obiekty są przypisane do kategorii typu item |
| Aktywności (Activity) | Activity → Category | BelongsTo | Zajęcia są przypisane do kategorii typu activity |
Odwołania w innych domenach¶
| Domena | Plik | Użycie |
|---|---|---|
| Item | ChangeCategoryItemControllerRequest | Zmiana kategorii obiektu |
| Item | StoreItemControllerRequest (via ActiveCategoryRule) | Walidacja aktywnej kategorii przy tworzeniu |
| Activity | ChangeCategoryActivityControllerRequest | Zmiana kategorii aktywności |
| Activity | StoreActivityControllerRequest (via ActiveCategoryRule) | Walidacja aktywnej kategorii przy tworzeniu |
| Activity | UpdateActivityControllerRequest (via ActiveCategoryRule) | Walidacja aktywnej kategorii przy aktualizacji |
| Support | ActiveCategoryRule | Reguła walidacji sprawdzająca aktywność i scope kategorii |
Walidacja w innych domenach
Przy tworzeniu/aktualizacji Item i Activity system waliduje czy podana kategoria jest aktywna i ma odpowiedni scope (ActiveCategoryRule). Nie można przypisać nieaktywnej kategorii ani kategorii o niewłaściwym typie.
9. Konfiguracja¶
Enum: CategoryEnumService¶
Plik: app/Domain/Category/Service/CategoryEnumService.php
| Case | Wartość | Opis |
|---|---|---|
CATEGORY_SCOPE_ITEM | item | Kategoria dla obiektów |
CATEGORY_SCOPE_ACTIVITY | activity | Kategoria dla aktywności |
Metody:
| Metoda | Zwraca | Opis |
|---|---|---|
getCategoryScopes() | array | Mapa scope → tłumaczenie (np. 'item' => 'Item') |
getCategoryScopesKeys() | array | Lista kluczy scope: ['item', 'activity'] |
Typy kategorii¶
| Typ | Kod | Przeznaczenie | Relacja |
|---|---|---|---|
| Obiekty | item | Grupowanie sal, boisk, sprzętu | items() → HasMany Item |
| Aktywności | activity | Grupowanie zajęć, kursów, warsztatów | relActivities() → HasMany Activity |
Rozdzielenie typów
Kategoria typu "item" nie może być przypisana do aktywności i odwrotnie. Nazwy kategorii muszą być unikalne w obrębie typu (UniqueScopeCategoryNameRule). Typ (scope) nie może być zmieniony po utworzeniu kategorii.
Walidacja koloru¶
Dozwolone formaty HEX
#RGB— np.#F00(czerwony)#RGBA— np.#F00F(czerwony, pełna przezroczystość)#RRGGBB— np.#FF0000(czerwony)#RRGGBBAA— np.#FF0000FF(czerwony, pełna przezroczystość)
Zabronione kolory
Kolor czarny (#000, #000000) jest zabroniony ze względu na czytelność interfejsu.
Próg wydajności¶
| Parametr | Wartość | Opis |
|---|---|---|
| Próg async (zmiana stanu) | > 50 modeli | Powyżej 50 powiązanych modeli, kaskadowa zmiana stanu wykonywana asynchronicznie |
| Kolejka | domain | SupportEnumService::ON_QUEUE_DOMAIN |
10. Znane ograniczenia i TODO¶
| Nr | Opis | Priorytet |
|---|---|---|
| 1 | Brak soft-delete — usunięta kategoria jest permanentna | Niski |
| 3 | Brak walidacji czy ikona lucide_icon istnieje w bibliotece Lucide | Niski |
| 4 | Brak endpointu DELETE /categories/{id} — kategorie można tylko dezaktywować | Świadome ograniczenie |
| 5 | Pole extra_params nie jest w migracji jako kolumna — obsługiwane przez ResolveModelContext | ⚠️ Do wyjaśnienia |
Przykładowe kategorie¶
Dla obiektów (item)¶
| Nazwa | Ikona | Kolor | Przykłady |
|---|---|---|---|
| Sale konferencyjne | Niebieski | Sala A, Sala B, Sala Rady | |
| Hale sportowe | Zielony | Hala MOSiR, Hala ZS1 | |
| Boiska | Zielony | Boisko Orlik, Kort tenisowy | |
| Baseny | Błękitny | Basen olimpijski, Brodzik | |
| Sprzęt | Szary | Namioty, Stoły, Krzesła |
Dla aktywności (activity)¶
| Nazwa | Ikona | Kolor | Przykłady |
|---|---|---|---|
| Fitness | Różowy | Aerobik, Zumba, Pilates | |
| Zajęcia dla dzieci | Żółty | Pływanie maluchy, Gimnastyka | |
| Warsztaty | Fioletowy | Ceramika, Malarstwo | |
| Kursy językowe | Granatowy | Angielski A1, Niemiecki B2 |
Reguły walidacyjne (szczegóły)¶
UniqueScopeCategoryNameRule¶
Plik: app/Domain/Category/Request/Rule/UniqueScopeCategoryNameRule.php
Sprawdza unikalność nazwy kategorii w obrębie danego scope. Przy aktualizacji wyklucza bieżącą kategorię z porównania ($ignoreId).
ModelBelongsToCategoryRule¶
Plik: app/Domain/Category/Request/Rule/ModelBelongsToCategoryRule.php
Używana w changeState — waliduje czy podane UUID modeli rzeczywiście należą do tej kategorii. Sprawdza relację na podstawie scope:
item→ sprawdzaitems()(HasMany Item)activity→ sprawdzarelActivities()(HasMany Activity)
ActiveCategoryRule (Support)¶
Plik: app/Support/Action/Request/Rule/ActiveCategoryRule.php
Używana w domenach Item i Activity przy tworzeniu/aktualizacji — sprawdza czy kategoria istnieje, jest aktywna (is_active = true) i ma odpowiedni scope.
Struktura plików domeny¶
app/Domain/Category/
├── Action/
│ ├── Attr/
│ │ └── GetTotalRelationsAttr.php # Zliczanie relacji (zależne od scope)
│ ├── Controller/
│ │ ├── IndexCategoryController.php # Lista kategorii (Spatie QueryBuilder)
│ │ ├── ShowCategoryController.php # Szczegóły kategorii
│ │ ├── StoreCategoryController.php # Tworzenie kategorii
│ │ └── UpdateCategoryController.php # Aktualizacja kategorii
│ └── Other/
│ ├── CreateCategory.php # Category::create()
│ ├── UpdatedCategoryObserver.php # Deleguje do IsActive observer
│ └── UpdatedIsActiveCategoryObserver.php # Kaskadowa zmiana stanu
├── Controller/
│ └── CategoryController.php # Główny kontroler (index, show, store, update, changeState)
├── Model/
│ └── Category.php # Model Eloquent
├── Observer/
│ └── CategoryObserver.php # Observer: updated hook
├── Policy/
│ └── CategoryPolicy.php # Policy: view, create, update
├── Request/
│ ├── ChangeStateCategoryControllerRequest.php
│ ├── IndexCategoryControllerRequest.php
│ ├── Rule/
│ │ ├── ModelBelongsToCategoryRule.php # Walidacja przynależności modelu
│ │ └── UniqueScopeCategoryNameRule.php # Unikalna nazwa w scope
│ ├── StoreCategoryControllerRequest.php
│ └── UpdateCategoryControllerRequest.php
├── Resource/
│ └── CategoryResource.php # Transformacja do JSON
└── Service/
├── CategoryEnumService.php # Enum scope: item/activity
├── TraitCategory.php # Logika biznesowa
└── TraitControllerCategory.php # Orchestracja kontrolera
Best practices¶
Wskazówki dla administratorów
- Planuj strukturę — przemyśl kategorie przed dodawaniem obiektów
- Unikaj duplikatów — system wymusza unikalność nazw w obrębie typu
- Używaj ikon — ikony Lucide pomagają wizualnie odróżnić kategorie
- Sezonowe zarządzanie — dezaktywuj kategorie zamiast usuwać
- Selektywna reaktywacja — przy aktywacji podaj listę modeli do przywrócenia
Historia zmian¶
| Data | Typ | Opis |
|---|---|---|
| 26 lut 2026 | Aktualizacja dokumentacji | Restrukturyzacja wg szablonu — dodano: pełną strukturę tabeli DB z indeksami (2.3), diagram ER relacji (2.2), kompletne opisy endpointów API z przykładami JSON request/response (sekcja 3), numerowaną listę reguł biznesowych (4.2), sekcję testów (10), sekcję znanych ograniczeń (11), pełne opisy reguł walidacyjnych, macierz uprawnień. Zachowano i rozszerzono istniejące treści. |
| 15 lut 2026 | Aktualizacja dokumentacji | Kompleksowa aktualizacja — dodano sekcje: API Endpoints, Filtry, Observery, Reguły walidacyjne, Struktura plików. |