До Livewire додана підтримка завантаження файлів і це своєрідна зміна правил гри.
June 10, 2020 / Автор: Caleb Porzio
Завантаження файлів у веб-додатки - це те, з чим я завжди боровся.
Вони прості, коли ви просто вкладаєте тег <input type = "file" всередину тега <form> та подаєте форму старомодним способом.
Однак, все стає складніше в розширеному JavaScript-стилі з поданням форми AJAX.
Livewire нещодавно додав підтримку для завантаження файлів із вікна. Я думаю, що це буде зміна гри, і я не можу чекати, щоб розповісти вам про це!
Давайте розглянемо дві найпоширеніші стратегії завантаження файлів (та їх недоліки), і тоді ми побачимо, як Livewire найкращий в обох світі.
По-перше, традиційне подання форми. Laravel робить обробку таких видів завантажень неймовірно простими. Розглянемо з деяким зразком коду:
Досвід розробника:
<form action="/profile" method="POST" type="multipart"> <input type="text" name="email"> <input type="file" name="photo"> <button>Save</button> </form>
public function store() { request()->validate([ 'username' => 'required|email', 'photo' => 'image|max:2000', ]); $filename = request('photo')->store('photos'); Profile::create([ 'username' => request('username'), 'photo' => $filename, ]); }
Цей підхід простий і простий, але має ряд недоліків:
Розглянемо сучасний фронт-енд на базі JS. Більше традиційних подань форми немає. Все обробляється за допомогою AJAX-запитів на сервері, використовуючи щось на зразок axios або fetch ().
На жаль, завантажити файл через AJAX не так просто, як ви сподіваєтесь.
Крім того, більшість розробників вибирають для завантаження файлу техніку під назвою "сторона завантаження" перед тим, як форма подається з причин, про які я згадував раніше.
Досвід розробника:
Ось компонент Vue, який я зібрав на слух (жодних гарантій, що це насправді працює), щоб продемонструвати цю техніку:
<template> <form @submit.prevent="save"> <input v-model="email" type="text" name="email"> <input @change="updatePhoto" type="file" name="photo"> <button>Save</button> </form> </template> <script> export default { data() { return { email: '', photo: '', file: null, } }, methods: { updatePhoto(event) { let formData = new FormData(); formData.append('file', event.target.files[0]); axios.post('/file-upload', formData) .then(response => { this.photo = response.data.filePath }) }, save() { axios.post('/profile', { email: this.email, photo: this.photo, }).then(response => { ... }) }, } } </script>
Тепер розглянемо код на сервері, необхідний для цього:
public function handleUpload() { request()->validate(['file' => 'file|max:10000']); return [ 'file' => request('file')->storeAs('/tmp'), ]; } public function handleFormSubmit() { request()->validate([ 'email' => 'required|email', 'photo' => 'required|string', ]); $tmpFile = Storage::get('/tmp/'.request('photo')); Storage::put('/avatars/'.request('photo'), $tmpFile); Storage::delete('/tmp/'.request('photo')); Profile::create([ 'username' => request('username'), 'photo' => request('photo'), ]); }
Як я вже згадував, ця стратегія називається "бічне завантаження". Це дозволяє отримати всю потужність та гнучкість, які ви можете захотіти, але ціною деяких надзвичайних кількостей додаткової складності. Часто для чогось такого простого, як дозволити користувачу завантажити аватар.
Зауважте, що цей код стане набагато складнішим, коли ми почнемо додавати такі речі, як перевірка, завантаження спінерів тощо ...
Ми можемо зробити краще, ніж це. Значно краще.
Оскільки Livewire заснований на JavaScript, ми не можемо просто використовувати традиційні подання форм.
Натомість Livewire використовує функцію "сторона завантаження", але приховує всю складність для вас (з конфігурацією ZERO), надаючи вам досвід традиційного подання форми, але з кількома оновленнями.
Ось базовий приклад:
Досвід розробників
class Profile extends Component { use WithFileUploads; public $email; public $photo; public function save() { $this->validate([ 'email' => 'required|email', 'photo' => 'image|max:2000', ]); $filename = $this->photo->store('photos'); Profile::create([ 'username' => $this->username, 'photo' => $filename, ]); } public function render() { return view('livewire.profile'); } }
<form wire:submit.prevent="save"> <input wire:model="email" type="text" name="email"> <input wire:model="photo" type="file" name="photo"> <button>Save</button> </form>
Досить просто так?
Пам'ятайте, що під капотом Livewire насправді "завантажується" файл у тимчасовий каталог.
Перевага тут, однак, полягає в тому, що вам не доведеться робити жодну роботу.
Все вже зроблено за вас.
Дозвольте мені проглянути всі недобрі речі, які Livewire дозволяє виконувати при завантаженні файлів.
Обробка декількох завантажень у Livewire - це вишенька на торті.
Ось як це виглядає:
<input wire:model=“photos” type=“file” multiple>
Livewire виявляє атрибут "декілька" і обробляє все за вас.
На стороні сервера властивість $ this-> photos буде ARRAY завантажених файлів.
Ви можете перевірити їх і зберегти як будь-який інший масив:
... public $email; public $photos; public function save() { $this->validate([ 'email' => 'required|email', 'photos.*' => 'image|max:2000', ]); $filenames = collect($this->photos)->map->store('photos'); Profile::create([ 'username' => $this->username, 'photos' => $filenames->implode(','), ]); } ...
Хочете показати користувачеві показник завантаження під час завантаження файлу?
Знову поводьтеся з цим, як зазвичай у Livewire за допомогою wire:loading
:
<input wire:model=“photo” type=“file”> <div wire:loading wire:target="photo">Uploading...</div>
Повідомлення "Завантаження ..." тепер буде показано протягом усієї тривалості завантаження.
Недостатньо простий показник завантаження?
Livewire розсилає кілька корисних подій JavaScript, які можна легко підключити чимось на зразок AlpineJS.
Ось простий індикатор завантаження, написаний на Alpine всередині компонента Livewire:
<div x-data="{ progress: 0, uploading: false }" @livewire-upload-start="uploading = true" @livewire-upload-finish="uploading = false" @livewire-upload-error="uploading = false" @livewire-upload-progress="progress = $event.detail.progress" > <input wire:model=“photo” type=“file”> <progress max="100" x-bind:value="progress" x-show="uploading"></progress> </div>
Ви можете перевірити файл, як тільки він буде обраний, подібно до того, як ви перевірите будь-яке значення під час оновлення в Livewire.
Підключіться до "оновленого" життєвого циклу за межами власності файлів, і вас буде відключено!
class Profile extends Component { use WithFileUploads; public $email; public $photo; // Validate the photo as soon as it's set. public function updatedSave() { $this->validate([ 'photo' => 'image|max:2000', ]); } ... }
Зауважте, ви отримуєте досвід перевірки в реальному часі на передній панелі, але резервний код - це той самий код обробки помилок Laravel, який ви звикли:
... <input wire:model="photo" type="file" name="photo"> @error('photo') {{ $message }} @enderror ...
Livewire дозволяє легко дозволити користувачам завантажувати файли, які НІКОЛИ фактично не торкаються вашого сервера.
Це надзвичайно корисно для великих завантажень або без серверів, таких як Laravel Vapor.
Вкажіть диск для зберігання у вашому додатку, який використовує драйвер зберігання s3 Laravel, і все буде працювати бездоганно:
config/livewire.php
... 'temporary_file_upload' => [ 'disk' => 's3', ... ], ...
Тепер Livewire буде завантажувати тимчасовий файл безпосередньо в S3 за допомогою попередньо встановленої URL-адреси для завантаження.
Файли зберігатимуться в каталозі, який називається livewire-tmp / за замовчуванням.
Щоб налаштувати цю теку для видалення файлів старших за 24 години, Livewire забезпечує зручну команду artisan:
php artisan livewire:configure-s3-upload-cleanup
Примітка. Ви все ще можете вважати властивість $ this-> photo звичайною, не використовуючи S3. Тільки коли ви отримаєте доступ до його вмісту ($ this-> photo-> get ();) або його розміру (для перевірки: $ this-> photo-> getSize ()) буде викликано S3.
Це дозволяє швидко зберігати ваші запити Livewire для завдань, що не пов'язані з завантаженням.
Іноді ви можете показати користувачеві попередній перегляд файлу, який він тільки що вибрав, перш ніж натиснути кнопку «Надіслати».
Зазвичай це складно, оскільки тимчасові файли завантаження не є загальнодоступними.
Livewire дозволяє надзвичайно легко створювати тимчасові, безпечні, загальнодоступні URL-адреси для використання браузера.
Ось приклад перегляду компонента, який відображає попередній перегляд зображення для завантаженого користувачем файлу:
... @if ($photo) <img src="{{ $photo->temporaryUrl() }}"> @endif <input wire:model="photo" type="file" name="photo"> ...
Тепер, як тільки користувач вибере файл (і після того, як Livewire обробить нове завантаження внутрішньо), він побачить попередній перегляд, перш ніж він вирішить надіслати форму.
Ще більше дивовижності S3
Якщо ви налаштували тимчасовий завантажувальний диск Livewire на "s3", тоді ->temporaryUrl() генерує попередньо підписану URL-адресу тимчасового файлу, яка читається безпосередньо з S3.
Тестувати завантаження файлів за допомогою Livewire неймовірно просто.
Ви можете використовувати всю функціональність, до якої ви звикли, зі стандартними контролерами Laravel.
Ось приклад тестового завантаження файлу в компоненті:
/** @test **/ function can_upload_photo() { Storage::fake(); $file = UploadedFile::fake()->image('avatar.png'); Livewire::test(Profile::class) ->set('email', 'calebporzio@livewire.com') ->set('photo', $file) ->call('save'); Storage::assertExists('avatar.png'); }
Filepond - це фантастична бібліотека JavaScript, яка робить перетягування та інші захоплення, пов'язані із завантаженням, надзвичайно легкими.
Функція завантаження файлів Livewire була створена для задоволення таких інтеграцій.
Якщо вас цікавить, як це виглядає, перейдіть до екранів завантаження файлів Livewire для отримання поглибленого посібника.
Вау, яка особливість. Простий на перший погляд, але набагато потужніший, якщо у вас є більш глибокі потреби.
Моя мета Livewire - зробити веб-розробки в Laravel максимально простими по-людськи (не будучи «рукою»). Я сподіваюся, що ця особливість перегукується з манрою.
Якщо ви готові зануритися, перегляньте документацію або перегляньте нову серію скріншотів із завантаженнями файлів, щоб дізнатися більше!
Щасливого завантаження!
– Caleb