My problem is similar to this post the different is I'm using modal to display the update fields. When I doing a PATCH request to my controller, my validation is failed on the required rules. I'm copying on how Laravel Breeze UpdateProfileInformation way to pass an form to new customized request and then proceed validate the rules there, if the validation works then it will dump the data.
But its always said "The xx field is required" although the request is already passed and filled with previous data
Here is the codes :
Update.vue
<template>
<div @click.self="closeModal()" class="backdrop">
<div
class="bg-base-100 justify-center items-center text-center w-[90%] h-fit p-5 m-auto mt-[3%] mb-[2%] overflow-auto rounded-md"
>
<slot class="w-fit h-fit">
<div class="p-2 flex flex-col">
<div
class="font-semibold text-xl text-neutral-content leading-tight mb-5"
>
<h1>Update existing data</h1>
</div>
<form @submit.prevent="update">
<div
class="p-2 flex flex-row card bg-neutral rounded-md"
>
<div class="w-[50%] items-center p-2">
<div class="flex flex-col p-2">
<div>
<div v-if="tempUrl" class="p-5">
<!-- M̶a̶k̶e̶s̶ C̶h̶e̶c̶k̶,̶ i̶f̶ o̶p̶e̶n̶ f̶i̶r̶s̶t̶ t̶i̶m̶e̶,̶ t̶a̶k̶e̶ i̶m̶g̶ u̶r̶l̶ f̶r̶o̶m̶ d̶b̶ a̶n̶d̶ d̶i̶s̶p̶l̶a̶y̶ i̶t̶ -->
<!-- I̶f̶ t̶h̶e̶r̶e̶ i̶s̶ a̶ c̶h̶a̶n̶g̶e̶ i̶n̶ i̶m̶a̶g̶e̶,̶ c̶h̶a̶n̶g̶e̶ p̶r̶e̶v̶i̶e̶w̶ t̶o̶ c̶u̶r̶r̶e̶n̶t̶ s̶e̶l̶e̶c̶t̶e̶d̶ i̶m̶a̶g̶e̶ -->
<h2 class="m-2">Preview Image</h2>
<img
class="m-auto rounded-md border-2 border-primary p-2"
:src="tempUrl"
alt=""
/>
</div>
<div v-else>
<!-- Change to img later -->
<h1>{{ form.image }}</h1>
</div>
</div>
<div class="flex flex-col">
<h1 class="m-2 text-start">Image</h1>
<input
id="image"
@change="setImage"
type="file"
class="file-input file-input-sm rounded-md file-input-bordered file-input-primary w-full max-w-xs"
/>
<InputError
class="mt-2"
:message="form.errors.image"
/>
</div>
</div>
<div class="flex flex-col p-2">
<div>
<h1 class="m-2 text-start">Code</h1>
</div>
<div class="flex flex-row">
<div v-if="radioCode">
<TextInput
id="codeText"
v-model="form.code"
type="text"
placeholder="Type here"
class="input input-bordered input-primary w-full max-w-xs"
required
/>
<InputError
class="mt-2"
:message="form.errors.code"
/>
</div>
<div v-else>
<select
id="codeSelect"
v-model="form.code"
class="select select-md select-primary w-full max-w-xs"
required
>
<option disabled selected>
Existing Code
</option>
<option
v-for="furniture in Furnitures"
>
{{ furniture.code }}
</option>
</select>
<InputError
class="mt-2"
:message="form.errors.code"
/>
<!-- Radio Group -->
</div>
<div class="ml-5 flex flex-col">
<div class="flex flex-row">
<input
type="radio"
name="radioCode"
class="mb-2 radio radio-sm radio-primary bg-inherit enabled:hover:border-info enabled:checked:bg-primary"
value="new"
@change="onChange($event)"
checked
/>
<label for="radio" class="ml-2"
>New data</label
>
</div>
<div class="flex flex-row">
<input
type="radio"
name="radioCode"
class="radio radio-sm radio-primary bg-inherit enabled:hover:border-info enabled:checked:bg-primary"
value="exist"
@change="onChange($event)"
/>
<label for="radio" class="ml-2"
>Existing data</label
>
</div>
</div>
</div>
</div>
<div class="flex flex-col p-2">
<h1 class="m-2 text-start">Description</h1>
<TextInput
id="description"
v-model="form.description"
type="text"
placeholder="Type here"
class="input input-bordered input-primary w-full max-w-xs"
/>
<InputError
class="mt-2"
:message="form.errors.description"
/>
</div>
<div class="flex flex-col p-2">
<h1 class="m-2 text-start">Category</h1>
<select
id="category"
v-model="form.category"
@change="categoryChange"
class="select select-md select-primary w-full max-w-xs"
>
<option v-for="category in categories">
{{ category }}
</option>
</select>
<InputError
class="mt-2"
:message="form.errors.category"
/>
</div>
<div class="flex flex-col p-2">
<h1 class="m-2 text-start">Wood Type</h1>
<select
id="woodtype"
v-model="form.woodtype"
class="select select-md select-primary w-full max-w-xs"
>
<option v-if="isRoot" value="Root">
Root
</option>
<option
v-if="!isRoot"
v-for="notRoot in notRootType"
>
{{ notRoot }}
</option>
</select>
<InputError
class="mt-2"
:message="form.errors.woodtype"
/>
</div>
</div>
<div class="w-[50%] items-center p-2">
<div class="flex flex-col p-2">
<h1 class="m-2 text-start">Width</h1>
<NumberInput
id="width"
v-model="form.width"
type="number"
placeholder="Type here"
class="input input-bordered input-primary w-full max-w-xs"
/>
<InputError
class="mt-2"
:message="form.errors.width"
/>
</div>
<div class="flex flex-col p-2">
<h1 class="m-2 text-start">Depth</h1>
<NumberInput
id="depth"
v-model="form.depth"
type="number"
placeholder="Type here"
class="input input-bordered input-primary w-full max-w-xs"
/>
<InputError
class="mt-2"
:message="form.errors.depth"
/>
</div>
<div class="flex flex-col p-2">
<h1 class="m-2 text-start">Height</h1>
<NumberInput
id="height"
v-model="form.height"
type="number"
placeholder="Type here"
class="input input-bordered input-primary w-full max-w-xs"
/>
<InputError
class="mt-2"
:message="form.errors.height"
/>
</div>
<div class="flex flex-col p-2">
<h1 class="m-2 text-start">Stock</h1>
<NumberInput
id="stock"
v-model="form.stock"
type="number"
placeholder="Type here"
class="input input-bordered input-primary w-full max-w-xs"
/>
<InputError
class="mt-2"
:message="form.errors.stock"
/>
</div>
<div class="flex flex-col p-2">
<h1 class="m-2 text-start">Price</h1>
<NumberInput
id="price"
v-model="form.price"
type="number"
placeholder="Type here"
class="input input-bordered input-primary w-full max-w-xs"
/>
<InputError
class="mt-2"
:message="form.errors.price"
/>
</div>
</div>
</div>
<div>
<button
class="btn m-4 px-10 btn-info"
:disabled="form.processing"
>
<div v-if="form.processing">
<span
class="loading loading-spinner loading-sm text-primary"
>Update</span
>
</div>
<div v-else>
<span>Update</span>
</div>
</button>
</div>
</form>
</div>
</slot>
</div>
</div>
</template>
<script>
import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout.vue";
import PrimaryButton from "@/Components/PrimaryButton.vue";
import TextInput from "@/Components/TextInput.vue";
import NumberInput from "@/Components/NumberInput.vue";
import InputLabel from "@/Components/InputLabel.vue";
import { Link, useForm } from "@inertiajs/vue3";
import { ref } from "vue";
import InputError from "@/Components/InputError.vue";
export default {
props: ["SelectedFurniture", "Furnitures"],
components: {
AuthenticatedLayout,
PrimaryButton,
TextInput,
NumberInput,
InputLabel,
InputError,
},
setup(props) {
const radioCode = ref(true);
const categories = ["Indoor", "Outdoor", "Handicraft", "Root"];
const notRootType = ["Teak wood", "Tiger wood", "Mahogany wood"];
const isRoot = ref(false);
const selectedCategory = ref("");
let tempUrl = ref("");
const form = useForm({
uuid: props.SelectedFurniture.uuid,
image: props.SelectedFurniture.image,
code: props.SelectedFurniture.code,
description: props.SelectedFurniture.description,
category: props.SelectedFurniture.category,
woodtype: props.SelectedFurniture.wood_type,
width: parseInt(props.SelectedFurniture.width),
depth: parseInt(props.SelectedFurniture.width),
height: parseInt(props.SelectedFurniture.height),
stock: parseInt(props.SelectedFurniture.stock),
price: parseFloat(props.SelectedFurniture.price),
});
console.log(props.SelectedFurniture.wood_type);
const update = () => {
// console.log(form);
form.patch(route("input.update"));
};
return {
form,
radioCode,
categories,
notRootType,
isRoot,
selectedCategory,
tempUrl,
update,
};
},
methods: {
closeModal() {
this.$emit("close");
},
setImage(event) {
const file = event.target.files;
this.form.image = file[0];
this.tempUrl = URL.createObjectURL(this.form.image);
// console.log(this.tempUrl);
},
onChange(event) {
this.radioCode = !this.radioCode;
this.form.reset("code");
// console.log(this.radioCode);
// console.log(this.form);
},
categoryChange(event) {
if (event.target.options.selectedIndex > -1) {
this.selectedCategory = event.target.value;
if (this.selectedCategory == "Root") {
this.isRoot = true;
} else {
this.isRoot = false;
}
// console.log(this.isRoot);
// console.log(this.selectedCategory);
}
},
},
};
</script>
<style scoped>
.modal {
width: 400px;
padding: 20px;
margin: 100px auto;
background: white;
border-radius: 10px;
text-align: center;
}
.backdrop {
top: 0;
position: fixed;
background: rgba(0, 0, 0, 0.5);
width: 100%;
height: 100%;
}
</style>
InputController
<?php
namespace App\Http\Controllers;
use Inertia\Inertia;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use App\Http\Requests\InputUpdateRequest;
class InputController extends Controller
{
//
public function index(Request $request){
$user = Auth::user();
// dd($user);
$furniture = DB::table('furniture')
->get();
// dd($furniture);
return Inertia::render('Input/Input', [
'furnitures' => $furniture,
]);
}
public function create(Request $request){
$validationInteger = "required|integer";
$validationString = "required|string";
$request -> validate([
'image' => "required|file",
'description' => $validationString,
'category' => $validationString,
'woodtype' => $validationString,
'width' => $validationInteger,
'height' => $validationInteger,
'depth' => $validationInteger,
'stock' => $validationInteger,
'price' => $validationInteger,
]);
// sleep(30);
dd($request->all());
}
public function update(InputUpdateRequest $request){
dd($request->all());
}
}
InputUpdateRequest
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class InputUpdateRequest extends FormRequest{
public function rules(): array{
$validationInteger = ["required","integer"];
$validationString = ["required","string"];
return [
'uuid' => $validationString,
'image' => ["required","file"],
'description' => $validationString,
'category' => $validationString,
'woodtype' => $validationString,
'width' => $validationInteger,
'height' => $validationInteger,
'depth' => $validationInteger,
'stock' => $validationInteger,
'price' => $validationInteger,
];
}
}
Routers
Route::middleware('auth:admin')->group(function (){
Route::get('/input',[InputController::class, 'index'])->name('input.index');
Route::post('/input',[InputController::class, 'create'])->name('input.create');
Route::patch('/input',[InputController::class, 'update'])->name('input.update');
Route::delete('/input',[InputController::class, 'delete'])->name('input.delete');
});
I tried using sometimes rule, but it will causing endless loading,
When I dump the request normally, it's passing the data succesfully

Edit :
I tried do the validation straight from the function itself still not working, but when I dump it, the requests is passed successfully
public function update(Request $request){
// dd($request->all());
$validationInteger = "required|integer";
$validationString = "required|string";
$request -> validate([
'image' => "required|file",
'description' => $validationString,
'category' => $validationString,
'woodtype' => $validationString,
'width' => $validationInteger,
'height' => $validationInteger,
'depth' => $validationInteger,
'stock' => $validationInteger,
'price' => $validationInteger,
]);
dd($request->all());
}
}