Upload or upload files in Laravel Inertia with Vue

Video thumbnail

Let's learn how file upload works in Laravel Inertia; as you can imagine, everything related to the base Laravel backend remains exactly the same, and the changes are on the client side so we can use the file upload integration from Inertia.

Configure Disk

We're going to create the disk where the post images will be uploaded, which will be in the public folder:

config/filesystems.php

'public_upload' => [
    'driver' => 'local',
    'root' => public_path(),
],

Controller for the Upload

Initially, we're going to handle file uploads using Inertia forms, specifically the upload of post images, independently of the form we already have for editing; we'll use the Save.vue one by placing a conditional only for editing:

app/Http/Controllers/Dashboard/PostController.php

public function edit(Post $post)
{
    $categories = Category::get();
    return inertia("Dashboard/Post/Edit", compact('post', 'categories'));
}

The controller for image upload is standard, applying validations for the mime type and ensuring it's an image:

$request->validate(
   [
     'image' => 'required|mimes:jpg,jpeg,png,gif|max:10240'
   ]
);

Delete the previous image (if it exists):

Storage::disk("public_upload")->delete("image/post/".$post->image);

Generate a random name for the image:

time()."." .$request['image']->extension();

Move to the public folder:

$request->image->move(public_path("image/post"),$filename);

And update the post:

$post->update($data);

Finally, we have:

app/Http/Controllers/Dashboard/PostController.php

***
use Illuminate\Support\Facades\Storage;
***
public function upload(Request $request, Post $post)
{
    $request->validate(
        [
            'image' => 'required|mimes:jpg,jpeg,png,gif|max:10240'
        ]
    );
    Storage::disk("public_upload")->delete("image/post/" . $post->image);
    $data['image'] = $filename = time() . "." . $request['image']->extension();
    $request->image->move(public_path("image/post"), $filename);
    $post->update($data);
    return to_route('post.index')->with('message', "Upload image to post successfully");
}

We create the route:

routes/web.php

Route::group(['middleware' => [
    'auth:sanctum',
    'prefix' => 'dashboard',
    config('jetstream.auth_session'),
    'verified',
]], function () {
    Route::resource('/category', App\Http\Controllers\Dashboard\CategoryController::class);
    Route::resource('/post', App\Http\Controllers\Dashboard\PostController::class);
    Route::post('/post/upload/{post}', [App\Http\Controllers\Dashboard\PostController::class,'upload'])->name('post.upload');
});

Vue Form for Upload

In the edit form, we define a prop for the image:

 const form = useForm({
      // ***
      category_id: props.post.category_id,
      image: "",
    });

And now, we're going to place an additional block to handle file uploads, as mentioned before, the image upload will be done independently of the update form:

resources/js/Pages/Dashboard/Post/Save.vue

  <div v-if="post.id != ''">
            <div class="container">
                <div class="card">
                    <div class="card-body">
                        <div class="grid grid-cols-2 gap-2">
                            <div class="col-span-6">
                                <InputLabel value="Image" />
                                <TextInput type="file" @input="form.image = $event.target.files[0]" />
                                <InputError :message="errors.image" class="mt-2" />

                                <PrimaryButton @click="upload">Upload</PrimaryButton>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

    </AppLayout>
***
setup(props) {
    const form = useForm({
      // ***
      image: "",
    });

    function submit() {
      router.put(route("post.update", form.id), form);
    }

    function upload() {
      router.post(route("post.upload", form.id), form);
    }

    return { form, submit, upload };
  },

The code above is simple: a file type field with its button, which calls a function named upload() where we send the form, just like we do when updating a record; it's important to note that to use file upload, it must be a POST request; if you want to use file upload in other types of requests like DELETE, PATCH, or PUT, you must use the POST request type and specify the method in question (we'll see this later):

router.post(route(***), {
    _method: "put",
    // tus datos
},

With this, we'll have fully functional file uploads.

Drag and Drop in Vue

Video thumbnail

The next operation we are going to apply is to be able to order the todos, that is, to reorder them via drag and drop so that they have a different order, to put it redundantly. For this, we are going to use the following plugin. There are others, but this one works quite well for us, which is from the SortableJS team or, rather, a variant of SortableJS, which is a very famous JavaScript plugin. In this case, this is like the port for Vue, which is the following: simply type Drag and Drop next in Vue, and it would be the following page:

https://github.com/SortableJS/vue.draggable.next

Important: the one that says next, at least as of today, you have to check that, is for Vue 3, as indicated here, and the one for Vue 2 would be this one we have here. At least as of today, it remains this way, and as I indicated, it is from the SortableJS team. You can take a look at the plugin here in case you don't know it, which is only for JavaScript. Well, here we have the installation, and now we'll see how to use it. We copy the command and install:

$ npm i -S vuedraggable@next

And unlike other plugins, there is no need to configure it in the main Vue instance; simply import it as any component:

<script>
import draggable from "vuedraggable";

export default {
 components: {
   draggable,
 },
}
</script>

And use it; there are several configurations for using it, and you can see the details in the official documentation, but the basic usage, which consists of creating a list that can be reordered via Drag and Drop, is:

<draggable v-model="myArray">
 <template #item="{ element }">
   <div>
     {{ element }}
   </div>
 </template>
</draggable>
***
data() {
 return {
   myArray: ["Test", "Test 2", "Test 3", "Test 4"],
 };
},

Using the draggable component, we specify the v-model, indicating the property, which must be an array, either of Strings, as in the previous example, or of objects; there is no need to use a v-for to iterate over the data, instead, we can use:

#item="{ element }"

The next step is SPA navigation between pages using Inertia Link.

I agree to receive announcements of interest about this Blog.

We will see the process of uploading or loading files in Laravel Inertia, which includes an implementation that shows how to implement drag and drop uploads.

| 👤 Andrés Cruz

🇪🇸 En español