Files
Qore\Next\System\Models\File stores uploaded file metadata and links the physical file to the model that owns it.
Most applications should interact with files through FileField, not by creating File models directly.
What The Model Stores
File uses a UUID primary key and stores:
disk,path,filename,mime_typeandsizefor storage;namefor the field name that uploaded the file;user_idfor the uploading user;entity_typeandentity_idfor the owning model;delete_atfor temporary uploads that should be cleaned up if they are never attached.
The computed url attribute points to the file view route:
$file->url; // route('files.view', $file->id)
Upload Flow
Uploads go through FileController::store() and FileService::storeFileFromUpload():
- The request resolves the resource, model and field when available.
- If the field uploads files, Qore checks max file size and allowed MIME types.
- A
Filerecord is created withdelete_atset a few hours in the future. - The
FilePolicyuploadcheck runs. - The uploaded file is stored on disk.
- The frontend receives
FileJsonResource.
The temporary delete_at value matters because a user can upload a file before the parent form is submitted. If the parent form never saves, the file can be deleted later.
You can also create a temporary Qore file from backend code:
$file = qore()->files()->storeFileFromUpload($uploadedFile, 'attachments');
Uploads and downloads are not authorized by the field alone. app/Policies/FilePolicy.php controls whether the current user may upload or view a file. Check upload() when a FileField upload is rejected, and check view() when an uploaded file cannot be opened.
Attaching Files To Models
Models that use FileField must implement ModelWithFiles and use HasFiles:
use Illuminate\Database\Eloquent\Model;
use Qore\Next\System\Model\HasFiles;
use Qore\Next\System\Model\ModelWithFiles;
class Project extends Model implements ModelWithFiles
{
use HasFiles;
}
FileField attaches files during lazy mutation, after the parent model has been saved:
- removed file IDs are deleted;
- new file IDs are associated to the model;
delete_atis cleared for attached files;- the change is written to the resource logbook.
Viewing Files
FileController::view() finds the file, authorizes view, then serves it from the configured disk.
Images and PDFs are displayed inline. Other MIME types are downloaded with X-Content-Type-Options: nosniff.
Node responses can ask the frontend to download one or more files by adding response state:
$manager->downloadFiles([$file]);
downloadFiles() expects Qore\Next\System\Models\File instances. The files are serialized with FileJsonResource, persisted only in the node response, and the React node context requests /api/files/{uuid} for each file.
For generated files, create a temporary File through FileService::createFile() and pass it to the node manager. Generated files are deleted after 30 minutes by default.
Files created manually with FileService::createFile() are temporary and will be deleted after 30 minutes unless you pass a custom deleteAt value.
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Qore\Next\System\Node\Button\ClickEvent;
use Qore\Next\System\Nodes\ButtonNode;
use Qore\Next\System\Nodes\PageNode;
use function Qore\Next\System\Helpers\qore;
qore()->page('Reports', function (PageNode $page): void {
$page->addChild(
(new ButtonNode('Download report'))->onClick(function (ClickEvent $event): void {
$csv = implode("\n", [
'Name,Total',
'Acme,1250',
'Qore,980',
]);
$temporaryPath = 'exports/'.Str::uuid().'/report.csv';
Storage::disk('local')->put($temporaryPath, $csv);
try {
$report = qore()->files()->createFile(
path: Storage::disk('local')->path($temporaryPath),
);
} finally {
Storage::disk('local')->delete($temporaryPath);
}
$event->getNodeManager()->downloadFiles([$report]);
})
);
});
You can also download an existing file. Fetch the File model by its UUID and pass it to downloadFiles():
use Qore\Next\System\Models\File;
use Qore\Next\System\Node\Button\ClickEvent;
use Qore\Next\System\Nodes\ButtonNode;
use Qore\Next\System\Nodes\PageNode;
qore()->page('Invoices', function (PageNode $page): void {
$page->addChild(
(new ButtonNode('Download invoice'))->onClick(function (ClickEvent $event): void {
$file = File::query()->findOrFail('9b786e3d-9e83-4a42-b8c4-fef5c06725f3');
$event->getNodeManager()->downloadFiles([$file]);
})
);
});
Deleting Files
When a File model is deleted, the model event also deletes the physical file from storage if it exists.
That means application code should delete the File model instead of only deleting the disk object.