🛠️ Actualización técnica: validación y subida de imágenes + controlador seguro en boilerplateproducts 🖼️

En este cambio abordamos varios problemas prácticos que afectaban la subida de imágenes y la robustez del endpoint admin/products/save. Objetivos principales:

  • ✅ Permitir JPG/JPEG además de PNG (y manejar HEIC de iPhone).
  • ✅ Soportar subida desde móvil (cámara/galería).
  • ✅ Validar tipo y tamaño en cliente y servidor (pasamos de 2 MB a 5 MB).
  • ✅ Enviar la imagen con FormData solo si existe y evitar errores por selectores incorrectos.
  • ✅ Mejorar el controlador en CodeIgniter 4: validaciones, rutas absolutas, manejo seguro de archivos, respuestas JSON y logging. 📦

1 — Problema inicial 🐞

  • El JS validaba solo image/png, por lo que bloqueaba .jpg/.jpeg. ❌
  • Selectores inconsistentes (.imagenProducto vs #imagenProducto) => riesgo de undefined. ⚠️
  • El controlador PHP aceptaba únicamente PNG, hacía var_dump, usaba rutas relativas y agarraba excepciones equivocadas. 🧨
  • Tamaño máximo 2 MB; se solicitó ampliar a 5 MB. 📈
  • No había protocolo JSON consistente entre frontend y backend. 🧩

2 — Cambios en el frontend (JS) 📱

Qué se cambió (resumen)

  • Validación de tipos permitidos: image/png, image/jpeg, image/jpg. ✅
  • Detección y rechazo de HEIC (iPhone) con aviso o rechazo según preferencia. 🚫🧾
  • Límite de tamaño subido: 5 MB (maxSize = 5 * 1024 * 1024). 📏
  • Uso de FormData solamente si el archivo existe. 🎒
  • Unificación de selectores: usar id="imagenProducto" y mantener class para preview. 🧭
  • e.preventDefault() y bloqueo/rehabilitación del botón con complete. 🔒➡️🔓
  • dataType: "json" en AJAX con fallback a texto plano para compatibilidad. 🔁
  • Mensajes Toast claros y amigables. 🔔

Snippet — parte clave del JS (resumido)

// ... (código resumido; validar tipo, tamaño 5MB, añadir FormData solo si existe)
var maxSize = 5 * 1024 * 1024; // 5 MB
if (imagenProducto.size > maxSize) {
    Toast.fire({ icon: 'error', title: "La imagen pesa más de 5 MB." });
    $btn.removeAttr("disabled");
    return;
}

3 — Cambios en el backend (Controlador CodeIgniter 4) ⚙️

Objetivos del backend

  • Aceptar PNG y JPG/JPEG. 🟢
  • Validar tamaño (5 MB). 📦
  • Evitar var_dump en producción. 🚫
  • Usar FCPATH para mover archivos a public/images/products. 📁
  • Crear carpeta si no existe. 🧱
  • Devolver JSON con estado ok|error y message. 💬
  • Borrar la imagen anterior de forma segura al actualizar. 🧹
  • Manejar excepciones generales correctamente. 🔒

Snippet — método save() resumido

// Validación de archivo
$maxSize = 5 * 1024 * 1024;
$allowed = ['png','jpg','jpeg'];
if ($imagenProducto && $imagenProducto->isValid()) {
    $ext = strtolower($imagenProducto->getClientExtension() ?: '');
    if (!in_array($ext, $allowed)) {
        return $this->response->setStatusCode(415)->setJSON(['status'=>'error','message'=>lang('empresas.imageExtensionIncorrect')]);
    }
    if ($imagenProducto->getSize() > $maxSize) {
        return $this->response->setStatusCode(413)->setJSON(['status'=>'error','message'=>lang('empresas.imageTooLarge')]);
    }
    $datos['routeImage'] = $imagenProducto->getRandomName();
}

4 — Configuración del servidor y notas operativas 🖥️

  • PHP (php.ini): aumentar upload_max_filesize y post_max_size por encima de 5 MB (ej. 8M). 🔧
  • Nginx: client_max_body_size 8M;. ⚠️
  • Permisos: public/images/products debe ser escribible por el proceso web (ej. www-data). 🛡️
  • Seguridad: siempre validar MIME real y extensión en el servidor — no confiar en JS. 🔍
  • Backups: si las imágenes son críticas, considera mantener copia antes de unlink. 💾

5 — Pruebas que hicimos y checklist para QA 🧪✅

Pruebas realizadas:

  • Subida desde desktop: JPG, PNG. 💻
  • Subida desde móvil: cámara (Android/Chrome) y galería. 📱
  • Subida desde iPhone (HEIC) → detectado y rechazado con mensaje. 🍏🚫
  • Reemplazo de imagen de producto: antiguo archivo borrado y nuevo movido. 🔁
  • Validación de tamaños: >5MB rechazado, <5MB aceptado. ⚖️
  • Manejo de respuesta JSON en frontend y fallback a texto. 🔄

Checklist para QA / PR:

  • [ ] ✅ El input tiene id="imagenProducto" y accept="image/png, image/jpeg" (o accept="image/*" si se quiere cámara directa).
  • [ ] ✅ Validación en JS y servidor coincide (5 MB).
  • [ ] ✅ images/products existe y es escribible en staging/prod.
  • [ ] ✅ Mensajes Toast claros para cada caso (HEIC, tamaño, formato).
  • [ ] ✅ Al actualizar producto, la imagen anterior se borra sólo si corresponde.
  • [ ] ✅ Tests unitarios / integración pasan (si existen).
  • [ ] ✅ composer.lock no fue tocado por estos cambios (si aplica).

6 — Mensajes de commit / PR recomendados ✍️

Commit:
fix(products): accept jpeg/png, validate size 5MB, handle uploads safely

Descripción PR sugerida:

  • Frontend: validar JPG/JPEG + PNG, rechazar HEIC, aumentar tamaño a 5MB, enviar FormData solo si existe, unificar selectores y mejorar UX (disable button + toast).
  • Backend (CI4): aceptar jpg/png, validar tamaño, usar FCPATH para almacenamiento, crear carpeta si no existe, devolver JSON, borrar imagen anterior de forma segura, mejorar manejo de errores y logging.

Incluye checklist de QA y capturas de la subida desde móvil/desktop.


7 — Próximos pasos recomendados 🚀

  • ✂️ Optimizar imágenes: redimensionar/comprimir en cliente o servidor para ahorrar espacio y tiempo de subida.
  • ♻️ Soportar HEIC: implementar conversión con Imagick o servicio externo si quieres aceptar HEIC automáticamente.
  • 🧪 Automatizar pruebas: agregar tests para multipart upload y para el flujo de reemplazo/borrado de imágenes.
  • 🔁 Pipeline CI/CD: en staging hacer composer update si fuera necesario, probar y en producción usar composer install (lockfile).

8 — Conclusión 🎯

Estos cambios hacen la subida de imágenes más robusta, amigable para móviles y segura en el servidor. El frontend ahora maneja errores con mensajes claros y el backend devuelve JSON estructurado para un manejo predecible en la UI. Además, ampliamos el límite a 5 MB y arreglamos detalles clásicos como rutas relativas y selectores inconsistentes. ✅