BenjaminD
BenjaminD

Reputation: 29

How to validate FormData object within Laravel 7 using AJAX post?

Been trying different ways to get a FormData object validated within Laravel. Here is my javascript:

$("#submit-note").click(function(e) {
    e.preventDefault();
    var lead_id = $('input[name="lead_id"]').val();
    var note = $("#note").val();
    var file = $('#file-upload');

    let formData = new FormData($('#edit-form')[0]);

    //JSON.stringify(Array.from(formData));

    console.log(...formData);

    $.ajaxSetup({
        headers: {
            "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content")
        }
    });
    $.ajax({
        url: "/leadme/create-note",
        method: "POST",
        cache:false,
        dataType: false,
        processData: false,
        data: formData,
        success: function(response) {
            console.log('Formdata sent');
            console.log(response);
            $(".toast").toast("show");
        },
        error: function (jqXHR, textStatus, errorThrown) {
            console.log(jqXHR, textStatus, errorThrown);
        }
    });
});

This then gets received by the following Laravel controller:

public function createNote(Request $request)
    {
        $validated = $request->validate([
            '*.lead_id.*' => 'numeric|required',
            '*.note.*' => 'string|required',
            //'files.*' => 'mimes:jpeg, jpg, png, bmp, doc, pdf, mp3, svg, gif, webp|nullable|max:250000',
        ]);

        dd($validated);

        $lead = Lead::where('id', $request->lead_id)->first();

        $validated['date'] = date('Y-m-d');
        $validated['time'] = date('G:i');
        $validated['action'] = 'action';
        $validated['lead_status_option_id'] = 1;
        $validated['user_id'] = auth()->user()->id;

        if ($request->hasFile('files') && !empty($validated['files']->name)) {
            foreach ($request->files as $file) {
                //Get filename with the extension
                $fileNameWithExt = $file->getClientOriginalName();
                //Filename to store example: lead_4_document.pdf
                $fileNameToStore =  'lead_' . $lead->id . '_' . $fileNameWithExt;
                //Upload image
                $path = $file->storeAs('/storage/user_uploads', $fileNameToStore);

                dd($lead->uploadFile->update(array('filename' => $fileNameToStore, 'file_url' => $path)));
            }
        }
        Note::create($validated);
        //$lead->note->update(array('note' => $request->note));
        //$lead->update($validated);
    }

Essentially this controller has to save the submitted note and potentially a number of files attached. Though before I can move onto fixing the images I have to get the notes section right. Also, if you are wondering why I am doing this the way I am, this is what has been recommended to me, apparently it makes the multi-file upload process easier. So far, no success. Any help is appreciated, thank you!

As per @Bazaim's request:

{"------WebKitFormBoundarybD9HBrf3Pp8mqc7o
↵Content-Disposition:_form-data;_name": ""_token"
↵
↵77fGMTkhrymzykdxDahWuriG5mItSJn1EX4j53…m
↵
↵
↵------WebKitFormBoundarybD9HBrf3Pp8mqc7o--"}
"------WebKitFormBoundarybD9HBrf3Pp8mqc7o ↵Content-Disposition:_form-data;_name": ""_token"
↵
↵77fGMTkhrymzykdxDahWuriG5mItSJn1EX4j53kY
↵------WebKitFormBoundarybD9HBrf3Pp8mqc7o
↵Content-Disposition: form-data; name="lead_id"
↵
↵13
↵------WebKitFormBoundarybD9HBrf3Pp8mqc7o
↵Content-Disposition: form-data; name="toggle_option-13"
↵
↵0
↵------WebKitFormBoundarybD9HBrf3Pp8mqc7o
↵Content-Disposition: form-data; name="toggle_option-13-13"
↵
↵2
↵------WebKitFormBoundarybD9HBrf3Pp8mqc7o
↵Content-Disposition: form-data; name="note"
↵
↵dgdgdg
↵------WebKitFormBoundarybD9HBrf3Pp8mqc7o
↵Content-Disposition: form-data; name="file[]"; filename=""
↵Content-Type: application/octet-stream
↵
↵
↵------WebKitFormBoundarybD9HBrf3Pp8mqc7o--"
__proto__:
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()

And here is the extended version of the the hidden text: ""_token"

77fGMTkhrymzykdxDahWuriG5mItSJn1EX4j53kY
------WebKitFormBoundarybD9HBrf3Pp8mqc7o
Content-Disposition: form-data; name="lead_id"

13
------WebKitFormBoundarybD9HBrf3Pp8mqc7o
Content-Disposition: form-data; name="toggle_option-13"

0
------WebKitFormBoundarybD9HBrf3Pp8mqc7o
Content-Disposition: form-data; name="toggle_option-13-13"

2
------WebKitFormBoundarybD9HBrf3Pp8mqc7o
Content-Disposition: form-data; name="note"

dgdgdg
------WebKitFormBoundarybD9HBrf3Pp8mqc7o
Content-Disposition: form-data; name="file[]"; filename=""
Content-Type: application/octet-stream


------WebKitFormBoundarybD9HBrf3Pp8mqc7o--"

After implementing Bazaim's edit: the AJAX looks like this:

$("#submit-note").click(function(e) {
    e.preventDefault();
    var lead_id = $('input[name="lead_id"]').val();
    var note = $("#note").val();
    var file = $('#file-upload');


    let formData = new FormData($('#edit-form')[0]);

    //JSON.stringify(Array.from(formData));

    console.log(...formData);

    $.ajaxSetup({
        headers: {
            "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content")
        }
    });
    $.ajax({
        url: "/leadme/create-note",
        method: "POST",
        cache:false,
        dataType: false,
        processData: false,
        contentType: false,
        data: formData,
        success: function(response) {
            console.log('Formdata sent');
            console.log(response);
            $(".toast").toast("show");
        },
        error: function (jqXHR, textStatus, errorThrown) {
            console.log(jqXHR, textStatus, errorThrown);
        }
    });
});

And the controller like this:

public function createNote(Request $request)
    {
        //return response()->json($request->all());

        $validated = $request->validate([
            'lead_id' => 'numeric|required',
            'note' => 'string|required',
            //'files.*' => 'mimes:jpeg, jpg, png, bmp, doc, pdf, mp3, svg, gif, webp|nullable|max:250000',
        ]);

        $lead = Lead::where('id', $request->lead_id)->first();

        $validated['date'] = date('Y-m-d');
        $validated['time'] = date('G:i');
        $validated['action'] = 'action';
        $validated['lead_status_option_id'] = 1;
        $validated['user_id'] = auth()->user()->id;

        if ($request->hasFile('files') && !empty($validated['files']->name)) {
            foreach ($request->files as $file) {
                //Get filename with the extension
                $fileNameWithExt = $file->getClientOriginalName();
                //Filename to store example: lead_4_document.pdf
                $fileNameToStore =  'lead_' . $lead->id . '_' . $fileNameWithExt;
                //Upload image
                $path = $file->storeAs('/storage/user_uploads', $fileNameToStore);

                dd($lead->uploadFile->update(array('filename' => $fileNameToStore, 'file_url' => $path)));
            }
        }
        Note::create($validated);
        //$lead->note->update(array('note' => $request->note));
        //$lead->update($validated);
    }

The note now gets through and updates successfully. Just need to implement the files next.

Upvotes: 0

Views: 3690

Answers (1)

Obzi
Obzi

Reputation: 2390

If I understand weel, you have one lead_id.
But your validator required an array of number.

(I've removed *. & .* from the line lead_id.)

Correction :

$validated = $request->validate([
            'lead_id' => 'numeric|required',
            '*.note.*' => 'string|required',
            //'files.*' => 'mimes:jpeg, jpg, png, bmp, doc, pdf, mp3, svg, gif, webp|nullable|max:250000',
        ]);

Can you change your AJAX call for :

(Added contentType: false,)

 $.ajax({
        url: "/leadme/create-note",
        method: "POST",
        cache:false,
        dataType: false,
        processData: false,
        contentType: false,
        data: formData,
        success: function(response) {
            console.log('Formdata sent');
            console.log(response);
            $(".toast").toast("show");
        },
        error: function (jqXHR, textStatus, errorThrown) {
            console.log(jqXHR, textStatus, errorThrown);
        }
    });

Upvotes: 1

Related Questions