Obsługa przesyłania plików (upload) jest jednym z częstych wymagań w aplikacjach webowych. Na szczęście Livewire, w ścisłej integracji z Laravel, pozwala na bardzo prostą i bezpieczną pracę z uploadem. W dzisiejszym artykule omówimy podstawy przesyłania plików w Livewire, walidacji plików i zapisywania ich w storage Laravel. Poruszymy też kwestie bezpieczeństwa, w tym limity rozmiarów i typów.
Artykuł nawiązuje do poprzednich tematów:
- Livewire w Laravel – wprowadzenie
- Tworzenie i zarządzanie stanem komponentów w Livewire
- Obsługa formularzy i walidacja w Livewire
- Zaawansowana praca z danymi (CRUD w Livewire)
- Eventy i komunikacja między komponentami w Livewire
- Usprawnienia i optymalizacja w Livewire
- Przekazywanie parametrów, Sloty i re-używalne komponenty w Livewire
Konfiguracja i podstawowy przykład uploadu pliku w Livewire
Livewire wspiera mechanizm uploadu plików, wykorzystując pod spodem standardowe funkcje Laravel do obsługi żądania i walidacji. Aby rozpocząć, należy upewnić się, że:
- W pliku konfiguracyjnym
config/filesystems.phpmamy zdefiniowany dysk, na który będziemy zapisywać pliki (domyślniepubliclublocal). - Katalog, w którym zapisywane są pliki (np.
storage/app/public) jest poprawnie zlinkowany do publicznej ścieżki (symlink dopublic/storage).
Podstawowy komponent z polem uploadu
Załóżmy, że tworzymy prosty komponent PhotoUpload. W nim mamy właściwość $photo, którą zwiążemy z polem <input type="file" />.
Klasa komponentu
photo->store('photos', 'public');
// Można też np. emitować event informujący o sukcesie
session()->flash('message', 'Zdjęcie zostało zapisane!');
}
public function render()
{
return view('livewire.photo-upload');
}
}
Zwróć uwagę na:
use WithFileUploads;– niezbędne, aby Livewire wiedziało, jak obsługiwać upload plików.$photo– właściwość, której wartość zostanie ustawiona przezwire:modellubwire:model.defer.
Widok photo-upload.blade.php
@if (session()->has('message'))
{{ session('message') }}
@endif
Tutaj kluczowe jest:
wire:model="photo"– dzięki temu Livewire będzie przesyłać zawartość pliku do komponentu w momencie wyboru pliku.form wire:submit.prevent="save"– wysyła formularz do metodysave()w komponencie bez przeładowywania strony.
Po wyborze pliku i kliknięciu „Prześlij” Livewire automatycznie obsłuży upload oraz zapisze go w wybranym miejscu (/storage/app/public/photos/...) dzięki wywołaniu $this->photo->store('photos', 'public').
Walidacja plików
Walidacja plików w Livewire działa podobnie do standardowej walidacji Laravel. Możemy użyć reguł:
required– plik musi być przesłany.image– plik musi być grafiką (jpg, png, bmp, gif, svg, webp).mimes:jpg,pnglubmimetypes:image/jpeg,image/png– określenie dopuszczalnych typów MIME.max:1024– maksymalny rozmiar pliku (w kilobajtach).
Przykładowa walidacja:
public function save()
{
$this->validate([
'photo' => 'required|image|max:1024', // maksymalnie 1MB
]);
$path = $this->photo->store('photos', 'public');
session()->flash('message', "Plik zapisany w: $path");
}
Jeżeli plik nie przejdzie walidacji (np. jest za duży), w widoku pojawi się odpowiedni komunikat błędu przy polu photo.
Zapis pliku do storage i obsługa potencjalnych błędów
1. Ustawianie folderu docelowego
Metoda store($folder, $disk) zapisuje plik w podanym folderze na wskazanym dysku.
$folder– nazwa folderu (lub ścieżka), np.photos/profile-avatars.$disk– nazwa dysku zconfig/filesystems.php, np.publiclubs3.
2. Obsługa błędów
Jeśli w trakcie walidacji dojdzie do błędu (np. przekroczony rozmiar pliku), Livewire automatycznie wyrzuci wyjątek walidacyjny i zwróci komunikat do widoku.
Zachowanie w widoku:
@error('photo')
{{ $message }}
@enderror
3. Wyświetlanie przesłanego pliku
Po zapisaniu pliku w folderze public, można łatwo wyświetlić go w widoku, używając funkcji asset(), bądź z użyciem Storage::url($path):
@if($savedPath)
@endif
Bezpieczeństwo i ograniczenia
- Limity rozmiaru –
max:Xw regułach walidacji to najprostszy sposób. Można też ustawić limit uploadu wphp.ini(upload_max_filesize,post_max_size), co jest istotne, gdy pliki mogą być większe niż kilka MB. - Typy plików – Wskazówki
image,mimes:...czymimetypes:...minimalizują ryzyko, że użytkownik załaduje niebezpieczny plik (np. skrypt). - Folder docelowy – Najlepiej zapisywać dane w
storage/app/publici używać symbolicznego linkupublic/storage, by pliki były dostępne, ale trzymane poza główną ścieżką aplikacji. - Skanowanie antywirusowe – w krytycznych przypadkach warto rozważyć automatyczne skanowanie przesłanych plików (np. przez serwisy typu ClamAV).
Mini-lab: Formularz ładujący plik (np. avatar)
Stwórzmy kompletne ćwiczenie, w którym użytkownik może załadować plik ze zdjęciem profilowym (avatar) i je wyświetlić.
1. Komponent ProfileUpload
a) Generowanie pliku
php artisan make:livewire ProfileUpload
b) Klasa ProfileUpload.php
Poniżej przykładowa implementacja:
currentAvatarPath = $initialPath;
}
public function saveAvatar()
{
// Walidacja
$this->validate([
'avatar' => 'required|image|max:1024', // maks. 1MB
]);
// Zapis pliku
$path = $this->avatar->store('avatars', 'public');
// Ustawiamy nową ścieżkę
$this->currentAvatarPath = $path;
// (Opcjonalnie) tutaj można zaktualizować rekord użytkownika w bazie
// User::find(auth()->id())->update(['avatar' => $path]);
// Komunikat sukcesu
session()->flash('message', 'Avatar został zaktualizowany!');
}
public function deleteAvatar()
{
if ($this->currentAvatarPath) {
Storage::disk('public')->delete($this->currentAvatarPath);
$this->currentAvatarPath = null;
session()->flash('message', 'Avatar został usunięty.');
}
}
public function render()
{
return view('livewire.profile-upload');
}
}
Tutaj:
- Przechowujemy ścieżkę do obecnego avatara (np.
$currentAvatarPath) i wyświetlamy go, jeśli istnieje. - W
saveAvatar()walidujemy obraz, zapisujemy go na dyskupublic/avatars, a następnie ustawiamy$currentAvatarPath. - Metoda
deleteAvatar()kasuje plik z dysku i ustawia ścieżkę nanull.
c) Widok profile-upload.blade.php
Aktualizacja zdjęcia profilowego
@if (session()->has('message'))
{{ session('message') }}
@endif
@if ($currentAvatarPath)
@endif
@if ($currentAvatarPath)
@endif
Ważne elementy w widoku:
wire:model="avatar"zaccept="image/*", by ułatwić użytkownikowi wybór tylko pliku graficznego.- Wyświetlanie informacji o błędzie (
@error('avatar')...). - Przycisk do usunięcia pliku, jeśli aktualnie istnieje avatar.
Integracja
Możesz użyć <livewire:profile-upload :initial-path="$user->avatar" /> w dowolnym szablonie Blade, aby wstawić formularz zmiany zdjęcia profilowego. initialPath może pochodzić z bazy (np. $user->avatar).
Wgranie i wyświetlenie zdjęcia profilowego
W tym zadaniu stworzymy komponent ProfileUpload, który pozwala na wgranie (upload) zdjęcia użytkownika (np. avatara), zapis do storage, a następnie wyświetlenie go w interfejsie.
Krok 1: Wygeneruj komponent
php artisan make:livewire ProfileUpload
W folderze app/Http/Livewire pojawi się plik ProfileUpload.php, a w folderze resources/views/livewire – profile-upload.blade.php.
Krok 2: Klasa ProfileUpload.php
Oto przykładowa implementacja:
*/
public $avatar;
/**
* Przechowuje aktualną ścieżkę do avatara (jeśli istnieje).
* Możemy ją np. pobrać z bazy danych w `mount`.
*/
public $currentAvatarPath;
/**
* Metoda wywoływana podczas inicjalizacji komponentu
* (np. gdy przekazujemy parametry przez ).
*/
public function mount($initialPath = null)
{
$this->currentAvatarPath = $initialPath;
}
/**
* Metoda zapisująca wgrany plik.
*/
public function saveAvatar()
{
// Walidacja pliku (tylko obrazy, max 1 MB)
$this->validate([
'avatar' => 'required|image|max:1024', // w kB
]);
// Zapis pliku w folderze 'avatars' na dysku 'public'
$path = $this->avatar->store('avatars', 'public');
// Ustawiamy nową ścieżkę
$this->currentAvatarPath = $path;
// Opcjonalnie: możesz zaktualizować bazę, np.:
// User::find(auth()->id())->update(['avatar' => $path]);
// Komunikat o sukcesie
session()->flash('message', 'Avatar został pomyślnie zapisany!');
}
public function render()
{
return view('livewire.profile-upload');
}
}
Omówienie
use WithFileUploads;– trait, który umożliwia obsługę przesyłania plików w Livewire.$avatar– publiczna właściwość, do której przypiszemy plik z formularza (wire:model="avatar").saveAvatar()– metoda, która waliduje plik i zapisuje go wstorage/app/public/avatars.
Krok 3: Widok profile-upload.blade.php
Wgraj lub zaktualizuj swój avatar
{{-- Komunikat o powodzeniu --}}
@if (session()->has('message'))
{{ session('message') }}
@endif
{{-- Podgląd aktualnego avatara (o ile istnieje) --}}
@if ($currentAvatarPath)
@endif
{{-- Formularz przesyłania pliku --}}
Omówienie
<input type="file" wire:model="avatar">– to klucz do wiązania pliku z komponentem.@error('avatar') ... @enderror– wyświetlamy komunikat, gdy walidacja pliku zawiedzie (np. za duży rozmiar).<img src="{{ Storage::url($currentAvatarPath) }}" ...>– jeśli mamy aktualny avatar, pokazujemy go w interfejsie.
Użycie komponentu w widoku
Dzięki temu po zalogowaniu się user, komponent wczyta ścieżkę do aktualnego avatara (jeśli istnieje) oraz umożliwi jego zmianę.
Walidacja rozmiaru i typu pliku
W zadaniu powyżej już zastosowaliśmy podstawowe reguły – required|image|max:1024. Spróbujmy je rozszerzyć lub doprecyzować:
Krok 1: Modyfikacja metody walidacji
W komponencie (np. ProfileUpload.php) zmieniamy:
public function saveAvatar()
{
$this->validate([
'avatar' => 'required|mimes:jpg,png,jpeg|max:1024',
// lub 'avatar' => 'required|mimetypes:image/jpeg,image/png|max:1024'
]);
// Reszta logiki pozostaje taka sama...
}
Teraz pliki muszą być w formacie .jpg, .png lub .jpeg, a rozmiar nie przekracza 1 MB.
Jeśli potrzebujesz innego limitu, np. 2 MB, wystarczy max:2048.
Krok 2: Testowanie
- Spróbuj wgrać plik
.pngo rozmiarze ~1,2 MB – pojawi się błąd walidacji o przekroczeniu rozmiaru. - Wgraj plik
.pdf– pojawi się błąd o nieobsługiwanym formacie.
Zaimplementowanie funkcji kasowania plików
W niektórych aplikacjach po zmianie pliku trzeba usunąć poprzedni z dysku, by nie zalegał. Można też dać użytkownikowi możliwość „usunięcia” aktualnego pliku (np. przycisk „Usuń avatar”).
Krok 1: Dodaj metodę deleteAvatar() w komponencie
Przykład w ProfileUpload.php:
public function deleteAvatar()
{
if ($this->currentAvatarPath) {
// Usuwamy plik z dysku 'public'
\Storage::disk('public')->delete($this->currentAvatarPath);
// Resetujemy ścieżkę
$this->currentAvatarPath = null;
session()->flash('message', 'Avatar został usunięty.');
}
}
Krok 2: Dodaj przycisk usuwania w widoku
W profile-upload.blade.php:
@if ($currentAvatarPath)
@endif
Teraz, jeśli użytkownik ma ustawiony jakiś avatar, pojawi się przycisk „Usuń avatar”. Kliknięcie go wywoła deleteAvatar(), co usunie plik z storage/app/public/avatars/....
Krok 3: Testowanie
- Załaduj plik (sprawdź, czy pojawia się miniaturka i komunikat o sukcesie).
- Usuń plik – sprawdź, czy plik fizycznie zniknął z dysku (
storage/app/public/avatars) i czy w interfejsie avatar już się nie wyświetla.
Podsumowanie
Obsługa plików w Livewire jest prosta i naturalnie wpasowuje się w ekosystem Laravel:
- wire:model=”photo” – wiąże pole typu
filez właściwością w komponencie. - WithFileUploads – trait, bez którego upload by nie zadziałał.
- Walidacja – identyczna jak w Laravel (np.
required|image|max:1024). - Przechowywanie w storage – wykorzystanie metody
store()do zapisu na określonym dysku. - Bezpieczeństwo – weryfikacja typu, rozmiaru i właściwe przechowywanie plików (poza rootem aplikacji), a także konfiguracyjne limity w
php.ini.
Dzięki tym technikom możesz tworzyć formularze do uploadu zdjęć, dokumentów czy innych zasobów. Pamiętaj o sprawnym zarządzaniu plikami (usuwanie nieaktualnych) i odpowiednich regułach walidacji, by zachować czystość i bezpieczeństwo w swojej aplikacji.