laravelvue.jsputinertiajs

Laravel not processing FormData in a PUT request


I'm facing an issue while trying to send a PUT request with FormData in Laravel using Inertia. I need to update a user, but the PUT request seems to be ignored, and Laravel is not processing the FormData correctly. I was sending the PUT request via Inertia.js, but when I checked the request in Laravel, I found that the request body was empty.

Here’s the flow I'm following:

I’m trying to send FormData from the frontend (via Inertia.js). The PUT request is not being processed as expected. I checked the Laravel request body using dd($request->all()), and it came up empty. Here’s what I tried and how the request came up empty:

Code for debugging in the controller (Laravel):

public function update(Request $request, $id)
{
    // Debugging the request body
    dd($request->all()); // This shows an empty array
    
    // Trying to check php://input
    $rawData = file_get_contents("php://input");
    dd($rawData); // This shows data
}

Frontend (Inertia.js with FormData):

javascript

const formData = new FormData();
formData.append('first_name', model.first_name);
formData.append('last_name', model.last_name);
formData.append('email', model.email);
formData.append('is_superuser', model.is_superuser);
formData.append('is_active', model.is_active);
formData.append('password', model.password || ''); // Optional field
formData.append('profile_pic', model.profile_pic);


router.put(route('user.update', { id: userData.id }), formData, {
    preserveScroll: true,
    onError: (errors) => {
        const errorList = Object.entries(errors)
            .map(([, value]) => `${value}`)
            .join('\n');
        alertDialogService.setMessage(errorList).show();
    },
    forceFormData: true, // Ensure FormData is sent
});

Laravel Controller:

php
public function update(Request $request, $id)
{
    dd($request->all()); // Check the incoming request data
    
    $validatedData = $request->validate([
        'first_name' => 'required|max:30',
        'last_name' => 'required|max:30',
        'email' => 'required|email|unique:auth_users,email,' . $id,
        'is_superuser' => 'required|boolean',
        'is_active' => 'required|boolean',
        'password' => ['nullable', 'min:8'],
    ]);

    // Find and update the user data
    $user = User::findOrFail($id);
    $user->update($validatedData);

    return redirect()->route('user.index')->with('success', 'User updated successfully.');
}

What I observed: When I checked the Laravel request, I found that the request body was empty ($request->all() returned an empty array). I tried using file_get_contents("php://input") to read the raw data, it shows the formData.

How can i fix it? is there any extra configurations needed using vue inertia?


Solution

  • The workaround involves changing the request method to POST, while also informing Laravel that this is intended to be a PUT request by adding a _method field to the FormData.

    Here's how you can implement this solution:

    Frontend (Inertia.js with FormData)

    Instead of sending a PUT request directly, modify the FormData and use a POST request:

    const formData = new FormData();
    formData.append('first_name', model.first_name);
    formData.append('last_name', model.last_name);
    formData.append('email', model.email);
    formData.append('is_superuser', model.is_superuser);
    formData.append('is_active', model.is_active);
    formData.append('password', model.password || ''); // Optional field
    formData.append('profile_pic', model.profile_pic);
    
    // Add the _method field to indicate this is a PUT request
    formData.append('_method', 'PUT');
    
    router.post(route('user.update', { id: userData.id }), formData, {
        preserveScroll: true,
        onError: (errors) => {
            const errorList = Object.entries(errors)
                .map(([, value]) => `${value}`)
                .join('\n');
            alertDialogService.setMessage(errorList).show();
        },
        forceFormData: true, // Ensure FormData is sent
    });
    

    No changes are needed in your Laravel controller code for this solution. Laravel will recognize the _method field and correctly interpret the request as a PUT request.

    Explanation

    1. FormData with _method Field: By adding _method: PUT to the FormData, Laravel can interpret the POST request as a PUT request.
    2. Using router.post: You send the request as a POST rather than a PUT. This avoids the issue where Laravel fails to parse the request body when using PUT with multipart/form-data.
    3. Symfony's Limitation: The underlying issue is related to Symfony, which Laravel uses for handling HTTP requests. Symfony doesn't natively parse multipart/form-data correctly for PUT/PATCH requests. This is why using POST with a _method override is a common workaround.

    This solution should resolve the issue of Laravel showing an empty request body and allow you to update the data successfully.