Herramientas Informaticas

Categoría: PHP8 Página 1 de 3

🚀 Corrección completa del control de lotes en inventario: evita duplicados, valida subcategorías y mejora la experiencia de usuario

Entrada fija

Resumen rápido: En este artículo revisamos y documentamos una corrección integral para un problema común en sistemas de inventario: la generación de lotes duplicados al agregar productos rápidamente vía AJAX. Incluye la solución frontend con un contador en memoria, la validación backend de subcategoría, manejo de errores con Toast, ejemplos de código listos para copiar y todo explicado paso a paso. Además se referencia el commit donde se subieron los cambios.


🧭 Introducción — ¿qué problema resolvimos y por qué importa?

En aplicaciones web que manejan inventario, es frecuente tener un flujo donde el usuario busca un producto y presiona Agregar varias veces. Si ese evento dispara una petición AJAX al servidor para obtener el último lote disponible y la respuesta se procesa en paralelo, existe una ventana de inconsistencia: varias respuestas pueden leer el mismo estado inicial y generar el mismo consecutivo, produciendo lotes duplicados (por ejemplo LMPLMLAPTOP000001 repetido varias veces). Esto provoca problemas reales: registros duplicados en inventario, conflictos de seguimiento, problemas legales en trazabilidad, y mala experiencia de usuario.

La meta fue sencilla pero crítica: evitar duplicados y hacer el flujo robusto sin reescribir toda la arquitectura. Priorizamos una solución práctica, compatible con jQuery y CodeIgniter/PHP, que puedas aplicar hoy en tu proyecto.


🔎 Diagnóstico técnico — por qué ocurrieron los duplicados

El error tiene tres factores principales:

  1. Asincronía: las peticiones AJAX regresan en tiempos distintos.
  2. Dependencia del DOM: el código calculaba el siguiente consecutivo consultando elementos del DOM que todavía no reflejaban las inserciones hechas por otras respuestas.
  3. Falta de validación del backend: el backend asumía que existían datos (por ejemplo, la subcategoría), lo que podía llevar a lotes mal formados o a errores silenciosos.

La combinación de esos puntos genera la race condition: varias respuestas ven el mismo último consecutivo y todas calculan el mismo siguiente valor.


🛠️ Solución aplicada — enfoque general

Optamos por una solución híbrida y pragmática:

  • Frontend: mantener un contador en memoria (lotesContador) que almacene, por cada loteBase, el último consecutivo usado en la sesión. Al recibir la respuesta del backend, sincronizamos ese contador con el consecutivo que trae el servidor y, si corresponde, lo incrementamos localmente para el siguiente lote. Esto evita depender del estado del DOM y evita conflictos cuando el usuario hace clicks rápidos.
  • Backend: agregar validaciones tempranas para condiciones críticas (por ejemplo: que el producto tenga subcategoría). Si falta información, el servidor devuelve una respuesta JSON con error: true y un message descriptivo.
  • UX: usar toasts para informar errores (icono de error), y evitar mensajes ambiguos o iconos de éxito cuando ocurrió un fallo.

💡 ¿Por qué esta solución es práctica?

Porque:

  • No requiere reestructurar la base de datos ni añadir infraestructura adicional.
  • Es fácil de integrar en proyectos existentes con jQuery + backend PHP/CodeIgniter.
  • Reduce la ventana de inconsistencias en la interfaz del usuario y evita la mayoría de los duplicados en escenarios de click rápido.
  • Permite seguir usando al backend como fuente de verdad cuando hay cambios externos: si el backend reporta un consecutivo mayor al local, el frontend se sincroniza.

🔧 Código: backend (CodeIgniter) — validar subcategoría y devolver JSON de error

Este fragmento se inserta en el controller que calcula el lote (método calculateLot() o similar). La idea es validar que exista idSubCategoria en la fila del producto y que la subcategoría realmente exista en la tabla de subcategorías. Si falla, devolvemos un JSON con error.

// Validar que tenga subcategoría
if (empty($productData["idSubCategoria"])) {
    return $this->response->setJSON([
        "error" => true,
        "message" => "El producto no tiene subcategoría asignada"
    ]);
}

// Buscar subcategoría
$subCategoryData = $this->subCategory->select("*")
        ->where("id", $productData["idSubCategoria"])
        ->first();

// Validar que exista
if (!$subCategoryData) {
    return $this->response->setJSON([
        "error" => true,
        "message" => "Subcategoría no encontrada"
    ]);
}

$keyCategory = $subCategoryData["descripcion"];
// ... continuar con la construcción de $baseLot, búsqueda del último lote y generación del consecutivo ...

Con esto, si alguien intenta calcular un lote para un producto sin subcategoría, el frontend recibirá un JSON con error y se podrá mostrar un mensaje al usuario en lugar de producirse un lote inválido o un fallo más profundo.


🖥️ Código: frontend (jQuery) — patrón robusto para decidir loteFinal

A continuación está el patrón que implementamos en el success del AJAX. Debes declarar var lotesContador = {}; en un scope compartido (por ejemplo, al inicio de tu archivo JS o dentro de tu $(function(){...})), y luego usar esta lógica al recibir la respuesta del servidor para calcular el loteFinal.

/* Declarar al inicio del script */
var lotesContador = {};

/* Dentro del success del AJAX */
if (respuesta.error) {
    Toast.fire({
        icon: 'error',
        title: respuesta.message
    });
    return;
}

var loteCalculado = (respuesta && respuesta.lot) ? respuesta.lot : (lote || "UNKNOWN000001");

if (!loteCalculado) {
    console.error("No se pudo determinar el lote calculado");
    return;
}

var loteBase = loteCalculado.slice(0, -6);  // asume 6 dígitos de consecutivo
var consecutivoBackend = parseInt(loteCalculado.slice(-6), 10);
if (isNaN(consecutivoBackend)) consecutivoBackend = 0;

var loteFinal;

if (!lotesContador.hasOwnProperty(loteBase)) {
    // Primera vez: inicializar con lo que trae el backend
    lotesContador[loteBase] = consecutivoBackend;
    loteFinal = loteCalculado;
} else {
    // Ya hay un contador local
    if (consecutivoBackend > lotesContador[loteBase]) {
        // Backend avanzó por fuera de esta sesión: sincronizamos
        lotesContador[loteBase] = consecutivoBackend;
        loteFinal = loteCalculado;
    } else {
        // Usamos el siguiente consecutivo en memoria
        lotesContador[loteBase] = lotesContador[loteBase] + 1;
        var nuevo = String(lotesContador[loteBase]).padStart(6, "0");
        loteFinal = loteBase + nuevo;
    }
}

/* Llamar a la función que agrega el renglón con loteFinal */
agregarRenglon( idProduct, codeProduct, loteFinal, description, salePrice,
    porcentTax, porcentIVARetenido, porcentISRRetenido,
    claveUnidadSAT, unidad, claveProductoSAT );

Este flujo asegura que:

  • Si el backend ya avanzó el consecutivo en otra sesión, el frontend se sincroniza.
  • Si no, el frontend incrementa localmente el consecutivo para evitar duplicados cuando se hagan clicks rápidos.

📋 Función agregarRenglon — ejemplo práctico

Aquí tienes una versión robusta de tu función que construye el HTML del renglón y normaliza valores numéricos para evitar NaN o inputs inválidos:

function agregarRenglon(idProduct, codeProduct, lote, description, salePrice,
    porcentTax, porcentIVARetenido, porcentISRRetenido,
    claveUnidadSAT, unidad, claveProductoSAT) {

    salePrice = Number(salePrice) || 0;
    porcentTax = Number(porcentTax) || 0;
    porcentIVARetenido = Number(porcentIVARetenido) || 0;
    porcentISRRetenido = Number(porcentISRRetenido) || 0;

    var tax = (porcentTax > 0) ? ((porcentTax * 0.01) * salePrice) : 0;
    var IVARetenido = (porcentIVARetenido > 0) ? ((porcentIVARetenido * 0.01) * salePrice) : 0;
    var ISRRetenido = (porcentISRRetenido > 0) ? ((porcentISRRetenido * 0.01) * salePrice) : 0;
    var neto = (((porcentTax * 0.01) + 1) * salePrice) - (IVARetenido + ISRRetenido);

    var renglon = "<div class='form-group row nuevoProduct'>";
    renglon += "<div class='col-1'>";
    renglon += "<button type='button' class='btn btn-danger quitProduct'><span class='far fa-trash-alt'></span></button>";
    renglon += " <button type='button' data-toggle='modal' data-target='#modelMoreInfoRow' class='btn btn-primary btnInfo'><span class='fa fa-fw fa-pencil-alt'></span></button> ";
    renglon += "<input type='hidden' class='idProductR' name='idProductR' value='" + (idProduct || "") + "'>";
    renglon += "</div>";
    renglon += "<div class='col-1'>";
    renglon += "<input type='hidden' class='claveProductoSATR' name='claveProductoSATR' value='" + (claveProductoSAT || "") + "'>";
    renglon += "<input type='hidden' class='claveUnidadSatR' name='claveUnidadSatR' value='" + (claveUnidadSAT || "") + "'>";
    renglon += "<input type='hidden' class='unidad' name='unidad' value='" + (unidad || "") + "'>";
    renglon += "<input type='text' class='form-control codeProduct' name='codeProduct' value='" + (codeProduct || "") + "'> </div>";
    renglon += "<div class='col-1'> <input type='text' class='form-control lote' name='lote' value='" + (lote || "") + "' required> </div>";
    renglon += "<div class='col-6'> <input type='text' class='form-control description' name='description' value='" + (description || "") + "' required> </div>";
    renglon += "<div class='col-1'> <input type='number' class='form-control cant' name='cant' value='1' required>";
    renglon += "<input type='hidden' class='porcentIVARetenido' name='porcentIVARetenido' value='" + porcentIVARetenido + "'>";
    renglon += "<input type='hidden' class='porcentISRRetenido' name='porcentISRRetenido' value='" + porcentISRRetenido + "'>";
    renglon += "<input type='hidden' class='porcentTax' name='porcentTax' value='" + porcentTax + "'></div>";
    renglon += "<div class='col-1'> <input type='number' class='form-control price' name='price' value='" + salePrice + "' required>";
    renglon += "<input type='hidden' class='IVARetenido' name='IVARetenido' value='" + IVARetenido + "'>";
    renglon += "<input type='hidden' class='ISRRetenido' name='ISRRetenido' value='" + ISRRetenido + "'>";
    renglon += "<input type='hidden' class='tax' name='tax' value='" + tax + "'> </div>";
    renglon += "<div class='col-1'> <input readonly type='number' class='form-control total' name='total' value='" + salePrice + "'>";
    renglon += "<input type='hidden' class='neto' name='neto' value='" + neto + "'> </div>";
    renglon += "</div>";

    $('.rowProducts').append(renglon);

    if (typeof listProducts === "function") listProducts();
}

✅ Manejo de errores y UX — toasts y mensajes claros

Una buena UX evita frustración. Cuando el backend devuelve error: true, mostramos un toast con icono de error y el mensaje específico:

if (respuesta.error) {
    Toast.fire({
        icon: 'error',
        title: respuesta.message
    });
    return;
}

Observa dos puntos importantes:

  • No uses icon: 'success' para errores.
  • Mantén los mensajes del backend claros y útiles para el usuario (por ejemplo: “El producto no tiene subcategoría asignada”).

🧪 Pruebas que debes ejecutar (y por qué)

Antes de subir a producción, ejecuta una batería de pruebas que validen los casos reales:

  1. Clicks rápidos: simula spam click y verifica que los lotes generados en el DOM sean secuenciales y no repetidos.
  2. Producto sin subcategoría: intenta agregar un producto con idSubCategoria vacío o inexistente y verifica que aparezca el toast con el mensaje correcto y que no se agregue el renglón.
  3. Multiusuario: abre dos sesiones diferentes (o dos navegadores) y agrega productos con la misma loteBase para comprobar la sincronización con el backend.
  4. Eliminar renglón: borra renglones y agrega nuevos; el contador en memoria no se decrementa (esto evita reuso accidental de consecutivos), verifica que los nuevos lotes aumenten correctamente.
  5. Prueba de estrés: con un pequeño script o con herramientas como k6 o ab, simula múltiples peticiones al endpoint getLastLot y verifica que el backend responda correctamente y que el frontend se comporte como esperado.

⚠️ Consideraciones y limitaciones de la solución

La solución de contador en memoria es práctica y resuelve el problema en la mayoría de los casos, pero existen condiciones donde se requiere mayor robustez:

  • Escalabilidad y clusters distribuidos: si tu aplicación corre en múltiples servidores y hay concurrente masiva, el contador en memoria por sesión no garantiza unicidad global. En ese caso, considera un servicio centralizado (Redis, tabla con contador atómico) para emitir consecutivos atómicos.
  • Transacciones críticas: si la generación del lote debe ser 100% atómica con la inserción del registro en inventario, la lógica debe moverse al backend con locking o transacción que reserve el siguiente número y lo persista en la misma operación.
  • Reutilización de números: en esta solución NO se reutilizan consecutivos al borrar renglones. Reusar números es peligroso porque puede llevar a duplicados e inconsistencias en auditoría. Si requieres reuso, debe implementarse cuidadosamente en el backend y con auditoría.

🔁 Alternativas y mejoras (sin la sección “Siguiente paso”)

Aquí describimos varias alternativas que puedes implementar según tu nivel de exigencia:

1) Redis / contador centralizado

Usar INCR en Redis para obtener el siguiente consecutivo de forma atómica. Ideal para arquitecturas distribuidas. Ventajas: simple y rápido. Inconveniente: requiere infra adicional.

2) Tabla en BD con fila de contador y SELECT FOR UPDATE

Usar una tabla con el contador y reservar el número con locking a nivel de transacción. Ventajas: no necesitas nueva infra; Inconveniente: puede ser más lento bajo alta concurrencia y requiere diseño cuidadoso para evitar contención.

3) Generación optimista y reconciliación

Generas un lote provisional en frontend y al persistir en backend verificas si está disponible. Si no, el backend retorna el lote correcto y el frontend actualiza el renglón. Esto requiere un flujo UX para reconciliación (indicar “lote pendiente” y luego “confirmado”).


📦 Registro de cambios — commit en tu repositorio

Los cambios relacionados con esta corrección se subieron al repositorio en el commit referenciado a continuación. Revisa el diff para ver exactamente qué archivos y líneas se modificaron.

Commit con los cambios: :contentReference[oaicite:0]{index=0}


📌 Checklist completo para despliegue (rápido)

  • ✅ Aplicar validación de idSubCategoria en backend.
  • ✅ Devolver error:true y message si falta subcategoría.
  • ✅ Implementar lotesContador en frontend y sincronizar con backend.
  • ✅ Mostrar Toast con icon: 'error' cuando haya problemas.
  • ✅ Ejecutar pruebas de clicks rápidos y multiusuario.
  • ✅ Instrumentar logs en backend para cada lote generado (auditoría).
  • ✅ No decrementar el contador local al borrar renglones.
  • ✅ Revisar la necesidad de un contador global (Redis/BD) según volumen de concurrencia.

🔍 Ejemplos de mensajes útiles para el backend

  • El producto no tiene subcategoría asignada — cuando idSubCategoria está vacío.
  • Subcategoría no encontrada — cuando el ID no existe en la tabla de subcategorías.
  • No se pudo obtener el último lote — cuando la consulta a la tabla de saldos falla.

Mensajes claros hacen más fácil el debugging y mejoran la experiencia del desarrollador y del usuario final.


🧾 Buenas prácticas y recomendaciones finales

  1. Fail fast: valida temprano en backend y devuelve errores claros.
  2. Fuente de verdad: el backend siempre es la fuente de verdad; el frontend optimiza UX y reduce latencia.
  3. Auditoría: registra cada vez que se asigna un lote para trazar operaciones en caso de discrepancias.
  4. Pruebas automáticas: escribe pruebas e2e que cubran clicks rápidos y multiusuario.
  5. Documentación: documenta en el README del repo el comportamiento del contador en memoria y las condiciones que requieren un contador centralizado.

🔚 Conclusión

Resolver la duplicación de lotes en un sistema de inventario es crítico para mantener la integridad de los datos y la confianza del usuario. La estrategia que aplicamos —validación temprana en backend y un contador en memoria sincronizado en frontend— ofrece una solución práctica, rápida de implementar y efectiva para la gran mayoría de escenarios. Para entornos de alta concurrencia o arquitecturas distribuidas, existe la opción de aumentar la robustez con contadores atómicos centralizados (Redis, BD con locking).

Si quieres que convierta esta guía en un artículo formateado para tu tema de WordPress (por ejemplo con estilos específicos o bloques personalizados), o que genere una versión en Markdown para tu repo, lo preparo. Solo pégalo en un bloque HTML personalizado y debería quedar correcto en Gutenberg.

Artículo generado para facilitar la integración de correcciones en el módulo de inventario. Copia y pega en tu editor de WordPress (bloque HTML personalizado) y listo. ✅

🚀 Nueva actualización lav1.5.1 — Manejo mejorado de Imagen de Perfil en Boilerplate

Entrada fija

🚀 Nueva actualización lav1.5.1 — Manejo mejorado de Imagen de Perfil en Boilerplate

La versión lav1.5.1 introduce una de las mejoras más esperadas en el flujo de autenticación y gestión de usuarios: la capacidad de manejar de forma nativa la imagen de perfil, con validación robusta, soporte para valores NULL en base de datos y un avatar por defecto cuando no se encuentra la imagen.

Este release no solo mejora la experiencia visual y de usabilidad, sino que también fortalece la integración de datos del usuario dentro de las vistas del sistema. A continuación, presentamos en detalle todos los cambios, mejoras y la forma recomendada de actualizar tu proyecto.


✨ Principales Novedades

🔹 1. Imagen de perfil con validación

Ahora el sistema valida si el campo profile_image existe en la base de datos y contiene un valor válido.

  • Si el campo está vacío o en NULL, se muestra automáticamente un avatar genérico.
  • Si el archivo no existe en el servidor, el sistema también utiliza el avatar por defecto.

🔹 2. Avatar por defecto

El avatar por defecto se toma de la CDN oficial de AdminLTE:

https://cdn.jsdelivr.net/npm/admin-lte@3.0.2/dist/img/avatar.png

🔹 3. Integración en el Header y Sidebar

La imagen de perfil ahora aparece en dos áreas clave:

  • Navbar (parte superior) junto al nombre de usuario.
  • Sidebar (menú lateral) dentro del panel de usuario.

🔹 4. Comando de actualización de esquema

Con esta versión se incluyen nuevos campos y tablas, por lo que añadimos el comando:

php spark boilerplate:update

Este comando sincroniza tu base de datos con los cambios de la versión actual.


📦 Cómo actualizar a lav1.5.1

1. Actualizar dependencias con Composer

Ejecuta en la raíz de tu proyecto:

composer update julio101290/boilerplate

Esto descargará la nueva versión y reemplazará los archivos necesarios.

2. Ejecutar la actualización de esquema

Después de actualizar el código, corre el comando:

php spark boilerplate:update

Este proceso migrará las tablas y añadirá los nuevos campos, entre ellos el de profile_image.

3. Limpiar cachés y asegurarte de que todo carga

php spark cache:clear
php spark config:clear

🛠 Cambios Técnicos Detallados

Validación en vistas

Ejemplo de uso en el header:

<a href="<?= base_url(route_to('user-profile')) ?>" class="nav-link d-flex align-items-center">
    <img src="<?= user()->profile_image && file_exists(FCPATH . 'uploads/profiles/' . user()->profile_image) 
        ? base_url('uploads/profiles/' . user()->profile_image) 
        : 'https://cdn.jsdelivr.net/npm/admin-lte@3.0.2/dist/img/avatar.png' ?>"
        class="avatar-img img-circle bg-gray mr-2 elevation-<?= config('Boilerplate')->theme['navbar']['user']['shadow'] ?>"
        alt="<?= user()->username ?>" height="32">
    <?= user()->username ?>
</a>

Ejemplo en el sidebar:

<div class="image">
    <img src="<?= user()->profile_image && file_exists(FCPATH . 'uploads/profiles/' . user()->profile_image) 
        ? base_url('uploads/profiles/' . user()->profile_image) 
        : 'https://cdn.jsdelivr.net/npm/admin-lte@3.0.2/dist/img/avatar.png' ?>"
        class="img-circle elevation-<?= config('Boilerplate')->theme['sidebar']['user']['shadow'] ?>"
        alt="User Image">
</div>

Cambios en la Base de Datos

  • Se añade el campo profile_image a la tabla de usuarios.
  • Este campo acepta NULL de manera nativa.
  • Si no se define, la vista renderiza automáticamente el avatar por defecto.

Compatibilidad

  • Compatible con PostgreSQL y MariaDB.
  • No requiere cambios manuales en tablas previas: el comando php spark boilerplate:update se encarga de todo.

🔒 Beneficios de Seguridad

  • No se exponen rutas inválidas de imágenes.
  • Se evita el error de “imagen rota”.
  • Se normalizan los valores NULL.
  • Mejora la experiencia del usuario manteniendo consistencia en toda la interfaz.

📊 Ejemplo Visual del Cambio

Antes (versión anterior):

  • Los usuarios sin foto aparecían con un ícono roto.
  • Los campos NULL no eran validados correctamente.

Ahora (lav1.5.1):

  • Todos los usuarios tienen avatar visible.
  • Integración estética y funcional en Navbar y Sidebar.

📝 Changelog resumido

  • ✅ Validación de profile_image en todas las vistas principales.
  • ✅ Avatar por defecto cuando el campo está vacío o NULL.
  • ✅ Compatibilidad con PostgreSQL y MariaDB.
  • ✅ Nuevo comando php spark boilerplate:update.
  • ✅ Correcciones menores en estilos de AdminLTE.

🚀 Guía Paso a Paso de Migración

  1. Respaldar tu proyecto actual.
  2. Ejecutar:
   composer update julio101290/boilerplate
  1. Correr la migración:
   php spark boilerplate:updatecommand
  1. Limpiar caché.
  2. Verificar que los usuarios aparecen con sus fotos de perfil o con el avatar por defecto.

📌 Notas Finales

Este release v1.5.1 marca un paso adelante en la personalización de Boilerplate.
Ahora cada usuario tiene una experiencia más consistente y profesional dentro de la aplicación.

👉 Si quieres revisar el código y los commits de esta versión:
🔗 Release en GitHub

Cómo instalar Composer en Linux Mint 21.3 (Xia)

Entrada fija

Cómo instalar Composer en Linux Mint 21.3 (Xia)

Sigue estos pasos desde la terminal para instalar Composer correctamente:

1️⃣ Instalar PHP y dependencias

sudo apt update
sudo apt install php-cli unzip curl

2️⃣ Descargar el instalador de Composer

curl -sS https://getcomposer.org/installer -o composer-setup.php

3️⃣ Verificar integridad del instalador (opcional)

HASH=$(curl -sS https://composer.github.io/installer.sig)
php -r "if (hash_file('sha384', 'composer-setup.php') === '$HASH') { echo 'Verificado correctamente'.PHP_EOL; } else { echo 'ERROR: Archivo corrupto'.PHP_EOL; unlink('composer-setup.php'); }"

4️⃣ Instalar Composer globalmente

sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer

5️⃣ Verificar instalación

composer --version

🧹 Eliminar instalador (opcional)

rm composer-setup.php

¡Listo! Ya tienes Composer instalado en tu sistema.

🚀 Cómo Instalar Apache, PHP, MariaDB y Certbot en Deepin 23 (Servidor LAMP + HTTPS)

Entrada fija


🎯 Objetivo

En esta guía aprenderás a configurar un servidor web local con Apache, PHP y MariaDB en Deepin 23 y utilizar un alias personalizado como tusitio.localhost para tus desarrollos. Ideal para entornos de prueba de proyectos como Laravel, WordPress, etc.

1. 🔄 Actualizar el sistema

sudo apt update && sudo apt upgrade -y

2. 🌐 Instalar Apache

sudo apt install apache2 -y
sudo systemctl status apache2

Verifica accediendo a http://localhost.

3. 🐘 Instalar PHP y módulos necesarios

sudo apt install php libapache2-mod-php php-mysql php-cli php-xml php-curl php-mbstring php-zip php-gd -y
php -v

4. 🛢️ Instalar MariaDB

sudo apt install mariadb-server mariadb-client -y
sudo systemctl status mariadb

5. 🔐 Asegurar MariaDB

sudo mariadb
ALTER USER 'root'@'localhost' IDENTIFIED BY 'TuContraseñaSegura';
DELETE FROM mysql.user WHERE User='';
DELETE FROM mysql.user WHERE User='root' AND Host!='localhost';
DROP DATABASE IF EXISTS test;
FLUSH PRIVILEGES;
EXIT;

6. 🛠️ Crear VirtualHost local tusitio.localhost

sudo mkdir -p /var/www/tusitio
sudo nano /etc/apache2/sites-available/tusitio.conf

Pega el siguiente contenido:

<VirtualHost *:80>
    ServerAdmin admin@localhost
    ServerName tusitio.localhost
    DocumentRoot /var/www/tusitio

    <Directory /var/www/tusitio>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/tusitio_error.log
    CustomLog ${APACHE_LOG_DIR}/tusitio_access.log combined
</VirtualHost>

Habilitar el sitio y reiniciar Apache:

sudo a2ensite tusitio.conf
sudo systemctl reload apache2

7. 📝 Añadir alias en /etc/hosts

sudo nano /etc/hosts

Agrega al final del archivo:

127.0.0.1   tusitio.localhost

8. 🚀 Probar

Crea un archivo de prueba:

echo "<?php echo 'Hola desde tusitio.localhost'; ?>" | sudo tee /var/www/tusitio/index.php

Abre en el navegador: http://tusitio.localhost

✅ Resultado

ComponenteEstado
ApacheActivo
PHPFuncional
MariaDBProtegida
Alias localtusitio.localhost operativo

📌 Conclusión

Ya tienes un entorno de desarrollo web local usando un alias personalizado (tusitio.localhost) ideal para trabajar de forma organizada, simulando servidores reales. Puedes replicar este proceso para todos tus proyectos locales.

¿Qué Base de Datos Elegir? Comparativa de MariaDB, PostgreSQL y SQL Server

Entrada fija
  • Si buscas una solución gratuita, potente, extensible y con una gran comunidad, ideal para una amplia variedad de aplicaciones (web, móviles, empresariales) y si no dependes fuertemente del ecosistema Microsoft: PostgreSQL es una excelente opción. Su robustez y características avanzadas lo hacen muy versátil.
  • Si necesitas una solución gratuita, con buen rendimiento y alta compatibilidad con MySQL, especialmente si ya tienes experiencia con MySQL o planeas una migración sencilla: MariaDB es una alternativa sólida.
  • Si tu infraestructura ya está fuertemente basada en productos Microsoft, necesitas una integración perfecta con ellos, un amplio conjunto de herramientas empresariales y soporte comercial robusto, y el presupuesto no es la principal limitante: SQL Server sería la opción más adecuada.

En resumen:

  • PostgreSQL: Versátil, potente, de código abierto, ideal para diversas aplicaciones.
  • MariaDB: Gratuita, buen rendimiento, compatible con MySQL.
  • SQL Server: Óptimo para entornos Microsoft, con amplias funcionalidades comerciales.

Para tomar la mejor decisión, te recomiendo considerar los siguientes factores específicos para tu situación en Los Mochis:

  • Requisitos de tu aplicación: ¿Qué tipo de datos manejarás? ¿Qué tipo de consultas realizarás? ¿Necesitas características específicas como geodatos?
  • Tamaño y escalabilidad esperada: ¿Cuánto crecerá tu base de datos y tu aplicación?
  • Experiencia de tu equipo: ¿Con qué base de datos están más familiarizados tus desarrolladores y administradores?
  • Presupuesto: ¿Puedes asumir los costos de licencia de SQL Server?
  • Infraestructura existente: ¿Qué sistemas operativos utilizas? ¿Necesitas una integración estrecha con otras herramientas?
  • Soporte local: ¿Hay experiencia y soporte técnico disponible en Los Mochis para alguna de estas bases de datos en particular?

Considerando que trabajarás con CodeIgniter 4, tanto MariaDB como PostgreSQL son excelentes opciones y muy populares en la comunidad de PHP y CodeIgniter. SQL Server también puede funcionar bien, pero podría tener algunas consideraciones adicionales.

MariaDB con CodeIgniter 4:

  • Ventajas:
    • Compatibilidad Directa: CodeIgniter 4 tiene un excelente soporte para MySQL, y dado que MariaDB es altamente compatible, la configuración y el uso serán muy sencillos.
    • Rendimiento: Puede ofrecer un buen rendimiento para aplicaciones web desarrolladas con CodeIgniter 4.
    • Facilidad de Uso: Muchos desarrolladores PHP están familiarizados con MySQL/MariaDB, lo que podría facilitar el desarrollo y la administración.
    • Código Abierto y Gratuito: Sin costos de licencia, lo cual es atractivo para muchos proyectos.
    • Comunidad: Amplia comunidad de usuarios de PHP y MySQL/MariaDB.
  • Desventajas:
    • Menos Características Avanzadas: Comparado con PostgreSQL, podría carecer de algunas características más avanzadas si tu aplicación en el futuro las necesitara (tipos de datos más complejos, extensiones como PostGIS, etc.).

PostgreSQL con CodeIgniter 4:

  • Ventajas:
    • Características Potentes: Ofrece características avanzadas que podrían ser útiles para aplicaciones más complejas (tipos de datos JSON, arrays, funciones avanzadas, etc.).
    • Integridad de Datos: Conocido por su robustez y cumplimiento de los estándares SQL, lo que puede contribuir a una mayor integridad de los datos.
    • Extensiones: La capacidad de extender su funcionalidad con extensiones como PostGIS para datos geoespaciales es una gran ventaja si tu aplicación lo requiere.
    • Rendimiento Sólido: Funciona muy bien con aplicaciones web y puede manejar grandes volúmenes de datos y consultas complejas de manera eficiente.
    • Comunidad: Una comunidad fuerte y activa dentro del mundo de PHP y PostgreSQL.
  • Desventajas:
    • Curva de Aprendizaje Ligeramente Mayor: Si no estás familiarizado con PostgreSQL, podría haber una pequeña curva de aprendizaje en comparación con MySQL/MariaDB.
    • Configuración Inicial: La configuración inicial podría tener algunos pasos ligeramente diferentes en comparación con MySQL/MariaDB.

SQL Server con CodeIgniter 4:

  • Ventajas:
    • Potencia y Funcionalidades: Ofrece un conjunto robusto de características empresariales.
    • Integración (si usas Windows Server): Si tu servidor corre en Windows Server, la integración podría ser más sencilla en algunos aspectos.
  • Desventajas:
    • Costo de Licencia: El costo de las licencias puede ser un factor limitante.
    • Configuración Adicional: La configuración para que CodeIgniter 4 se conecte a SQL Server podría requerir algunos pasos adicionales y la instalación de drivers específicos.
    • Menor Popularidad en el Mundo PHP: Aunque funciona, no es tan comúnmente utilizado con PHP como MariaDB o PostgreSQL, por lo que la comunidad y los ejemplos específicos para CodeIgniter 4 podrían ser menores.

¿Cuál es la mejor opción para CodeIgniter 4 ?

Considerando que se usara CodeIgniter 4, tanto MariaDB como PostgreSQL son excelentes opciones.

  • Si buscas simplicidad, familiaridad (si vienes de MySQL), buen rendimiento para aplicaciones web típicas y una configuración sencilla con CodeIgniter 4, MariaDB es una opción muy sólida y popular.
  • Si anticipas que tu aplicación podría necesitar características más avanzadas en el futuro, valoras la integridad de los datos, o quieres tener la flexibilidad de usar extensiones potentes como PostGIS, PostgreSQL es una opción fantástica y cada vez más popular en la comunidad PHP.

Mi recomendación general para CodeIgniter 4 sería:

  • Si no tienes necesidades muy específicas y buscas una solución probada y sencilla, elige MariaDB. Es muy probable que cumpla con todos tus requisitos para la mayoría de las aplicaciones web.
  • Si prevés que tu aplicación crecerá en complejidad o necesitará características más avanzadas, o si simplemente prefieres la potencia y las características de PostgreSQL, entonces esta sería una excelente elección. CodeIgniter 4 tiene un buen soporte para PostgreSQL.

SQL Server podría ser una buena opción si ya tienes una fuerte inversión en el ecosistema Microsoft y esa es la base de datos estándar en tu entorno. Sin embargo, para un proyecto nuevo con CodeIgniter 4, MariaDB o PostgreSQL suelen ser opciones más comunes y con una integración más directa en el mundo del desarrollo PHP.

En resumen, para CodeIgniter 4, te recomendaría inclinarte por MariaDB por su simplicidad y compatibilidad directa, o por PostgreSQL si anticipas necesidades más avanzadas en el futuro. Ambas son excelentes bases de datos y funcionarán muy bien con el framework. ¡La elección final dependerá de tus requisitos específicos!

CodeIgniter 4 Boilerplate CFDI Descarga Masiva

Entrada fija
Latest Stable Version
Total Downloads
Latest Unstable Version
License

   

image

CodeIgniter 4 Boilerplate CFDI Descarga Masiva

Biblioteca para la Descarga Masiva de Facturas Electrónicas CFDI del SAT, solo requiere Certificado Fiel, Clave y Contraseña.

Requerimientos

  • PhpCfdi\SatCatalogos
  • julio101290/boilerplatelog
  • hermawan/codeigniter4-datatables
  • phpcfdi/cfditopdf
  • phpcfdi/cfdi-to-json”
  • phpcfdi/xml-cancelacion

Instalación

Ejecutar Comandos

composer require phpcfdi/sat-catalogos

composer require hermawan/codeigniter4-datatables

composer require julio101290/boilerplatelog

composer require julio101290/boilerplatecompanies

composer require julio101290/boilerplatestorages

composer require julio101290/boilerplatetypesmovement

composer require julio101290/boilerplatequotes

composer require julio101290/boilerplatecfdidescargamasiva

Ejecutar comando de migración y sembrado

php spark boilerplatecompanies:installcompaniescrud

php spark boilerplatelog:installlog

php spark boilerplatestorages:installstorages

php spark boilerplatetypesmovement:installtypesmovement

php spark boilerplatequotes:installquotes

Creando el menu, Ejemplo

image

Listo

image

Usage

You can find how it works with the read code routes, controller and views etc. Finnally… Happy Coding!

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Contributions are very welcome.

License

This package is free software distributed under the terms of the MIT license.

CodeIgniter 4 Boilerplate Catalogo de Vehiculos

Entrada fija
Latest Stable Version
Total Downloads
Latest Unstable Version
License

   

CodeIgniter 4 Boilerplate Catalogo de Vehiculos

CodeIgniter4 Boilerplatevehicles CRUD MVC tipo captura Vehículo y vehículos para facturas y Carta Mexicana Porte y módulo de taller

Requerimientos

  • PhpCfdi\SatCatalogos
  • julio101290/boilerplatelog
  • hermawan/codeigniter4-datatables

Instalación

Ejecutar Comandos

composer require phpcfdi/sat-catalogos

composer require hermawan/codeigniter4-datatables

composer require julio101290/boilerplatelog

composer require julio101290/boilerplatecompanies

composer require julio101290/boilerplatestorages

composer require julio101290/boilerplatetypesmovement

composer require julio101290/boilerplatevehicles

Ejecutar comandos para la migración y sembrado

php spark boilerplatecompanies:installcompaniescrud

php spark boilerplatelog:installlog

php spark boilerplatestorages:installstorages

php spark boilerplatetypesmovement:installtypesmovement

php spark boilerplatevehicles:installvehicles

Creamos el menú para el tipo de vehiculo, Ejemplo

image

Creamos el menú para el vehiculo, ejemplo

image

Listo

image
image

Usage

You can find how it works with the read code routes, controller and views etc. Finnally… Happy Coding!

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Contributions are very welcome.

License

This package is free software distributed under the terms of the MIT license.

CodeIgniter 4 Boilerplate Inventory

Entrada fija
Latest Stable Version
Total Downloads
Latest Unstable Version
License

   

CodeIgniter 4 Boilerplate Inventory

CodeIgniter4 Boilerplateinventory Create entries, exits and inventory transfer

Requirements

  • PhpCfdi\SatCatalogos
  • julio101290/boilerplatelog
  • hermawan/codeigniter4-datatables

Installation

Run commands

composer require phpcfdi/sat-catalogos

composer require hermawan/codeigniter4-datatables

composer require julio101290/boilerplatelog

composer require julio101290/boilerplatecompanies

composer require julio101290/boilerplatestorages

composer require julio101290/boilerplatetypesmovement

composer require julio101290/boilerplateinventory

Run command for migration and seeder

php spark boilerplatecompanies:installcompaniescrud

php spark boilerplatelog:installlog

php spark boilerplatestorages:installstorages

php spark boilerplatetypesmovement:installtypesmovement

boilerplateinventory:installinventory

Make the Menu, Example

image
image

Ready

image
image

Usage

You can find how it works with the read code routes, controller and views etc. Finnally… Happy Coding!

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Contributions are very welcome.

License

This package is free software distributed under the terms of the MIT license.

CodeIgniter 4 Boilerplate Tipos de Movimiento de inventario

Entrada fija

CodeIgniter 4 Boilerplate Tipos de inventario

CodeIgniter4 Boilerplatetypesmovement CRUD MVCfor the types of movement inventory, with fields as companye, description, type Input/Output, and it’s transfer

Requerimientos

  • PhpCfdi\SatCatalogos
  • julio101290/boilerplatelog
  • hermawan/codeigniter4-datatables

Instalación

Ejecutar comandos

composer require phpcfdi/sat-catalogos

composer require hermawan/codeigniter4-datatables

composer require julio101290/boilerplatelog

composer require julio101290/boilerplatecompanies

composer require julio101290/boilerplatestorages

composer require julio101290/boilerplatetypesmovement

Ejecutar comandos de migración y sembrado

php spark boilerplatecompanies:installcompaniescrud

php spark boilerplatelog:installlog

php spark boilerplatestorages:installstorages

php spark boilerplatetypesmovement:installtypesmovement

Crea el menu, ejemplo

image

Listo

image

Usage

You can find how it works with the read code routes, controller and views etc. Finnally… Happy Coding!

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Contributions are very welcome.

License

This package is free software distributed under the terms of the MIT license.

Instalación y Uso de Boilerplate Settings en CodeIgniter 4

Entrada fija

CodeIgniter 4 Boilerplate Settings

Esta biblioteca es una extensión para configuraciones globales de plantilla Boilerplate. Puede guardar el nombre de la compañía, el número de teléfono, el correo electrónico y cambiar el idioma en toda la aplicación CodeIgniter 4.

Instalación

Ejecutar comando

composer require julio101290/boilerplatesettings

Ejecutar el comando para la migración y sembrado

php spark boilerplatesettings:installsettings

Creamos el menu

image

Listo al final queda asi

image

Usage

You can find how it works with the read code routes, controller and views etc. Finnally… Happy Coding!

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Contributions are very welcome.

License

This package is free software distributed under the terms of the MIT license.

Página 1 de 3

Creado con WordPress & Tema de Anders Norén