Backend
The Documents module handles file upload, metadata, label tagging, file download/preview, and CRUD within the project context, with a filterable global index.
File Structure
app/
├── Http/
│ ├── Controllers/Documents/
│ │ └── DocumentController.php
│ └── Requests/Documents/
│ ├── StoreDocumentRequest.php
│ └── UpdateDocumentRequest.php
├── Models/
│ └── Document.php
├── Queries/Documents/
│ ├── DocumentIndexQuery.php
│ └── DocumentStatsQuery.php
└── Services/Documents/
└── DocumentService.php
Routes
| Method | URI | Action | Description |
|---|---|---|---|
| GET | /documents | index | Global paginated document list with filters |
| POST | /projects/{project}/documents | store | Upload new document to the project |
| PUT | /projects/{project}/documents/{document} | update | Update document metadata/labels |
| DELETE | /projects/{project}/documents/{document} | destroy | Delete document and file |
| GET | /projects/{project}/documents/{document}/download | download | File download |
| GET | /projects/{project}/documents/{document}/preview | preview | Inline file preview |
Controller
The DocumentController orchestrates the flow and delegates storage logic to DocumentService.
Methods
- index() - Uses
DocumentIndexQueryfor paginated list andDocumentStatsQueryfor statistics cards - store() - Validates with
StoreDocumentRequest, delegates upload toDocumentService::upload()and redirects toprojects.show?tab=documents - update() - Validates with
UpdateDocumentRequest, delegates update toDocumentService::update() - destroy() - Delegates deletion to
DocumentService::delete() - download() - Delegates file download to
DocumentService::download() - preview() - Delegates inline preview to
DocumentService::preview()
Service
DocumentService encapsulates the module's file-system logic.
Main Responsibilities
- upload() - generates a unique filename, saves file to
local/documentsdisk, creates document record, and syncs labels - update() - updates only metadata (
name,notes) and syncs labels - delete() - deletes physical file (if present) then the DB record
- download() - verifies file existence and returns a download response with the document name
- preview() - verifies file existence, determines mime-type, and returns an inline response with secure headers
Model
The Document model is located in app/Models/Document.php.
Features
- project relationship - each document belongs to a project
- labels relationship - many-to-many with
Labelviadocument_labelpivot - Scopes -
forProject(),withLabel(),search(),recent() - File helpers -
file_size(accessor),file_extension(accessor) - URL helpers -
getDownloadUrl(),getPreviewUrl(),getDeleteUrl(),getUpdateUrl() - toFormPayload() - edit payload (
id+ editable fields +label_ids)
Form Requests
Validation handled by:
- StoreDocumentRequest - document creation/upload
- UpdateDocumentRequest - document metadata update
Required Fields (store)
name- document namefile- required file (mimes:pdf,jpg,jpeg,png, max 10MB)
Required Fields (update)
name- document name
Optional Fields
label_ids- label array (exists:labels,id)notes- notes (max:1000)
Query Classes
The module uses Query Classes to keep query logic out of the controller.
DocumentIndexQuery
Manages the global document list with:
- eager loading
project,labels - filter by label (
label_id) - text search (
search) on document/project name - recent sorting (
uploaded_at descviarecent()scope) - pagination (20)
DocumentStatsQuery
Calculates statistics for the index:
this_month- documents uploaded in the current monthby_label- top labels by document count (max 5)