
Laravel + Vue: завантаження файлів
23.01.2019 22:23 | Laravel, Vue
Коротко розглянемо, як завантажувати файли в Laravel та Vuejs.
Створюємо новий інстанс Laravel:
laravel new larexp
Переходимо в директорію проекту і відразу встановлюємо залежності:
cd larexp && npm install
Vue
Оскільки це всього лише рецепт, не будемо особливо морочитися і просто перепишемо приклад компонента. Зайдемо в resources/js/components
та змінимо назву ExampleComponent.vue
на AttachmentForm.vue
. Не забудемо внести зміни в реєстрацію компонента у файлі resources/js/app.js
:
Vue.component('attachment-form', require('./components/AttachmentForm.vue').default);
Відкриваємо файл компонента і для початку пропишемо javascript. В даних у нас буде (що відображається) назва і безпосередньо сам файл (attachment). Також знадобляться методи onAttachmentChange()
, який при зміні файлу буде присвоювати його властивості attachment
, та метод submit()
для відправки даних на сервер за допомогою axios
. Оскільки ми передаємо файл, будемо використовувати об'єкт FormData
, який на виході використовує такий же формат, як і при відправці звичайної форми з параметром encoding
, значення якого встановлено в multipart/form-data
. Крім цього, обов'язково треба і в опціях передати дане значення. Знову ж таки для спрощення, після отримання відповіді просто виведемо в консоль повідомлення або відловимо помилку, якщо така буде. Код метода submit()
:
submit () {
const config = { 'content-type': 'multipart/form-data' }
const formData = new FormData()
formData.append('name', this.name)
formData.append('attachment', this.attachment)
axios.post('/', formData, config)
.then(response => console.log(response.data.message))
.catch(error => console.log(error))
}
Швиденько накидаємо форму за допомогою класів Bootstrap і в підсумку файл компонента буде виглядати так:
<template>
<form @submit.prevent="submit">
<div class="form-group">
<input type="text" class="form-control" placeholder="Name" v-model="name">
</div>
<div class="form-group">
<div class="custom-file">
<input type="file"
class="custom-file-input"
id="customFile"
@change="onAttachmentChange"
>
<label class="custom-file-label" for="customFile">Choose file</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</template>
<script>
export default {
data () {
return {
name: null,
attachment: null
}
},
methods: {
submit () {
const config = { 'content-type': 'multipart/form-data' }
const formData = new FormData()
formData.append('name', this.name)
formData.append('attachment', this.attachment)
axios.post('/', formData, config)
.then(response => console.log(response.data.message))
.catch(error => console.log(error))
},
onAttachmentChange (e) {
this.attachment = e.target.files[0]
}
}
}
</script>
Не заради авторизації, а для отримання готового шаблону Blade виконаємо команду:
php artisan make:auth
Відткриваємо resources/views/home.blade.php
і вбудовуємо компонент:
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">File Uploads</div>
<div class="card-body">
<attachment-form></attachment-form>
</div>
</div>
</div>
</div>
</div>
@endsection
Laravel
Створюємо модель і міграцію для файлів-вкладень:
php artisan make:model -m Attachment
Додаємо потрібні поля:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateAttachmentsTable extends Migration
{
public function up()
{
Schema::create('attachments', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('path')->unique();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('attachments');
}
}
та запускаємо:
php artisan migrate
Хоча це і простий приклад, подумаємо про проект трохи більше і про те, що можливо доведеться завантажувати аватар в профіль користувача, зображення і / або вкладення постів або товарів і т.д. Іншими словами, замість того, щоб прописати метод завантаження прямо в моделі, створимо трейт, що дозволить горизонтально розширювати будь-які класи, де необхідне завантаження будь-яких файлів. Заодно і передбачимо ситуацію з використанням різних сховищ і каталогів в цих сховищах. Файл трейта буде розташовуватися в app/Traits/Eloquent
(каталоги створюємо ручками) і буде називатися Uploadable.php
. Ось його код:
<?php
namespace App\Traits\Eloquent;
use Illuminate\Support\Facades\Storage;
trait Uploadable
{
public function upload($file, $storage = 'public', $folder = 'uploads')
{
$filename = uniqid() . '_' . str_replace(' ', '_', $file->getClientOriginalName());
$path = Storage::disk($storage)->putFileAs($folder, $file, $filename);
if (Storage::disk($storage)->exists($path)) {
return $path;
}
return null;
}
}
Можна було і не створювати своє ім'я файлу, а покластися на Laravel, але давайте таки додамо до згенерованого унікального ідентифікатору оригінальну назву файлу, попутно замінивши пробіли на нижні підкреслення.
Включаємо трейт в модель Attachment:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\Traits\Eloquent\Uploadable;
class Attachment extends Model
{
use Uploadable;
protected $fillable = ['name', 'path'];
}
Також створимо клас StoreAttachmentRequest
, в який винесемо валідацію (розширення вказані перші які прийшли в голову + максмальний розмір файлу 1Mb):
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreAttachmentRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => 'required|string|max:255',
'attachment' => 'required|max:1024|mimes:pdf,png,jpeg,gif,txt'
];
}
}
Використаємо HomeController
, в якому напишемо метод store()
для збереженя файлів. Після збереження віддамо повідомлення про те, що файл успішно завантажений (те саме повідомлення, вивід якого ми раніше прописували у Vue):
<?php
namespace App\Http\Controllers;
use App\Attachment;
use App\Http\Requests\StoreAttachmentRequest;
class HomeController extends Controller
{
public function index()
{
return view('home');
}
public function store(StoreAttachmentRequest $request)
{
$attachment = new Attachment;
$attachment->name = $request->name;
$attachment->path = $attachment->upload($request->attachment);
$attachment->save();
return response()->json([
'message' => 'Attachment has been successfully uploaded.',
]);
}
}
Останній штрих - роути. Відткриваємо routes/web.php
та використаємо те, що є, додавши маршрут для збереження/завантаження даних:
Route::get('/', 'HomeController@index')->name('home');
Route::post('/', 'HomeController@store');
Готово, можна перевіряти. Успіхів!