0

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>
Daniel T.
  • 37
  • 1
  • 10
  • Put the condicional `if ($validator->fails())` before tying to get the file extension to avoid the exeption if there is not file – porloscerros Ψ Oct 23 '19 at 01:34
  • Then add your inputs to a FormData object and send the formdata https://stackoverflow.com/a/21045034/7498116 – porloscerros Ψ Oct 23 '19 at 01:53
  • Try changing from `image: $("#frmAddProduct input[name=image]").val()` to `image: $("#frmAddProduct input[name=image]")[0].files[0]` in your `data` object – KmoSkillz Oct 23 '19 at 04:27

3 Answers3

1

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();
    }
});
Ahmad Karimi
  • 1,285
  • 2
  • 15
  • 26
0

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();
        }
    });
});
Jordan Lipana
  • 437
  • 2
  • 4
  • 18
0

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!

Daniel T.
  • 37
  • 1
  • 10