I build a Laravel SPA for a client. The client wanted a portfolio website that would allow him to upload images as well as data pertaining to that image. So I build a controller:
public function store(Request $request): Response
{
PaintingService::createPaintingService($request);
//This will return to the main gallery
return self::returningToGallery();
}
And I also created a service for this method:
public static function createPaintingService($request): void
{
if ($request->hasFile('image')) {
$fileName = time() . '.' . $request->image->extension();
$request->image->move(public_path('storage'), $fileName);
Painting::create([
'title' => $request->title,
'size' => $request->size,
'medium' => $request->medium,
'location' => $request->location,
'frame_status' => $request->frame_status,
'status' => $request->status,
'notes' => $request->notes,
'category' => $request->category,
'image' => $fileName
]);
}
}
The form written is vue:
<template>
<backend-layout>
<h2 class = "text-center text-4xl mb-10 tracking-wider">Create a New Painting</h2>
<div class = "flex justify-center m-8">
<form @submit.prevent="submit" enctype="multipart/form-data" method="post">
<div class="flex flex-wrap -mx-3 mb-6">
<div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
<label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
for = "project-title">
Painting Title
</label>
<input
id = "project-title"
v-model = "form.title"
class = " appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
placeholder = "Project Title" required
type = "text">
</div>
<div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
<label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
for = "project-category">
Painting Category
</label>
<input
id = "project-category"
v-model = "form.category"
class = " appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
placeholder = "Category" required
type = "text">
</div>
<div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
<label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
for = "painting-size">
Painting Size
</label>
<input
id = "painting-size"
v-model = "form.size"
class = " appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
placeholder = "Painting Size" required
type = "text">
</div>
<div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
<label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
for = "painting-medium">
Painting Medium
</label>
<input
id = "painting-medium"
v-model = "form.medium"
class = " appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
placeholder = "Painting Medium" required
type = "text">
</div>
<div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
<label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
for = "painting-location">
Painting Location
</label>
<input
id = "painting-location"
v-model = "form.location"
class = " appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
placeholder = "Painting Location"
required type = "text">
</div>
<div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
<label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
for = "painting-frame">
Painting Frame
</label>
<input
id = "painting-frame"
v-model = "form.frame_status"
class = " appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
placeholder = "Painting Frame"
required type = "text">
</div>
<div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
<label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
for = "painting-status">
Painting Status
</label>
<select
id = "skill-percentage"
v-model = "form.status"
class = "appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
name = "skill_percentage" required>
<option value = "available">Available</option>
<option value = "unavailable">Not Available</option>
>
</select>
</div>
<div class="w-full md:w-1/2 px-3 mb-6 md:mb-0">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
for="painting-image">
Painting Image
</label>
<input
id = "painting-image"
name = "image"
class = " appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
placeholder = "Painting Image"
type = "file" @change = "previewImage"
required
@input = "form.image = $event.target.files[0] || $event.dataTransfer.files">
</div>
<div class = "w-full md:w-1/2 px-3 mb-6 md:mb-0">
<label class = "block uppercase tracking-wide text-gray-700 text-xs font-light mb-1"
for = "painting-notes">
Notes
</label>
<textarea
id = "painting-notes"
v-model = "form.notes"
class = "appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white-500"
cols = "30" name = "notes" placeholder = "Painting Notes"
required rows = "10"></textarea>
</div>
<div class="w-full md:w-1/2 px-3 mt-5">
<img :src = "url" :alt = "url">
</div>
</div>
<button class = "inline-block m-5" type = "submit">Submit</button>
<Link href="/paintings/" class="inline-block m-5">Back</Link>
</form>
</div>
</backend-layout>
The script of that vue file:
export default {
name: "create",
components: {
"backend-layout": backendLayout,
Link
},
setup() {
const form = useForm({
title: null,
size: null,
medium: null,
location: null,
frame_status: null,
status: null,
notes: null,
category: null,
image: null,
});
function submit(){
form.post(route('paintings.store'),{
forceFormData: true,
});
// Inertia.post(route('paintings.store'), form);
}
return { form, submit }
},
data(){
return{
url: null
}
},
methods:{
previewImage(e){
const imageURL = e.target.files[0];
this.url = URL.createObjectURL(imageURL);
}
},
I was able to get some images. When I conduct a dd test on images that work.
But there are images that won't upload:
I am not sure why.
I discovered after posting this that the memeType is not “image/jpeg” or some other type of image. Instead, it was a generic type “application/octet-stream”. Once I reformatted the image by renaming the image then saving it, I was able to change it the desired memeType.