Estoy intentando cargar una imagen usando ajax en Laravel, pero cuando lo hago, recibo este mensaje Call to a member function getClientOriginalExtension() on null. No sé si hay algo mal con mi código. ¡Ayuda!

ProductController

Aquí es donde trato de enviar la imagen.

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

Este es mi archivo Product.js. Funciona correctamente pero ahora necesito agregar la imagen al producto.

$(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>
0
Daniel T. 23 oct. 2019 a las 01:24

3 respuestas

La mejor respuesta

No puede pasar una imagen a través de ajax obteniendo el valor del elemento. Además, es conveniente y preferible usar el objeto FormData para enviar archivos al servidor a través de ajax.

Entonces, intente esto en su lugar:

// 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();
    }
});
1
Ahmad Karimi 23 oct. 2019 a las 07:20

Necesita usar 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();
        }
    });
});
0
Jordan Lipana 23 oct. 2019 a las 09:13

Este es mi código después de algunas correcciones.

Controladora de producto

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();
            }
        });
    });

Y en mi forma, acabo de agregar enctype= "multipart/form-data". ¡Gracias!

0
Daniel T. 23 oct. 2019 a las 15:46