Dokumenty (Document)¶
Ostatnia aktualizacja dokumentacji: 26 lutego 2026 Stan synchronizacji z kodem: Zsynchronizowany
Automatyczne generowanie dokumentów — szablony DOCX wypełniane danymi z systemu.
Co to jest?¶
Domena Dokumentów zarządza szablonami dokumentów i ich generowaniem. To jak drukarka z gotowymi formularzami — wgrywasz szablon umowy z polami do wypełnienia (np. reservation.first_name lub rezerwacja.imie), a system automatycznie podstawia dane i generuje gotowy dokument.
System obsługuje: - Szablony DOCX z placeholderami (format PhpWord: ${klucz}) - Automatyczne wypełnianie danymi z kontekstu (rezerwacja, obiekt, organizacja, użytkownik, system) - Dwujęzyczne klucze — angielskie (reservation.first_name) i polskie (rezerwacja.imie) - Przypisanie szablonów do obiektów (Item) i organizacji (Organization) - Generowanie dokumentów dla rezerwacji, obiektów i organizacji - Walidację kluczy podczas wgrywania szablonu - Zarządzanie stanem aktywności szablonów
Jakie problemy rozwiązuje?¶
Korzyści dla JST
- Oszczędność czasu — dokumenty generują się automatycznie
- Spójność — wszystkie umowy wyglądają tak samo
- Brak błędów — dane pobierane automatycznie z systemu
- Łatwa aktualizacja — zmiana szablonu = zmiana wszystkich przyszłych dokumentów
- Walidacja — system sprawdza poprawność kluczy przy wgrywaniu szablonu
Struktura domeny¶
app/Domain/Document/
├── Action/
│ ├── Attr/
│ │ └── GetFullFilePathAttr.php # Resolves full file path from storage disk
│ ├── Controller/
│ │ ├── IndexDocumentController.php # Lista dokumentów (filtr assignable)
│ │ ├── ShowDocumentController.php # Szczegóły dokumentu
│ │ ├── StoreDocumentController.php # Tworzenie + przypisanie
│ │ ├── UpdateDocumentController.php # Aktualizacja metadanych
│ │ ├── AssignDocumentController.php # Przypisanie do modelu
│ │ ├── UnassignDocumentController.php # Odpisanie od modelu
│ │ ├── GenerateDocumentController.php # Generowanie wypełnionego dokumentu
│ │ ├── DownloadDocumentController.php # Pobieranie szablonu źródłowego
│ │ ├── IndexDocumentKeyController.php # Lista kluczy placeholderów
│ │ └── ShowDocumentKeyController.php # Szczegóły klucza
│ └── Other/
│ ├── AssignDocumentToModel.php # Przypisanie z deduplikacją
│ ├── BuildContext.php # Budowanie kontekstu danych
│ ├── CreateDocument.php # Upload + walidacja kluczy + zapis
│ ├── CreateDocumentAssignable.php # Batch tworzenie przypisań
│ ├── DocumentGenerator.php # Generowanie DOCX (PhpWord)
│ ├── DocumentKeyResolver.php # Mapowanie kluczy EN↔PL + rozwiązywanie wartości
│ ├── DocumentParser.php # Ekstrakcja placeholderów + walidacja
│ ├── UnassignDocumentFromModel.php # Usuwanie przypisania
│ └── UpdateDocument.php # Aktualizacja metadanych
├── Controller/
│ ├── DocumentController.php # Główny kontroler (9 metod)
│ └── DocumentKeyController.php # Kontroler kluczy (2 metody)
├── Model/
│ ├── Document.php # Szablon dokumentu
│ ├── DocumentAssignable.php # Polimorficzne przypisanie
│ └── DocumentKey.php # Klucz placeholdera (baza danych)
├── Observer/
│ └── DocumentObserver.php # Pusty observer (zarejestrowany via ObservedBy)
├── Request/
│ ├── AssignDocumentControllerRequest.php # Walidacja przypisania
│ ├── GenerateDocumentControllerRequest.php # Walidacja generowania
│ ├── IndexDocumentControllerRequest.php # Walidacja listy dokumentów
│ ├── IndexDocumentKeyControllerRequest.php # Walidacja listy kluczy
│ ├── StoreDocumentControllerRequest.php # Walidacja uploadu + klucze DOCX
│ ├── UpdateDocumentControllerRequest.php # Walidacja aktualizacji
│ └── Rule/
│ └── GeneratedForExistsRule.php # Walidacja istnienia modelu docelowego
├── Resource/
│ ├── DocumentResource.php # Zasób dokumentu
│ ├── DocumentAssignableResource.php # Zasób przypisania
│ └── DocumentKeyResource.php # Zasób klucza
└── Service/
├── DocumentEnumService.php # Typy dokumentów + konteksty kluczy
├── TraitDocument.php # Metody domenowe
└── TraitControllerDocument.php # Delegacja do Action/Controller
Kluczowe funkcjonalności¶
Dla administratora / supervisora / pracownika¶
| Funkcja | Opis | Status |
|---|---|---|
| Wgrywanie szablonów | Upload plików DOCX (max 10 MB) z walidacją kluczy | Dostępne |
| Zarządzanie szablonami | Edycja metadanych, aktywacja/dezaktywacja | Dostępne |
| Przypisanie do obiektów | Szablon per obiekt (Item) lub organizacja (Organization) | Dostępne |
| Generowanie dokumentu | Dla rezerwacji, obiektu lub organizacji | Dostępne |
| Pobieranie szablonu | Download oryginalnego pliku DOCX | Dostępne |
| Podgląd kluczy | API z listą dostępnych placeholderów | Dostępne |
| Zmiana stanu | Toggle is_active szablonu | Dostępne |
Jak to działa?¶
Scenariusz 1: Tworzenie szablonu dokumentu¶
flowchart LR
A[Admin tworzy szablon DOCX] --> B[Wgrywa do systemu]
B --> C{Walidacja kluczy}
C -->|OK| D[Przypisuje do obiektu/organizacji]
C -->|Błąd| E[Odrzucenie z listą<br/>nieznanych kluczy]
D --> F[Szablon gotowy do użycia] - Administrator tworzy dokument Word z placeholderami w formacie
${klucz} - Klucze mogą być w języku angielskim (np.
reservation.first_name) lub polskim (np.rezerwacja.imie) - Wgrywa szablon do systemu — system automatycznie wyciąga placeholdery z DOCX
- System waliduje klucze — sprawdza czy istnieją w tabeli
document_keys(kolumnakeylubkey_pl) - Jeśli klucze są nieznane → plik odrzucony z listą nieprawidłowych kluczy (HTTP 422)
- Przypisuje szablon do konkretnego obiektu (Item) i/lub organizacji (Organization)
Scenariusz 2: Generowanie dokumentu¶
sequenceDiagram
participant P as Pracownik
participant S as System
participant BC as BuildContext
participant DKR as DocumentKeyResolver
participant DG as DocumentGenerator
P->>S: POST /documents/{id}/generate
Note over P,S: generated_for_type + generated_for_id
S->>BC: Buduje kontekst danych
BC-->>S: {reservation, item, organization, user}
S->>DG: Generuje dokument
DG->>DG: Wyciąga placeholdery z szablonu
DG->>DKR: Rozwiązuje wartości kluczy
DKR-->>DG: Mapowane wartości
DG->>DG: Wypełnia szablon (PhpWord)
DG-->>S: Wygenerowany plik DOCX
S-->>P: BinaryFileResponse (download) - Pracownik wskazuje typ i ID obiektu, dla którego generuje dokument
BuildContextbuduje kontekst danych:- Reservation → ładuje rezerwację + reservationable (Item lub ActivityItem) + organizację
- Item → ładuje obiekt + organizację
- Organization → ładuje organizację
- Zawsze dodaje zalogowanego użytkownika (
auth()->user())
DocumentGeneratorwyciąga placeholdery z szablonu DOCXDocumentKeyResolvermapuje klucze (obsługuje polskie i angielskie nazwy) i rozwiązuje wartości z kontekstu- Nierozwiązane wartości zastępowane są symbolem
{...} - Wygenerowany plik zapisywany w
storage/app/private/generated/{uuid}.docx - Nazwa pliku do pobrania:
{slug-nazwy-dokumentu}_{YmdHis}.docx
Modele danych¶
Document¶
| Pole | Typ | Opis |
|---|---|---|
id | UUID | Identyfikator |
name | string | Nazwa szablonu (max 255 znaków) |
type | enum | Typ dokumentu (contract, regulation, information, correspondence, other) |
file_path | string | Ścieżka do pliku w storage |
file_name | string | Oryginalna nazwa pliku |
file_size | unsignedBigInteger | Rozmiar pliku w bajtach |
description | string/null | Opis szablonu |
is_active | boolean | Czy szablon jest aktywny |
created_at | datetime | Data utworzenia |
updated_at | datetime | Data aktualizacji |
Model: Domain\Document\Model\Document — rozszerza CustomModel (logowanie aktywności via Spatie), HasUuids, TraitDocument, #[ObservedBy(DocumentObserver)].
Relacje: assignables() → HasMany DocumentAssignable
Computed: full_file_path — accessor rozwiązujący pełną ścieżkę pliku (obsługuje URLe HTTP i ścieżki dyskowe).
DocumentKey¶
| Pole | Typ | Opis |
|---|---|---|
id | UUID | Identyfikator |
key | string | Klucz w języku angielskim (np. reservation.first_name), unique |
key_pl | string/null | Klucz w języku polskim (np. rezerwacja.imie) |
label | string | Etykieta klucza |
description | text/null | Opis klucza |
context | enum | Kontekst klucza (reservation, item, organization, user, global) |
value_type | enum | Typ wartości (string, date, datetime, number, boolean) |
example_value | string/null | Przykładowa wartość |
Model: Domain\Document\Model\DocumentKey — rozszerza CustomModel, HasUuids. Brak ObservedBy — nie ma observera.
Tabela: document_keys — przechowuje definicje kluczy do walidacji.
DocumentAssignable¶
| Pole | Typ | Opis |
|---|---|---|
id | UUID | Identyfikator |
document_id | UUID | ID dokumentu |
assignable_type | string | Typ modelu (Item lub Organization) |
assignable_id | UUID | ID modelu |
created_at | datetime | Data utworzenia |
updated_at | datetime | Data aktualizacji |
Model: Domain\Document\Model\DocumentAssignable — rozszerza CustomModel, HasUuids. Relacja polimorficzna (MorphTo).
Relacje: document() → BelongsTo Document, assignable() → MorphTo (Item lub Organization).
Struktura tabel w bazie danych¶
Migracja: 0001_01_01_000029_create_documents.php
Tabela documents¶
| Kolumna | Typ | Nullable | Default | Opis |
|---|---|---|---|---|
id | uuid | Nie | — | Klucz główny |
name | string | Nie | — | Nazwa szablonu |
type | enum(contract, regulation, information, correspondence, other) | Nie | — | Typ dokumentu |
file_path | string | Nie | — | Ścieżka pliku w storage |
file_name | string | Nie | — | Oryginalna nazwa pliku |
file_size | unsignedBigInteger | Nie | — | Rozmiar pliku w bajtach |
description | text | Tak | null | Opis szablonu |
is_active | boolean | Nie | true | Czy aktywny |
created_at | timestamp | Tak | — | Data utworzenia |
updated_at | timestamp | Tak | — | Data aktualizacji |
Indeksy: (type, is_active)
Tabela document_assignables¶
| Kolumna | Typ | Nullable | Default | Opis |
|---|---|---|---|---|
id | uuid | Nie | — | Klucz główny |
document_id | uuid | Nie | — | FK → documents.id (CASCADE) |
assignable_type | string | Nie | — | Typ modelu (Item/Organization) |
assignable_id | uuid | Nie | — | ID modelu |
created_at | timestamp | Tak | — | Data utworzenia |
updated_at | timestamp | Tak | — | Data aktualizacji |
Klucz obcy: document_id → documents.id ON DELETE CASCADE
Constraint unique: (document_id, assignable_type, assignable_id)
Indeksy: document_id, (assignable_type, assignable_id)
Tabela document_keys¶
| Kolumna | Typ | Nullable | Default | Opis |
|---|---|---|---|---|
id | uuid | Nie | — | Klucz główny |
key | string | Nie | — | Klucz EN (unique) |
key_pl | string | Tak | null | Klucz PL |
label | string | Nie | — | Etykieta |
description | text | Tak | null | Opis |
context | enum(reservation, item, organization, user, global) | Nie | — | Kontekst |
value_type | enum(string, date, datetime, number, boolean) | Nie | — | Typ wartości |
example_value | string | Tak | null | Przykładowa wartość |
created_at | timestamp | Tak | — | Data utworzenia |
updated_at | timestamp | Tak | — | Data aktualizacji |
Indeksy: key (unique), context, key_pl
Seeding kluczy w migracji
Migracja zawiera seed 32 predefiniowanych kluczy placeholderów (12 rezerwacja, 8 obiekt, 6 organizacja, 2 użytkownik, 4 system). Klucze są tworzone via DocumentKey::firstOrCreate().
Typy dokumentów¶
| Enum | Wartość | Opis |
|---|---|---|
CONTRACT | contract | Umowa |
REGULATION | regulation | Regulamin |
INFORMATION | information | Informacja |
CORRESPONDENCE | correspondence | Korespondencja |
OTHER | other | Inny |
Źródło: DocumentEnumService::getDocumentTypes().
Konteksty kluczy¶
| Enum | Wartość | Opis |
|---|---|---|
RESERVATION | reservation | Dane rezerwacji |
ITEM | item | Dane obiektu |
ORGANIZATION | organization | Dane organizacji |
USER | user | Dane użytkownika |
GLOBAL | global | Dane systemowe |
Źródło: DocumentEnumService::getDocumentKeyContexts().
Dostępne placeholdery¶
System udostępnia 32 placeholdery do wykorzystania w szablonach. Każdy klucz ma wersję angielską i polską — w szablonie DOCX można użyć dowolnej z nich.
Dane rezerwacji (12 kluczy)¶
| Klucz EN | Klucz PL | Opis | Przykład |
|---|---|---|---|
reservation.id | rezerwacja.id | ID rezerwacji | 550e8400-... |
reservation.uuid | rezerwacja.uuid | UUID rezerwacji | abc-123-def |
reservation.total_price | rezerwacja.cena_calkowita | Cena całkowita | 250,00 |
reservation.status | rezerwacja.status | Status rezerwacji | RES_COMPLETED |
reservation.first_name | rezerwacja.imie | Imię rezerwującego | Jan |
reservation.last_name | rezerwacja.nazwisko | Nazwisko rezerwującego | Kowalski |
reservation.email | rezerwacja.email | Email rezerwującego | jan@example.com |
reservation.mobile_phone | rezerwacja.telefon | Telefon rezerwującego | +48 123 456 789 |
reservation.created_at | rezerwacja.data_utworzenia | Data utworzenia | 2024-02-15 10:00 |
reservation.date_time_from | rezerwacja.data_od | Data rozpoczęcia | 2024-02-15 10:00 |
reservation.date_time_to | rezerwacja.data_do | Data zakończenia | 2024-02-15 12:00 |
reservation.days_time_array | rezerwacja.wszystkie_dni_rezerwacji | Wszystkie dni rezerwacji | (tablica → tekst) |
Dane obiektu (8 kluczy)¶
| Klucz EN | Klucz PL | Opis | Przykład |
|---|---|---|---|
item.name | obiekt.nazwa | Nazwa obiektu | Sala konferencyjna A |
item.full_address | obiekt.pelny_adres | Pełny adres | ul. Sportowa 5, 00-001 Warszawa |
item.street | obiekt.ulica | Ulica | ul. Sportowa |
item.street_number | obiekt.numer | Numer | 5 |
item.postal_code | obiekt.kod_pocztowy | Kod pocztowy | 00-001 |
item.city | obiekt.miasto | Miasto | Warszawa |
item.capacity | obiekt.pojemnosc | Pojemność | 50 |
item.area | obiekt.powierzchnia | Powierzchnia | 120 |
Dane organizacji (6 kluczy)¶
| Klucz EN | Klucz PL | Opis | Przykład |
|---|---|---|---|
organization.name | organizacja.nazwa | Nazwa organizacji | Gmina Przykładowo |
organization.nip | organizacja.nip | NIP | 123-456-78-90 |
organization.regon | organizacja.regon | REGON | 123456789 |
organization.email | organizacja.email | kontakt@gmina.pl | |
organization.mobile_phone | organizacja.telefon | Telefon | +48 12 345 67 89 |
organization.full_address | organizacja.pelny_adres | Pełny adres | ul. Główna 1, 00-001 Warszawa |
Dane użytkownika (2 klucze)¶
| Klucz EN | Klucz PL | Opis | Przykład |
|---|---|---|---|
user.name | uzytkownik.imię_i_nazwisko | Imię i nazwisko zalogowanego użytkownika | Anna Nowak |
user.email | uzytkownik.email | Email zalogowanego użytkownika | anna@gmina.pl |
Dane systemowe (4 klucze)¶
| Klucz EN | Klucz PL | Opis | Przykład |
|---|---|---|---|
system.name | system.nazwa | Nazwa aplikacji | Zajmij.to |
system.current_date | system.aktualna_data | Aktualna data | 2024-02-20 |
system.current_datetime | system.aktualna_data_czas | Aktualna data i czas | 2024-02-20 14:30 |
system.current_year | system.aktualny_rok | Aktualny rok | 2024 |
Walidacja kluczy
Klucze zdefiniowane są w tabeli document_keys w bazie danych. Podczas uploadu szablonu system wyciąga wszystkie placeholdery z pliku DOCX i waliduje je — zarówno po kolumnie key (EN) jak i key_pl (PL). Nieznane klucze powodują odrzucenie pliku z HTTP 422.
Powiązania z innymi domenami¶
| Domena | Powiązanie |
|---|---|
| Zasoby (Item) | Szablony przypisywane do obiektów, dane obiektu w placeholderach |
| Organizacje (Organization) | Szablony przypisywane do organizacji, dane organizacji w placeholderach |
| Rezerwacje (Reservation) | Generowanie dokumentów dla rezerwacji, dane rezerwacji w placeholderach |
| Aktywności (Activity) | Kontekst ActivityItem przy generowaniu dokumentu dla rezerwacji aktywności |
| Użytkownicy (User) | Dane zalogowanego użytkownika w placeholderach |
API Endpoints¶
Dokumenty (/api/documents)¶
| Metoda | Endpoint | Opis | Request |
|---|---|---|---|
GET | /documents | Lista dokumentów | IndexDocumentControllerRequest |
GET | /documents/{documentId} | Szczegóły dokumentu | — |
GET | /documents/{documentId}/download | Pobierz szablon DOCX | — |
POST | /documents | Wgraj nowy szablon | StoreDocumentControllerRequest |
PATCH | /documents/{documentId} | Aktualizuj metadane | UpdateDocumentControllerRequest |
POST | /documents/{documentId}/assign | Przypisz do modelu | AssignDocumentControllerRequest |
DELETE | /documents/{documentId}/unassign/{assignableId} | Odpisz od modelu | — |
POST | /documents/{documentId}/generate | Generuj wypełniony dokument | GenerateDocumentControllerRequest |
PATCH | /documents/{documentId}/change-state | Toggle is_active | — |
Klucze dokumentów (/api/document-keys)¶
| Metoda | Endpoint | Opis | Request |
|---|---|---|---|
GET | /document-keys | Lista kluczy placeholderów | IndexDocumentKeyControllerRequest |
GET | /document-keys/{keyId} | Szczegóły klucza | — |
Brak middleware na DocumentKeyController
Kontroler DocumentKeyController nie ma middleware z ograniczeniem ról — endpointy kluczy są dostępne dla wszystkich zalogowanych użytkowników.
Parametry żądań¶
Store (POST /documents)¶
| Pole | Walidacja | Opis |
|---|---|---|
name | required, string, max:255 | Nazwa szablonu |
type | required, string, in:DocumentEnumService | Typ dokumentu |
file | required, file, mimes:docx, max:10240 | Plik DOCX (max 10 MB) |
description | nullable, string | Opis |
is_active | sometimes, boolean | Czy aktywny (domyślnie: true) |
assignables | required, array | Przypisania do modeli |
assignables.*.assignable_type | required, string, in:[Item, Organization] | Typ modelu |
assignables.*.assignable_id | required_with, uuid | ID modelu |
Walidacja kluczy przy uploadzie
Request StoreDocumentControllerRequest wykonuje dodatkową walidację (withValidator): wyciąga placeholdery z wgranego pliku DOCX i sprawdza je w tabeli document_keys. Nieprawidłowe klucze dodawane jako błąd walidacji na polu file.
Update (PATCH /documents/{id})¶
| Pole | Walidacja | Opis |
|---|---|---|
name | sometimes, string, max:255 | Nazwa szablonu |
type | sometimes, string, in:DocumentEnumService | Typ dokumentu |
description | nullable, string | Opis |
is_active | sometimes, boolean | Czy aktywny |
Update nie pozwala na zmianę pliku
Endpoint aktualizacji zmienia jedynie metadane — nie ma możliwości ponownego uploadu pliku DOCX. Aby zmienić plik, należy utworzyć nowy dokument.
Generate (POST /documents/{id}/generate)¶
| Pole | Walidacja | Opis |
|---|---|---|
generated_for_type | required, string, in:[Reservation, Item, Organization] | Typ modelu docelowego |
generated_for_id | required, uuid, GeneratedForExistsRule | ID modelu docelowego |
GeneratedForExistsRule — niestandardowa reguła walidacji sprawdzająca istnienie rekordu w odpowiedniej tabeli na podstawie generated_for_type.
Assign (POST /documents/{id}/assign)¶
| Pole | Walidacja | Opis |
|---|---|---|
assignable_type | required, string, in:[Item, Organization] | Typ modelu |
assignable_id | required, uuid | ID modelu |
Index Documents (GET /documents)¶
| Pole | Walidacja | Opis |
|---|---|---|
per_page | sometimes, integer, min:1, max:100 | Liczba wyników na stronę |
assignable_type | sometimes, string, in:[Item, Organization] | Filtr po typie przypisania |
assignable_id | required_with:assignable_type, uuid | ID obiektu do filtrowania |
Index DocumentKeys (GET /document-keys)¶
| Pole | Walidacja | Opis |
|---|---|---|
per_page | sometimes, integer, min:1, max:100 | Liczba wyników na stronę |
context | sometimes, string, in:DocumentEnumService::keyContexts | Filtr po kontekście |
value_type | sometimes, string | Filtr po typie wartości |
Odpowiedzi API¶
DocumentResource¶
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Umowa najmu sali",
"type": "contract",
"type_lang": "Contract",
"file_path": "documents/contract/abc123.docx",
"file_name": "umowa_najmu_sali.docx",
"file_size": 45678,
"description": "Szablon umowy najmu sali konferencyjnej",
"is_active": true,
"created_at": "2024-02-15 10:00:00",
"updated_at": "2024-02-15 10:00:00",
"can": {
"view": true,
"create": true,
"update": true
},
"assignables": [
{
"id": "...",
"document_id": "550e8400-...",
"assignable_type": "Domain\\Item\\Model\\Item",
"assignable_id": "...",
"created_at": "2024-02-15 10:00:00",
"updated_at": "2024-02-15 10:00:00"
}
]
}
Relacje: RELATIONS = ['assignables'].
Pole can
Pole can zawiera uprawnienia aktualnie zalogowanego użytkownika (na podstawie DocumentPolicy). Jest usuwane z odpowiedzi dla niezalogowanych użytkowników (UNALLOWED_NO_AUTH_KEYS = ['can']).
DocumentKeyResource¶
{
"id": "660e8400-...",
"key": "reservation.first_name",
"key_pl": "rezerwacja.imie",
"label": "Imię rezerwującego",
"description": "Imię osoby dokonującej rezerwacji",
"context": "reservation",
"context_pl": "Reservation",
"value_type": "string",
"example_value": "Jan",
"created_at": "2024-02-15 10:00:00",
"updated_at": "2024-02-15 10:00:00"
}
Odpowiedzi na Store/Update¶
Endpointy store i update zwracają komunikat sukcesu (nie zasób):
Tworzenie szablonu¶
Krok 1: Sprawdź dostępne klucze¶
Użyj endpointu GET /api/document-keys aby zobaczyć listę dostępnych kluczy. Filtruj po kontekście: ?context=reservation.
Krok 2: Przygotuj dokument Word¶
Stwórz dokument .docx z placeholderami w formacie ${klucz} (format PhpWord TemplateProcessor):
UMOWA NAJMU
zawarta dnia ${system.current_date} (lub ${system.aktualna_data})
pomiędzy:
${organization.name} z siedzibą ${organization.full_address}
NIP: ${organization.nip}, REGON: ${organization.regon}
reprezentowaną przez ${user.name}
a
${reservation.first_name} ${reservation.last_name}
email: ${reservation.email}, tel: ${reservation.mobile_phone}
Przedmiot najmu: ${item.name}
Adres obiektu: ${item.full_address}
Pojemność: ${item.capacity} osób
Termin: od ${reservation.date_time_from} do ${reservation.date_time_to}
Kwota: ${reservation.total_price} zł
Klucze po polsku
Można używać kluczy w języku polskim — system je automatycznie rozpozna:
Krok 3: Wgraj szablon¶
POST /api/documents
Content-Type: multipart/form-data
name: "Umowa najmu sali"
type: "contract"
file: [plik.docx]
description: "Szablon umowy najmu"
assignables[0][assignable_type]: "Domain\Item\Model\Item"
assignables[0][assignable_id]: "uuid-obiektu"
System automatycznie: 1. Wyciągnie placeholdery z pliku DOCX 2. Zwaliduje klucze w tabeli document_keys 3. Zapisze plik w storage/app/private/documents/{type}/ 4. Utworzy rekord Document i przypisania DocumentAssignable
Krok 4: Generuj dokument¶
POST /api/documents/{documentId}/generate
{
"generated_for_type": "Domain\\Reservation\\Model\\Reservation",
"generated_for_id": "uuid-rezerwacji"
}
Odpowiedź: BinaryFileResponse — plik DOCX do pobrania.
Budowanie kontekstu (BuildContext)¶
Kontekst danych budowany jest automatycznie na podstawie generated_for_type:
flowchart TD
A[generated_for_type] --> B{Typ}
B -->|Reservation| C[Ładuj rezerwację<br/>+ reservationable<br/>+ reservationSlots]
B -->|Item| D[Ładuj obiekt<br/>+ organizację]
B -->|Organization| E[Ładuj organizację]
C --> F{reservationable type}
F -->|Item| G["context: reservation, item, organization"]
F -->|ActivityItem| H["context: reservation, activity_item, activity, organization"]
D --> I["context: item, organization"]
E --> J["context: organization"]
G --> K[+ user z auth]
H --> K
I --> K
J --> K | generated_for_type | Kontekst | Źródło danych |
|---|---|---|
Reservation (Item) | reservation, item, organization, user | Rezerwacja → Item → Organization |
Reservation (ActivityItem) | reservation, activity_item, activity, organization, user | Rezerwacja → ActivityItem → Activity → Organization |
Item | item, organization, user | Obiekt → Organization |
Organization | organization, user | Organizacja |
Użytkownik w kontekście
Zalogowany użytkownik (auth()->user()) jest zawsze dodawany do kontekstu — dzięki temu klucze user.name i user.email wypełniają się danymi aktualnie zalogowanej osoby (np. pracownika generującego dokument).
Mechanizm rozwiązywania kluczy (DocumentKeyResolver)¶
flowchart LR
A["Klucz z szablonu<br/>(np. rezerwacja.imie)"] --> B[normalizeKey]
B --> C{"Czy klucz PL<br/>w keyMappings?"}
C -->|Tak| D["Zamień na EN<br/>(reservation.first_name)"]
C -->|Nie| E[Użyj oryginalnego]
D --> F[resolveKey]
E --> F
F --> G{Prefix klucza}
G -->|reservation.| H[resolveReservationKey]
G -->|item.| I[resolveItemKey]
G -->|organization.| J[resolveOrganizationKey]
G -->|user.| K[resolveUserKey]
G -->|system.| L[resolveSystemKey]
H --> M[Wartość z modelu]
I --> M
J --> M
K --> M
L --> M Kluczowe szczegóły: - reservation.total_price — formatowany: number_format($value, 2, ',', ' ') (np. 250,00) - reservation.created_at — format: Y-m-d H:i - item.full_address / organization.full_address — składany z pól: "{street} {street_number}, {postal_code} {city}" - system.name — pobierany z config('app.name', 'Zajmij.to') - reservation.days_time_array — zwraca tablicę → konwertowana na tekst via implode("\n", ...)
Przechowywanie plików¶
| Lokalizacja | Opis |
|---|---|
storage/app/private/documents/{type}/ | Szablony źródłowe (per typ: contract, regulation, etc.) |
storage/app/private/generated/ | Wygenerowane dokumenty (nazwa: {uuid}.docx) |
Dysk: private (Laravel Storage).
Limit rozmiaru: max 10 MB (10240 KB) — walidacja w StoreDocumentControllerRequest.
Format: wyłącznie DOCX (mimes:docx, mimetypes:application/vnd.openxmlformats-officedocument.wordprocessingml.document).
Wyjątki¶
| Wyjątek | Kod | Opis |
|---|---|---|
InvalidDocumentKeysException | 422 | Plik zawiera nieznane klucze placeholderów |
NotFoundModelException | 404 | Dokument/klucz/model docelowy nie znaleziony |
InvalidDocumentKeysException (Infrastructure\Exceptions) — rzucany przez CreateDocument gdy walidacja kluczy w DOCX nie przejdzie. Wiadomość zawiera listę nieprawidłowych kluczy.
Wartość biznesowa¶
Argumenty dla decydentów
- Automatyzacja — koniec ręcznego wypełniania umów
- Standaryzacja — wszystkie dokumenty zgodne z wzorem
- Elastyczność — każdy obiekt może mieć własny szablon
- Dwujęzyczność kluczy — szablony mogą używać kluczy polskich lub angielskich
- Walidacja — system nie pozwoli wgrać szablonu z błędnymi kluczami
- Audyt — logowanie aktywności na modelu Document (Spatie ActivityLog via CustomModel)
Technologia¶
| Element | Technologia |
|---|---|
| Format szablonów | DOCX (Microsoft Word) |
| Biblioteka | PhpWord TemplateProcessor |
| Przechowywanie | Laravel Storage (dysk private) + baza danych |
| Format wyjściowy | DOCX |
| Walidacja kluczy | Tabela document_keys (klucze EN + PL) |
| Logowanie aktywności | Spatie ActivityLog (via CustomModel, tylko model Document) |
Uprawnienia¶
| Rola | Podgląd | Wgrywanie | Edycja | Generowanie | Przypisanie | Zmiana stanu | Pobieranie |
|---|---|---|---|---|---|---|---|
| Administrator | Tak | Tak | Tak | Tak | Tak | Tak | Tak |
| Supervisor | Tak | Tak | Tak | Tak | Tak | Tak | Tak |
| Employee | Tak | Nie | Nie | Tak | Nie | Nie | Tak |
Dwupoziomowa autoryzacja
Uprawnienia działają na dwóch poziomach:
- Middleware (
role:admin|supervisor|employee) — ogranicza dostęp do wszystkich endpointów tylko dla tych trzech ról. - Policy (
DocumentPolicy) — dodatkowo ogranicza operacje zapisu:create()→ wymaga roli Supervisor (Admin ma dostęp przezbefore()wBasePolicy)update()→ wymaga roli Supervisor (Admin ma dostęp przezbefore()wBasePolicy)
W efekcie Employee może jedynie przeglądać (index, show), pobierać (download) i generować (generate) dokumenty — nie może ich tworzyć, edytować, przypisywać, odpisywać ani zmieniać stanu.
Policy (DocumentPolicy)¶
DocumentPolicy rozszerza BasePolicy (która w before() nadaje Adminowi pełny dostęp).
| Metoda | Logika | Dostęp |
|---|---|---|
view(User, Document) | return true | Każdy zalogowany użytkownik |
create(User) | $this->isSupervisor($user) | Admin (via before) + Supervisor |
update(User, Document) | $this->isSupervisor($user) | Admin (via before) + Supervisor |
Brak metody delete
Policy nie definiuje metody delete — usuwanie dokumentów nie jest zaimplementowane w kontrolerze.
Observer¶
DocumentObserver — zarejestrowany via #[ObservedBy(DocumentObserver::class)] na modelu Document. Klasa jest pusta — nie implementuje żadnych hooków lifecycle.
Diagram komponentów¶
flowchart TB
subgraph Controllers
DC[DocumentController<br/>9 metod]
DKC[DocumentKeyController<br/>2 metody]
end
subgraph "Action/Controller"
IDC[IndexDocumentController]
SDC[ShowDocumentController]
STDC[StoreDocumentController]
UDC[UpdateDocumentController]
ADC[AssignDocumentController]
UADC[UnassignDocumentController]
GDC[GenerateDocumentController]
DDC[DownloadDocumentController]
IDKC[IndexDocumentKeyController]
SDKC[ShowDocumentKeyController]
end
subgraph "Action/Other"
CD[CreateDocument]
UD[UpdateDocument]
ADM[AssignDocumentToModel]
UADM[UnassignDocumentFromModel]
CDA[CreateDocumentAssignable]
DG[DocumentGenerator]
DP[DocumentParser]
DKR[DocumentKeyResolver]
BC[BuildContext]
end
subgraph Models
D[Document]
DK[DocumentKey]
DA[DocumentAssignable]
end
DC --> IDC & SDC & STDC & UDC & ADC & UADC & GDC & DDC
DKC --> IDKC & SDKC
STDC --> CD --> DP
STDC --> CDA
UDC --> UD
ADC --> ADM
UADC --> DA
GDC --> BC & DG
DG --> DP & DKR
CD --> D & DA
DP --> DK
ADM --> DA
BC --> D Znane ograniczenia i TODO¶
| Ograniczenie | Opis |
|---|---|
| Brak usuwania dokumentów | Kontroler nie implementuje metody delete — usunięcie szablonu jest możliwe jedynie bezpośrednio w bazie danych |
| Brak aktualizacji pliku | Endpoint update zmienia jedynie metadane — nie pozwala na ponowny upload pliku DOCX |
| Pusty observer | DocumentObserver jest zarejestrowany, ale nie implementuje żadnej logiki |
| Wygenerowane pliki nie są czyszczone | Pliki w storage/app/private/generated/ nie są automatycznie usuwane — brak CRON joba do czyszczenia |
Policy view niewykorzystana | Metoda view() w DocumentPolicy istnieje, ale nie jest wywoływana przez żaden endpoint kontrolera |