I'm using Laravel on the backend and React.js on the frontend. When I use Laravel validation for images, the image fails to upload. However, if I don't use validation for images, it works correctly. I wonder why. Can you please help me?
Custom Hooks
import { useEffect } from "react";
import { useNavigate, useParams } from "react-router";
import { useDispatch, useSelector } from "react-redux";
import { useForm } from "react-hook-form";
import { toast } from "@/components/ui/use-toast.js";
import { formatISO } from "date-fns";
import { siswaDetail, siswaUpdate } from "@/features/siswaSlice.js";
function UseSiswaEdit() {
const { id } = useParams();
const navigate = useNavigate();
const dispatch = useDispatch();
const { formError, formSuccess, formLoading, detailSiswa } = useSelector(
(state) => state.siswa,
);
const form = useForm({
defaultValues: {
nama: "",
nisn: "",
agama: "",
no_telpon: "",
jenis_kelamin: "",
tgl_lahir: "",
tempat_lahir: "",
foto: "",
alamat: "",
},
});
const storage = JSON.parse(sessionStorage.getItem("siswaDetail"));
useEffect(() => {
dispatch(siswaDetail(id));
if (storage) {
Object.keys(storage).forEach((key) => {
form.setValue(key, storage[key]);
});
}
if (formSuccess) {
toast({
title: "Success",
description: "Data siswa berhasil diubah!",
});
setTimeout(() => {
navigate("/siswa");
}, 1000);
}
}, [formSuccess, dispatch, form]);
function onSubmit(values) {
if (values.tgl_lahir) {
values.tgl_lahir = formatISO(values.tgl_lahir, {
representation: "date",
});
}
const formData = new FormData();
Object.keys(values).forEach((key) => {
formData.append(key, values[key]);
});
dispatch(siswaUpdate({ id, formData }));
}
return {
id,
formLoading,
formSuccess,
formError,
detailSiswa,
onSubmit,
form,
};
}
export default UseSiswaEdit;
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card.jsx";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@/components/ui/select.jsx";
import { Input } from "@/components/ui/input.jsx";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover.jsx";
import { Button } from "@/components/ui/button.jsx";
import { cn } from "@/lib/utils.js";
import { CalendarIcon } from "@radix-ui/react-icons";
import { format } from "date-fns";
import { Calendar } from "@/components/ui/calendar.jsx";
import { Textarea } from "@/components/ui/textarea.jsx";
import { Link } from "react-router-dom";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
} from "@/components/ui/form.jsx";
import { asset } from "@/service/apiCall.js";
import { Label } from "@/components/ui/label.jsx";
import useSiswaEdit from "@/hooks/siswa/useSiswaEdit.jsx";
function SiswaEdit() {
const { formLoading, formError, detailSiswa, onSubmit, form } =
useSiswaEdit();
return (
<Card>
<CardHeader>
<CardTitle>Ubah Data Siswa</CardTitle>
<CardDescription>
Mohon isi formulir ini dengan baik dan benar
</CardDescription>
</CardHeader>
<CardContent>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="nama"
render={({ field }) => (
<FormItem className="grid sm:grid-cols-12 gap-2 sm:gap-6 items-center mb-2">
<div className="sm:col-span-3">
<FormLabel htmlFor="name">
Nama
<span className="text-red-500 font-bold">*</span>
</FormLabel>
</div>
<FormControl>
<div className="sm:col-span-9">
<Input
type="text"
id="name"
placeholder="John Doe"
{...field}
/>
{formError?.name ? (
<p className="mt-2 ml-1 text-xs text-red-500">
{formError.name[0]}
</p>
) : (
""
)}
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="nisn"
render={({ field }) => (
<FormItem className="grid sm:grid-cols-12 gap-2 sm:gap-6 items-center mb-2">
<div className="sm:col-span-3">
<FormLabel htmlFor="nisn">
NISN <span className="text-red-500 font-bold">*</span>
</FormLabel>
</div>
<FormControl>
<div className="sm:col-span-9">
<Input
type="number"
id="nisn"
placeholder="123xxx"
{...field}
/>
{formError?.nisn ? (
<p className="mt-2 ml-1 text-xs text-red-500">
{formError.nisn[0]}
</p>
) : (
""
)}
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="agama"
render={({ field }) => (
<FormItem className="grid sm:grid-cols-12 gap-2 sm:gap-6 items-center mb-2">
<div className="sm:col-span-3">
<FormLabel htmlFor="agama">
Agama <span className="text-red-500 font-bold">*</span>
</FormLabel>
</div>
<FormControl>
<div className="sm:col-span-9">
<Input
type="text"
id="agama"
placeholder="Islam"
{...field}
/>
{formError?.agama ? (
<p className="mt-2 ml-1 text-xs text-red-500">
{formError.agama[0]}
</p>
) : (
""
)}
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="no_telpon"
render={({ field }) => (
<FormItem className="grid sm:grid-cols-12 gap-2 sm:gap-6 items-center mb-2">
<div className="sm:col-span-3">
<FormLabel htmlFor="no-telpon">
No. Telepon
<span className="text-red-500 font-bold">*</span>
</FormLabel>
</div>
<FormControl>
<div className="sm:col-span-9">
<Input
type="number"
id="no-telpon"
placeholder="0823xxx"
{...field}
/>
{formError?.no_telpon ? (
<p className="mt-2 ml-1 text-xs text-red-500">
{formError.no_telpon[0]}
</p>
) : (
""
)}
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="jenis_kelamin"
render={({ field }) => (
<FormItem className="grid sm:grid-cols-12 gap-2 sm:gap-6 items-center mb-2">
<div className="sm:col-span-3">
<FormLabel htmlFor="jenis-kelamin">
Jenis Kelamin
<span className="text-red-500 font-bold">*</span>
</FormLabel>
</div>
<FormControl>
<div className="sm:col-span-9">
<Select
id="jenis-kelamin"
defaultValue={field.value}
onValueChange={field.onChange}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Pilih Jenis Kelamin" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Jenis Kelamin</SelectLabel>
<SelectItem value="laki-laki">Laki-Laki</SelectItem>
<SelectItem value="perempuan">Perempuan</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
{formError?.jenis_kelamin ? (
<p className="mt-2 ml-1 text-xs text-red-500">
{formError.jenis_kelamin[0]}
</p>
) : (
""
)}
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="tgl_lahir"
render={({ field }) => (
<FormItem className="grid sm:grid-cols-12 gap-2 sm:gap-6 items-center mb-2">
<div className="sm:col-span-3">
<FormLabel htmlFor="tanggal-lahir">
Tanggal Lahir
<span className="text-red-500 font-bold">*</span>
</FormLabel>
</div>
<FormControl>
<div className="sm:col-span-9">
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[868px] justify-start text-left font-normal",
!field.value && "text-muted-foreground",
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value ? (
format(field.value, "PPP")
) : (
<span>Pick a date</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
initialFocus
/>
</PopoverContent>
</Popover>
{formError?.tgl_lahir ? (
<p className="mt-2 ml-1 text-xs text-red-500">
{formError.tgl_lahir[0]}
</p>
) : (
""
)}
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="tempat_lahir"
render={({ field }) => (
<FormItem className="grid sm:grid-cols-12 gap-2 sm:gap-6 items-center mb-2">
<div className="sm:col-span-3">
<FormLabel htmlFor="tempat-lahir">
Tempat Lahir
<span className="text-red-500 font-bold">*</span>
</FormLabel>
</div>
<FormControl>
<div className="sm:col-span-9">
<Input
type="text"
id="tempat-lahir"
placeholder="Pekanbaru, Indonesia"
{...field}
/>
{formError?.tempat_lahir ? (
<p className="mt-2 ml-1 text-xs text-red-500">
{formError.tempat_lahir[0]}
</p>
) : (
""
)}
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="alamat"
render={({ field }) => (
<FormItem className="grid sm:grid-cols-12 gap-2 sm:gap-6 items-center mb-2">
<div className="sm:col-span-3">
<FormLabel htmlFor="alamat">
Alamat
<span className="text-red-500 font-bold">*</span>
</FormLabel>
</div>
<FormControl>
<div className="sm:col-span-9">
<Textarea
type="text"
id="alamat"
placeholder="Jl. Sudirman No.xx"
{...field}
/>
{formError?.alamat ? (
<p className="mt-2 ml-1 text-xs text-red-500">
{formError.alamat[0]}
</p>
) : (
""
)}
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="foto"
render={({ field }) => (
<FormItem className="grid sm:grid-cols-12 gap-2 sm:gap-6 items-center mb-2">
<div className="sm:col-span-3">
<FormLabel htmlFor="foto">
Foto
<span className="text-red-500 font-bold">*</span>
</FormLabel>
</div>
<FormControl>
<div className="sm:col-span-9">
<Input
id="foto"
type="file"
accept=".jpg,.png,.jpeg"
onChange={(e) =>
field.onChange(
e.target.files ? e.target.files[0] : null,
)
}
/>
{formError?.foto ? (
<p className="mt-2 ml-1 text-xs text-red-500">
{formError.foto[0]}
</p>
) : (
""
)}
</div>
</FormControl>
</FormItem>
)}
/>
<div className="grid sm:grid-cols-12 gap-2 sm:gap-6 items-center mb-2">
<div className="col-span-3">
<Label htmlFor="name">Foto Sebelumnya</Label>
</div>
<div className="sm:col-span-9">
<div className="flex items-center gap-5">
<img
src={asset(detailSiswa?.foto)}
alt=""
className="w-[230px] h-[200px] rounded-full"
/>
</div>
</div>
</div>
<div className="mt-7 flex justify-end gap-x-2">
<Link to="/siswa">
<Button variant="outline">Kembali</Button>
</Link>
<Button type="submit" disabled={formLoading}>
{formLoading ? "Loading" : "Submit"}
</Button>
</div>
</form>
</Form>
</CardContent>
</Card>
);
}
export default SiswaEdit;
Validation
function update(): array
{
return [
'nama' => 'required|string|min:2',
'nisn' => 'required|string|min:2',
'jenis_kelamin' => 'required|in:laki-laki,perempuan',
'agama' => 'required|string|min:2',
'alamat' => 'required|string|min:2',
'no_telpon' => 'required|string|max:13',
'foto' => 'mimes:jpg,jpeg,png',
'tempat_lahir' => 'required|string|min:2',
'tgl_lahir' => 'required|date',
];
}
Controller Laravel
public function update(SiswaRequest $request, Siswa $siswa): JsonResponse
{
$fotosiswa = null;
if ($request->hasFile('foto')) {
Storage::disk('public')->delete($siswa->foto);
$fotosiswa = $request->file('foto')->store('foto/siswa', 'public');
}
$siswa->update([
'name' => $request->name,
'nisn' => $request->nisn,
'jenis_kelamin' => $request->jenis_kelamin,
'agama' => $request->agama,
'alamat' => $request->alamat,
'no_telpon' => $request->no_telpon,
'foto' => $fotosiswa ? $fotosiswa : $siswa->foto,
'tempat_lahir' => $request->tempat_lahir,
'tgl_lahir' => $request->tgl_lahir
]);
if ($request->filled('roles')) {
$siswa->syncRoles([$request->input('roles')]);
}
return response()->json($siswa);
}
The error after submit :
Validation Message : Failed to Upload
I tried to comment on validate image and it works properly
I already know what's happening. I tried to use the PATCH method for updating the data, but when I try using the POST method, it works fine.