Uploading files
File uploads are tricky. Submit them to a #[LiveAction]
with the files
modifier
on data-live-action
then process them.
Note: files aren't persisted and will be lost on each rerender if not stored.
Current file:
none
Current files:
(none)
// ... use statements hidden - click to show
#[AsLiveComponent]
class UploadFiles
{
use DefaultActionTrait;
public function __construct(private ValidatorInterface $validator)
{
}
#[LiveProp]
public ?string $singleUploadFilename = null;
#[LiveProp]
public ?string $singleFileUploadError = null;
#[LiveProp]
public array $multipleUploadFilenames = [];
#[LiveAction]
public function uploadFiles(Request $request): void
{
$singleFileUpload = $request->files->get('single');
if ($singleFileUpload) {
$this->validateSingleFile($singleFileUpload);
}
if ($singleFileUpload instanceof UploadedFile) {
[$this->singleUploadFilename] = $this->processFileUpload($singleFileUpload);
}
$multiple = $request->files->all('multiple');
foreach ($multiple as $file) {
if ($file instanceof UploadedFile) {
[$filename, $size] = $this->processFileUpload($file);
$this->multipleUploadFilenames[] = ['filename' => $filename, 'size' => $size];
}
}
}
private function processFileUpload(UploadedFile $file): array
{
// in a real app, move this file somewhere
// $file->move(...);
return [$file->getClientOriginalName(), $file->getSize()];
}
private function validateSingleFile(UploadedFile $singleFileUpload): void
{
$errors = $this->validator->validate($singleFileUpload, [
new Assert\File([
'maxSize' => '1M',
]),
]);
if (0 === \count($errors)) {
return;
}
$this->singleFileUploadError = $errors->get(0)->getMessage();
// causes the component to re-render
throw new UnprocessableEntityHttpException('Validation failed');
}
}
<div class="container" {{ attributes }}>
<p class="text-muted"><strong>Note:</strong> files aren't persisted and will be lost on each rerender if not stored.</p>
<div class="form-group mb-3">
<label for="single-file" class="form-label">Single file:</label>
<input
type="file"
name="single"
autocomplete="off"
id="single-file"
class="form-control-file {{ singleFileUploadError ? 'is-invalid' : '' }}"
>
<div class="form-text text-muted">Current file:
{% if singleUploadFilename %}
{{ singleUploadFilename }}
{% else %}
none
{% endif %}
</div>
{% if singleFileUploadError %}
<div class="invalid-feedback">{{ singleFileUploadError }}</div>
{% endif %}
</div>
<div class="form-group mb-3">
<label for="multiple-files" class="form-label">Multiple files:</label>
<input type="file" class="form-control-file" name="multiple[]" autocomplete="off" multiple="" id="multiple-files" >
<div class="form-text text-muted">Current files:
{% if multipleUploadFilenames %}
<ul>
{% for file in multipleUploadFilenames %}
<li>{{ file.filename }} ({{ file.size }} bytes)</li>
{% endfor %}
</ul>
{% else %}
(none)
{% endif %}
</div>
</div>
<button
data-action="live#action"
data-live-action-param="files|uploadFiles"
class="btn btn-primary"
>Upload files!</button>
</div>
Author
lustmored
Published
2023-06-26