Daniel T.
Daniel T.

Reputation: 37

How to upload image with ajax in laravel

I am trying to upload an image using ajax in Laravel, but when I do, I receive this message Call to a member function getClientOriginalExtension() on null. I don't know if there is something wrong with my code. Help!

ProductController

This is where I try to send the image.

public function store(Request $request)
    {
        $validator = Validator::make($request->input(), array(
            'name' => 'required',
            'category_id' => 'required',
            'description' => 'required',
            'price_neto' => 'required',
            'iva' => 'required',
            'price_total' => 'required',
            'image' => 'required|image',
        ));

        $productImage = $request->file('image');
        $productImageName = time() . $productImage->getClientOriginalExtension();
        $productImage->move(public_path("img/products"), $productImageName);

        if ($validator->fails()) {
            return response()->json([
                'error'    => true,
                'messages' => $validator->errors(),
            ], 422);
        }

        $products = Product::create($request->all());

        return response()->json([
            'error' => false,
            'products'  => $products,
        ], 200);
    }

Product.js

This is my Product.js file. It works correctly but now I need to add the image to the product.

$(document).ready(function() {
$("#btn-add").click(function() {
        $.ajaxSetup({
            headers: {
                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
            }
        });

        $.ajax({
            type: 'POST',
            url: '/product',
            data: {
                name: $("#frmAddProduct input[name=name]").val(),
                category_id: $("#frmAddProduct select[name=category_id]").val(),
                description: $("#frmAddProduct input[name=description]").val(),
                price_neto: $("#frmAddProduct input[name=price_neto]").val(),
                iva: $("#frmAddProduct input[name=iva]").val(),
                price_total: $("#frmAddProduct input[name=price_total]").val(),
                image: $("#frmAddProduct input[name=image]").val(),
            },
            dataType: 'json',
            success: function(data) {
                $('#frmAddProduct').trigger("reset");
                $("#frmAddProduct .close").click();
                window.location.reload();

            },
            error: function(data) {
                var errors = $.parseJSON(data.responseText);
                $('#add-product-errors').html('');
                $.each(errors.messages, function(key, value) {
                    $('#add-product-errors').append('<li>' + value + '</li>');
                });
                $("#add-error-bag").show();
            }
        });
    });
});

function addProductForm() {
    $(document).ready(function() {
        $("#add-error-bag").hide();
        $('#addProductModal').modal('show');
    });
}

Product.blade.php

<div class="modal fade" id="addProductModal">
    <div class="modal-dialog">
        <div class="modal-content">
            <form id="frmAddProduct">
                <div class="modal-header">
                    <h5 class="modal-title">
                        <span class="fw-mediumbold">New</span> 
                        <span class="fw-light"> Product</span>
                    </h5>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>

                <div class="modal-body">
                    <div class="alert alert-danger" id="add-error-bag">
                        <ul id="add-product-errors"></ul>
                    </div>
                    <div class="row">

                        {{-- another code --}}

                        <div class="col-md-6">
                            <div class="form-group">
                                <label>Imagen</label>
                                <input class="form-control-file" id="image" name="image" required="" type="file">
                            </div>
                        </div>

                    </div>
                </div>
                    <div class="modal-footer">
                        <button type="button" id="btn-add" class="btn btn-primary">Add</button>
                        <button type="button" class="btn btn-danger" data-dismiss="modal">Close</button>
                    </div>
            </form>         
        </div>
    </div>
</div>

Upvotes: 0

Views: 417

Answers (3)

Daniel T.
Daniel T.

Reputation: 37

This is my code after some corrections.

Product Controller

public function store(Request $request)
    {
        $validator = Validator::make($request->input(), array(
            'name' => 'required',
            'category_id' => 'required',
            'description' => 'required',
            'price_neto' => 'required',
            'iva' => 'required',
            'price_total' => 'required',
            'image' => '',
        ));

        $productImage = $request->file('image');
        $productImageName = rand() . '.' . $productImage->getClientOriginalExtension();
        $productImage->move(public_path('img/products'), $productImageName);

        if ($validator->fails()) {
            return response()->json([
                'error'    => true,
                'messages' => $validator->errors(),
            ], 422);
        }

        $products = Product::create([
            'name' => $request->name,
            'category_id' => $request->category_id,
            'description' => $request->description,
            'price_neto' => $request->price_neto,
            'iva' => $request->iva,
            'price_total' => $request->price_total,
            'image' => $productImageName,
        ]);

        return response()->json([
            'error' => false,
            'products'  => $products,
        ], 200);
    }

Product.js

$("#btn-add").click(function() {

        var formData = new FormData($("#frmAddProduct")[0]);

        $.ajaxSetup({
            headers: {
                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
            }
        });

        $.ajax({
            type: 'POST',
            url: '/product',
            data: formData,
            processData: false,
            contentType: false,
            dataType: 'json',
            success: function(data) {
                $('#frmAddProduct').trigger("reset");
                $("#frmAddProduct .close").click();
                window.location.reload();

            },
            error: function(data) {
                var errors = $.parseJSON(data.responseText);
                $('#add-product-errors').html('');
                $.each(errors.messages, function(key, value) {
                    $('#add-product-errors').append('<li>' + value + '</li>');
                });
                $("#add-error-bag").show();
            }
        });
    });

And in my form, I just added enctype= "multipart/form-data". Thanks!

Upvotes: 0

Jordan Lipana
Jordan Lipana

Reputation: 447

You need to use FormData:

$("#btn-add").click(function(){
    var formData = new FormData($("#frmAddProduct")[0]);
    $.ajaxSetup({
        headers: {
            'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        }
    });
    $.ajax({
        type: 'POST',
        url: '/product',
        data: formData,
        dataType: 'json',
        success: function(data) {
            $('#frmAddProduct').trigger("reset");
            $("#frmAddProduct .close").click();
            window.location.reload();
        },
        error: function(data) {
            var errors = $.parseJSON(data.responseText);
            $('#add-product-errors').html('');
            $.each(errors.messages, function(key, value) {
                $('#add-product-errors').append('<li>' + value + '</li>');
            });
            $("#add-error-bag").show();
        }
    });
});

Upvotes: 0

Ahmad Karimi
Ahmad Karimi

Reputation: 1383

You can't pass an image through ajax by getting the element's value. Also, it's convenient and preferred to use FormData object to send files to the server via ajax.

So, try this instead:

// Select the image holding element.
var productImage = document.querySelector('#frmAddProduct input[name=image]');

// Creating an instance of FormData to submit the form.
var formData = new FormData();
formData.append('name', $("#frmAddProduct input[name=name]").val());
formData.append('category_id', $("#frmAddProduct select[name=category_id]").val());
formData.append('description', $("#frmAddProduct input[name=description]").val());
formData.append('price_neto', $("#frmAddProduct input[name=price_neto]").val());
formData.append('iva', $("#frmAddProduct input[name=iva]").val());
formData.append('price_total', $("#frmAddProduct input[name=price_total]").val());
formData.append('image', productImage.files[0]);

$.ajax({
    type: 'POST',
    url: '/product',
    data: formData,
    processData: false,
    contentType: false,
    dataType: 'json',
    success: function(data) {
        $('#frmAddProduct').trigger("reset");
        $("#frmAddProduct .close").click();
        window.location.reload();

    },
    error: function(data) {
        var errors = $.parseJSON(data.responseText);
        $('#add-product-errors').html('');
        $.each(errors.messages, function(key, value) {
            $('#add-product-errors').append('<li>' + value + '</li>');
        });
        $("#add-error-bag").show();
    }
});

Upvotes: 1

Related Questions