sididev's avatar

Problem with updating a blog in Laravel Inertia.js when uploading an image

I'm facing an issue with my Laravel Inertia.js code when trying to update my blog. During the update process, when I upload a new image, I receive validation errors for the 'title' and 'content' fields even though they are filled out. However, if I update without uploading an image, everything works fine.

// web.php
Route::resource('/blog', BlogController::class);
// controller

    public function edit (Blog $blog)
    {
        return Inertia::render('Blog/Edit', [
            'blog' => $blog
        ]);
    }

    public function update(Request $request, Blog $blog)
    {
        $validated = $request->validate([
            'title' => 'required|string|min:3',
            'content' => 'required|min:3',
            'image_path' => 'nullable|max:2048',
        ]);

        if ($request->file('image_path')) {
            if ($blog->image_path) {
                Storage::disk('public')->delete($blog->image_path);
            }
            $imagePath = $request->file('image_path')->store('images/blog', 'public');
        } else {
            $imagePath = $blog->image_path;
        }

        $blog->update([
            'title' => $validated['title'],
            'content' => $validated['content'],
            'image_path' => $imagePath,
        ]);

        return Redirect::route('blog.list')->with(
            'message',
            'Mis à jour avec succès !'
        );
    }    
// model

<?php
namespace App\Models;
use Cviebrock\EloquentSluggable\Sluggable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;

class Blog extends Model
{
    use HasFactory, Sluggable;

    protected $fillable = ['title', 'content', 'image_path'];

    protected static function boot()
    {
        parent::boot();

        static::deleting(function ($blog) {
            if ($blog->image_path) {
                Storage::disk('public')->delete($blog->image_path);
            }
        });
    }

    public function sluggable(): array
    {
        return [
            'slug' => [
                'source' => 'title'
            ]
        ];
    }

    public function getRouteKeyName()
    {
        return 'slug';
    }

}
// Edit form 

<template>
    <AuthenticatedLayout>
        <div
            class="min-h-screen flex items-center justify-center bg-gray-100 py-12 px-4 sm:px-6 lg:px-8"
        >
            <div
                class="max-w-4xl w-full space-y-8 bg-white p-6 rounded-lg shadow-md"
            >
                <div>
                    <h2
                        class="mt-6 text-center text-3xl font-extrabold text-gray-900"
                    >
                        Modifier blog
                    </h2>
                </div>
                <form class="mt-8 space-y-6" @submit.prevent="update">
                    <div class="rounded-md shadow-sm -space-y-px">
                        <div>
                            <label for="title" class="sr-only">Title</label>
                            <input
                                id="title"
                                name="title"
                                type="text"
                                v-model="form.title"
                                required
                                class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
                                placeholder="Title"
                            />
                            <InputError
                                :message="form.errors.title"
                                class="my-2"
                            />
                        </div>

                        <div class="mt-4">
                            <label for="content" class="sr-only">Content</label>
                            <QuillEditor
                                v-model:content="form.content"
                                contentType="html"
                                theme="snow"
                                toolbar="full"
                                style="height: 200px"
                                placeholder="Écrivez le contenu ici..."
                                aria-required="false"
                            />
                            <InputError
                                :message="form.errors.content"
                                class="mt-2"
                            />
                        </div>
                    </div>

                    <div class="mt-4 flex flex-col">
                        <label for="image_path" class="sr-only">Image</label>
                        <input
                            type="file"
                            @change="uploadImage"
                            class="cursor-pointer"
                        />
                        <InputError
                            :message="form.errors.image_path"
                            class="mt-2"
                        />
                    </div>

                    <div>
                        <ButtonPrimary type="submit" class="w-full">
                            Modifier blog
                        </ButtonPrimary>
                    </div>
                </form>
            </div>
        </div>
    </AuthenticatedLayout>
</template>

<script setup>
import { QuillEditor } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'
import ButtonPrimary from '@/Components/ButtonPrimary.vue'
import { useForm } from '@inertiajs/vue3'
import InputError from '@/Components/InputError.vue'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue'

const props = defineProps(['blog'])
const form = useForm({
    title: props.blog.title,
    content: props.blog.content,
    image_path: props.blog.image_path,
})

const uploadImage = (event) => {
    form.image_path = event.target.files[0]
}

const update = () => {
    form.put(route('blog.update', { blog: props.blog.slug }), {
        onSuccess: () => form.reset(),
    })
}
</script>
0 likes
7 replies
LaryAI's avatar
Level 58

The issue you're facing is related to the validation errors for the 'title' and 'content' fields when uploading a new image during the blog update process. This is happening because the validation rules are not properly set up to handle the image upload.

To fix this issue, you need to update the validation rules in your update method in the BlogController as follows:

$validated = $request->validate([
    'title' => 'required|string|min:3',
    'content' => 'required|min:3',
    'image_path' => 'nullable|image|max:2048',
]);

Here, we have added the image rule to the image_path field to ensure that only image files are allowed to be uploaded.

Additionally, you need to update the update method in your Vue component to properly handle the image upload. Modify the uploadImage method as follows:

const uploadImage = (event) => {
    form.image_path = event.target.files[0]
}

With these changes, the validation should work correctly when uploading an image during the blog update process.

sididev's avatar

@jlrdw I can create posts (CRUD) now if I need to update a specific post I need to change the title or content or image for example.

I have no problem if I change the title and content. The post is updated without problem. But if I try to change the image it tells me that the fields are required (title and content) as if they were empty. In reality the entries are filled in and it tells me The title field is required. & The content field is required.

jlrdw's avatar

@d3v I suggest only allow editing the image if it needs to change. I suggest making it a separate operation. Because you will probably want to have code that deletes the old image also.

sididev's avatar

@amirf Yeah i do... it's was that form.put, in inertiajs i have to use post with _method: 'put' + data sure for update with file

import { router } from '@inertiajs/vue3'

router.post(`/users/${user.id}`, {
  _method: 'put',
  avatar: form.avatar,
})

see here https://inertiajs.com/file-uploads let's me know if u need more explication.

sididev's avatar
sididev
OP
Best Answer
Level 8

@amirf

here is my solution

const props = defineProps(['blog'])
const form = useForm({
    title: props.blog.title,
    content: props.blog.content,
    image_path: null,
})

const handleFiles = (selectedFiles) => {
    if (selectedFiles.length > 0) {
        form.image_path = selectedFiles[0]
    } else {
        form.image_path = null
    }
}

// what u realy need i think...
const update = () => {
    router.post(route('blog.update', { blog: props.blog.slug }), {
        _method: 'put',
        ...form,
    })
}

Please or to participate in this conversation.