CakePHP 4.x Solicitud de Ajax con archivo CSV

Estoy desarrollando una interfaz de usuario sensible en CakePHP 4.x que ocasionalmente utiliza las solicitudes de Ajax. Mis solicitudes de Ajax están funcionando muy bien pero estoy teniendo muchos problemas para incorporar un archivo CSV en la solicitud para que mi controlador pueda manejar los datos. Lo que quiero lograr es que puedo elegir un archivo CSV, presentar la prensa y que la Solicitud Ajax envía el archivo al controlador y utiliza las filas independientes para actualizar la base de datos.

Mi código:

Javscript:

function importProducts() {
    /* Getting form data */
    let form = document.getElementById('importProductsForm');

    let formData = new FormData();

    let file = $(form.products_file).prop('files')[0];

    formData.append("csv_file", file);

    /* Moving product stock */
    ajaxRequest('Products', 'importProducts', formData, processImportProducts);
}

function ajaxRequest(controller, action, data = null, callback = null) {
    $.ajax({
        url : "=$this-Url->build(['controller' => '']);?>" + "/" + controller + "/" + action,
        type : 'POST',
        data : {
            'data': data
        },
        dataType :'json',
        /*processData: false,*/
        /*contentType: false,*/
        success : function(dataArray) {    
            let response = dataArray.response;
            
            if (typeof response.data !== 'undefined') {
                data = response.data;
                
                if (callback != null) {
                    callback(data);
                }
            } else if (response.success == 0) {
                data = null;
                
                giveError(response.errorTemplate);
            } else {
                data = null;
                
                if (callback != null) {
                    callback(data);
                }
            }
        },
        error : function(request,error)
        {
            console.error(error);
        }
    });
}

De momento la función controladora no hace nada especial sino recibir y configurar los datos:

public function importProducts() {
    $this->RequestHandler->renderAs($this, 'json');

    $response = [];

    if($this->request->is('post')) {
        $data = $this->request->getData();

        $response['test'] = $data;
    } else {
        $response['success'] = 0;
    }

    $this->set(compact('response'));
    $this->viewBuilder()->setOption('serialize', true);
    $this->RequestHandler->renderAs($this, 'json');
}

Después de una investigación descubrí que podría usar el objeto FormData para enviar el archivo. El error que recibí fue "invocación ilegal". Después de más investigaciones descubrí que esto tenía que ver con la perspicacia automática de cadenas por Ajax. Según algunos otros posts de StackOverflow podría resolver esto estableciendo las propiedades de processdata y contenttype a false. Esto solucionó el problema, pero resultó en una solicitud de Ajax que siempre estaría vacía (que no contiene ningún dato). Lo probé sin el archivo CSV con un objeto de datos regular que contiene una variable con una cadena, pero también resultó en una solicitud vacía (no se envían datos al controlador).

Así que mi problema es que sin la propiedad processdata como falsa obtengo el error de "invocación ilegal", de lo contrario con processdata como falso literario no recibo ningún dato en mi controlador. Estoy buscando solución para resolver este problema así que puedo enviar mi archivo CSV o al menos los datos dentro del archivo a mi controlador.

Otras soluciones que el uso de FormData también son bienvenidas, por ejemplo traté de leer el archivo CSV en Javascript y convertirlo en otro objeto (con el jquery csv api) para enviar al controlador, tristemente sin éxito hasta ahora.

Pregunta hecha hace 3 años, 4 meses, 26 días - Por pixelpioneerbbb5


2 Respuestas:

  • Para enviar un archivo CSV a través de una solicitud AJAX en CakePHP 4.x, necesitas realizar algunos ajustes en tu código. Aquí hay algunos pasos que puedes seguir:

    • Asegúrate de que estás capturando el archivo correctamente en el JavaScript y lo estás adjuntando a FormData de la manera correcta. En tu función importProducts(), asegúrate de que estás seleccionando el archivo de la forma adecuada:
    javascript
    let file = form.products_file.files[0];
    
    • Antes de enviar la solicitud AJAX, asegúrate de establecer processData y contentType a false en la configuración de la solicitud. Esto evita que jQuery intente procesar los datos de forma predeterminada y te permite enviar archivos correctamente:
    javascript
    $.ajax({
        ...
        processData: false,
        contentType: false,
        ...
    });
    
    • En tu controlador de CakePHP, asegúrate de acceder al archivo CSV correctamente desde la solicitud. Puedes hacerlo de la siguiente manera:
    php
    public function importProducts() {
        $this->RequestHandler->renderAs($this, 'json');
    
        $response = [];
    
        if ($this->request->is('post')) {
            $file = $this->request->getData('csv_file');
    
            // Process the CSV file here
    
            $response['success'] = 1;
        } else {
            $response['success'] = 0;
        }
    
        $this->set(compact('response'));
        $this->viewBuilder()->setOption('serialize', true);
    }
    
    • Una vez que hayas procesado el archivo CSV en el controlador, puedes realizar las operaciones necesarias en la base de datos.

    Con estos ajustes, deberías poder enviar correctamente el archivo CSV a través de la solicitud AJAX y procesarlo en tu controlador de CakePHP. Recuerda ajustar el procesamiento del archivo CSV según tus necesidades específicas en tu aplicación. ¡Espero que esta información te sea de ayuda! Si necesitas más ayuda, no dudes en preguntar.

    Respondida el Dec 20, 2020 a las 22:44 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • Solución usando FormData:

    Para resolver el problema usando FormData, debes establecer las opciones processData y contentType a false en la configuración de la solicitud Ajax. Esto evitará que jQuery procese y serialice los datos automáticamente, lo que permitirá enviar el archivo CSV como datos binarios.

    Así es como puedes hacerlo:

    
    function importProducts() {
      // Obtener los datos del formulario
      let form = document.getElementById('importProductsForm');
      let formData = new FormData(form);
    
      // Incluir el archivo CSV en los datos
      let file = $(form.products_file).prop('files')[0];
      formData.append("csv_file", file);
    
      // Enviar la solicitud Ajax
      $.ajax({
        url: "=$this-Url->build(['controller' => '']);?>" + "/" + controller + "/" + action,
        type: 'POST',
        data: formData,
        dataType: 'json',
        processData: false,
        contentType: false,
        success: function(dataArray) {
          // Procesar la respuesta
          // ...
        },
        error: function(request, error) {
          // Manejar el error
          // ...
        }
      });
    }
    

    Solución alternativa usando CSV API:

    Si tienes problemas para usar FormData, puedes intentar utilizar la biblioteca CSV API de jQuery para leer el archivo CSV y convertirlo en un objeto JSON que puedes enviar al controlador.

    Así es como puedes hacerlo:

    javascript
    function importProducts() {
      // Obtener el archivo CSV
      let csvFile = $(form.products_file).prop('files')[0];
    
      // Leer el archivo CSV y convertirlo a JSON
      Papa.parse(csvFile, {
        header: true,
        complete: function(results) {
          // Enviar la solicitud Ajax con los datos JSON
          $.ajax({
            url: "=$this-Url->build(['controller' => '']);?>" + "/" + controller + "/" + action,
            type: 'POST',
            data: JSON.stringify(results.data),
            dataType: 'json',
            success: function(dataArray) {
              // Procesar la respuesta
              // ...
            },
            error: function(request, error) {
              // Manejar el error
              // ...
            }
          });
        }
      });
    }
    

    Espero que esto ayude a resolver tu problema.

    Respondida el Dec 20, 2020 a las 22:54 - por Gemini

    Votos positivos: 0 | Votos negativos: 0