Blog

  • Cómo hacer un join en Doctrine

    Cómo hacer un join en Doctrine

    Parece algo bien simple, ¿no? Después de todo, hacerlo en SQL lo es. Pero si estás usando Doctrine, lo correcto es usar las capacidades del ORM.

    Recientemente tenía que resolver un problema de un cliente: se le habían duplicado registros en la base y era necesario eliminarlos.

    El punto es que no se tenían que eliminar todos los registros, si no sólo los problemáticos.

    El modelo de datos es algo así:

    Lo que yo necesitaba hacer era eliminar aquellas transacciones que, entre otros criterios de filtro, pertenecieran a un proveedor en particular.

    La forma en que lo hice fue mediante una consulta que busque todos los objetos necesarios y luego los elimine:

    $qb = $this->em
                ->createQueryBuilder()
                ->select('t')
                ->from(Transaction::class, 't')
                ->where('t.date BETWEEN :f AND :t')
                ->setParameter('f', $fromDate)
                ->setParameter('t', $toDate)
                ->orderBy('t.date')
            ;

    Hasta aquí se arma la consulta genérica, sólo incluyendo los filtros obligatorios (Las fechas básicamente).

    Luego, si se desea usar el filtro por proveedor tenemos:

    $qb->innerJoin(Account::class, 'a')
       ->andWhere('a.provider = :p')
       ->setParameter('p', $provider);

    Todo normal, ¿cierto?

    Pues aquí arrancaron los problemas.

    Cuando fuí a verificar en la pantalla de la aplicación cuántos registros correspondían al rango de fecha y al proveedor encontré unos 3000, sin embargo, el script me estaba diciendo que iba a eliminar unas 100k transacciones.

    De modo que decidí agregarle al script una opción para ver el SQL que estaba a punto de ejecutar y esto es lo que encontré:

    SELECT t0_.id AS id_1, t0_.amount AS amount_5, t0_.account_id AS account_id_10 FROM transaction t0_ INNER JOIN account a1_ WHERE (t0_.date BETWEEN ? AND ?) AND a1_.provider_id = ? ORDER BY t0_.date ASC

    Nuevamente me quedé un rato mirando el SQL. Todo se veía bien.

    ¿Qué podía estar pasando?

    Bueno… la verdad es que no estaba tan bien el SQL. Si le prestás un poco de atención notarás que al INNER JOIN le falta un detalle: la cláusula ON.

    El SQL que debería haberse generado debía ser más parecido a:

    SELECT t0_.id AS id_1, t0_.amount AS amount_5, t0_.account_id AS account_id_10 FROM transaction t0_ INNER JOIN account a1_ ON a1_.id = t0.account_id WHERE (t0_.date BETWEEN ? AND ?) AND a1_.provider_id = ? ORDER BY t0_.date ASC

    Sí. Ese simple detalle estaba haciendo una diferencia fundamental.

    Sin la cláusula ON el INNER JOIN se convierte en el producto cartesiano de las dos tablas, con lo cual se pierde totalmente el sentido de usar un JOIN y el resultado tiene muy poco sentido

    Perfecto, el problema está identificado. ¿Cómo lo solucionamos?

    Como de costumbre, se trata de volver a las fuentes. La documentación de Doctrine es bastante clara al respecto.

    Se puede hacer algo como:

    $qb->innerJoin(Account::class, 'a', Join::ON, 't.account_id = a.id')
       ->andWhere('a.provider = :p')
       ->setParameter('p', $provider);

    Y el resultado será correcto, pero si lo dejamos así estamos haciendo trabajo extra… y ya que tenemos un ORM, ¿por qué hacerlo?

    Siendo t el alias de la clase Transaction y estando la relación definida como parte del mapeo objeto-relacional una mejor versión es esta:

    $qb->innerJoin('t.a', 'a')
       ->andWhere('a.provider = :p')
       ->setParameter('p', $provider);

    En conclusión SQL y DQL son parecidos pero no tanto. Vale la pena conocer las diferencias y usar lo mejor de cada uno en cada ocasión.

  • Por qué programar en Spanglish es una mala idea

    Por qué programar en Spanglish es una mala idea

    Es muy común, en los comienzos de la carrera profesional, la adopción de malas prácticas.

    En este sentido los desarrolladores de habla hispana tienen una desventaja respecto de sus pares angloparlantes.

    Para los segundos palabras como if, for o while tienen sentido por sí mismas.

    Los primeros en cambio requieren un esfuerzo extra para hacer la traducción, por pequeño que sea.

    El hecho de que no contar con el inglés como lengua materna lleva muchas veces a plasmar las ideas utilizando el vocabulario que resulta natural.

    Sin embargo, las palabras reservadas del lenguaje utilizado, en este caso PHP, están tomadas del inglés.

    Es por eso que es muy común encontrar construcciones como esta en el código producido por programadores latinos:

    if (!empty($usuariosRegistrados)) {
      ...
    }

    Este código no tendrá problemas en ser ejecutado, sin embargo, encierra varios inconvenientes no siempre detectables a simple vista.

    Aumento de la carga cognitiva

    Este tipo de estructuras fuerza al cerebro a hacer un esfuerzo de traducción (sea de Español a Inglés o viceversa) que, por pequeño que sea, afecta el rendimiento.

    De alguna forma es similar a la diferencia entre:

    $verbose = $input->getOption('verbose');
    
    if ( $verbose ) {
        $output->writeln('Importing from: '. $filename);
    }
    
    $f = fopen( $filename, 'r' );
    $doctrine = $this->getContainer()->get('doctrine');
    $em = $doctrine->getEntityManager();
    
    $processed = $newAccounts = $newAgents = $changedAgents = $skipped = 0;
    $limit = $input->getOption('limit');
    if ( $limit && $verbose ) {
        $output->writeln('Processing maximum '.$limit.' accounts');
    }

    Y:

    $verbose = $input->getOption('verbose');if ( $verbose ) {    $output->writeln('Importing from: '. $filename);}$f = fopen( $filename, 'r' );$doctrine = $this->getContainer()->get('doctrine');
    $em = $doctrine->getEntityManager();
    $processed = $newAccounts = $newAgents = $changedAgents = $skipped = 0;$limit = $input->getOption('limit');if ( $limit && $verbose ) {    $output->writeln('Processing maximum '.$limit.' accounts');}

    Dificultad para la colaboración internacional

    Este punto se hace evidente cuando se requiere que desarrolladores que no hablan Español tengan una participación activa en el proyecto.

    Comprender código escrito por otras personas (O por nosotros mismos habiendo pasado un tiempo considerable) resulta difícil. Si a esa complejidad se le suma la falta de significado de los identificadores, la tarea se volverá realmente ardua, cuando no directamente imposible.

    Basta pensar en un código como:

    if (!empty($a) && $bxbhg > 50 ) {
      ...
    }

    Independientemente de la experiencia y conocimientos que se tengan sobre PHP, trabajar sobre este código no será sencillo, mucho menos placentero.

    Imposibilidad de aprovechar el potencial de herramientas

    Los entornos de desarrollo (IDEs) modernos, los frameworks y demás herramientas a menudo incorporan caractetísticas de generación de código en forma automática.

    Por ejemplo, un framework de testing automatizado como phpUnit puede generar sus resultados de esta forma:

    PHPUnit 9.5.10 by Sebastian Bergmann and contributors.
    
    ..                                                                  2 / 2 (100%)
    
    Time: 00:00.009, Memory: 6.00 MB
    
    OK (2 tests, 2 assertions)    

    O de esta:

    PHPUnit 9.5.10 by Sebastian Bergmann and contributors.
    
    Calculator
     ✔ Add adds
    
    Time: 00:00.004, Memory: 6.00 MB
    
    OK (2 tests, 2 assertions)

    Según los parámetros pasados en la invocación del ejecutable.

    Claro que, para que esto tenga sentido, el nombre del método a ser probado debe tener algún significado como frase escrita en inglés.

    En este caso sería:

    <?php
    
    declare(strict_types=1);
    
    use PHPUnit\Framework\TestCase;
    
    final class CalculatorTest extends TestCase
    {
            public function testAddAdds()
            {
                    $sut = new Calculator();
                    $this->assertEquals(8, $sut->add(5, 3));
            }
    }

    Entonces… ¿es necesario aprender inglés?

    La respuesta depende en gran medida de los objetivos profesionales.

    Para quien aspire a integrar equipos de desarrollo compuestos por profesionales de diversas nacionalidades, no dominar el inglés puede convertirse en un limitante importante.

    Claro que un buen nivel de inglés no es suficiente para competir globalmente pero es un buen comienzo.

  • Cómo validar que un correo existe usando PHP

    Cómo validar que un correo existe usando PHP

    Seguramente alguna vez te habrás topado con la necesidad de registrar correos electrónicos de los visitantes de tu sitio, ¿cierto?

    Usualmente esto se hace para mantenerlos al tanto de las novedades.

    Claro que, aunque vos tengas las mejores intenciones, es entendible que la gente sea un poco escéptica con tanto spammer dando vueltas, por lo tanto, no es raro que muchos llenen el campo correo electrónico con basura, lo cual no sirve más que para hacer crecer tu base de datos inútilmente.

    ¿Qué podés hacer para evitar esto?

    Hay varias medidas que se pueden tomar, te voy a comentar algunas:

    Cómo validar que el texto sea una dirección de correo electrónico

    Lo primero de lo que deberías asegurarte es de que el texto ingresado por el usuario es (o al menos podría ser) una dirección de correo electrónico.

    Para hacer esto tenés dos opciones: hacerlo desde el frontend o desde el backend. Si tenés dudas de cuál te conviene te recomiendo este artículo.

    Me voy a concentrar en la validación del lado del servidor que es la que efectivamente se hará usando PHP.

    Asumamos que se recibe un POST que incluye un campo email y necesitamos verificar su contenido.

    Como de costumbre, existen muchas formas de hacerlo pero ciertamente hay una que supera a las demás: usar las herramientas incorporadas.

    Concretamente me refiero a la función filter_var, se puede usar de esta forma:

    <?php
    
    echo (filter_var($argv[1], FILTER_VALIDATE_EMAIL) ? 'Es email' : 'No es email').PHP_EOL;
    

    Corriendo este script de esta forma:

    php test.php mauro.chojrin@leewayweb.com

    Obtendrás esta salida:

    Es email

    Mientras que si lo ejecutas así:

    php test.php mauro.chojrin.leewayweb.com

    La salida será:

    No es email

    Hasta aquí pinta bien, ¿cierto? Pero… ¿es realmente una dirección de correo? Es decir, ¿existe una casilla de correo que responda a esa dirección?

    Cómo validar que la dirección ingresada exista

    Como decía, que el texto ingresado cumpla con el formato de una dirección de correo es condición necesaria pero no suficiente para nuestros propósitos.

    Necesitamos algo más sólido.

    Algo que podemos usar es una validación a nivel de SMTP, es decir, intentar enviar un correo y ver qué responde el servidor que, supuestamente, debería recibirlo.

    Implementar esto desde 0 es, claramente, una tarea compleja.

    Mejor apoyarnos en alguna librería existente. Por ejemplo esta.

    La instalamos usando composer así:

    composer require zytzagoo/smtp-validate-email --update-no-dev

    Y luego podemos usar un código como este:

    <?php
    
    require 'vendor/autoload.php';
    
    use SMTPValidateEmail\Validator as SmtpEmailValidator;
    
    $validator = new SmtpEmailValidator($argv[1], 'sender@example.org');
    
    $results   = $validator->validate();
    
    echo $results[$argv[1]] ? 'El email existe' : 'El email no existe';
    
    echo PHP_EOL;

    Y al ejecutar esto:

    php test.php mauro.chojrin@leewayweb.com

    Nos confirmará que la dirección existe.

    Nos queda sólo una pequeña duda… ¿hay alguien leyendo esos emails?

    Cómo validar que alguien chequea esa casilla

    Esta parte es un poco más compleja, aunque definitvamente es lo mejor que se puede hacer.

    Un método muy conocido es el doble opt-in.

    Se trata de enviar un correo pidiendo la confirmación y sólo activar el registro cuando alguien haga click en el link que contiene dicho correo.

    Una forma de implementarlo es crear un campo adicional dentro de la tabla de usuarios que se llene con un token, el cual será enviado a la dirección introducida por el usuario dentro de un link similar a:

    http://tusitio.com/users/activate.php?token=$token

    Del lado de activate.php tenés que buscar un usuario cuyo token coincida con el parámetro recibido y, en tal caso, marcar el registro como activo.

    En este video podés ver un ejemplo de solución de un problema parecido (El recupero de contraseña).

    Recapitulando

    En este post hablamos de tres modos de validar que un correo recibido sea, efectivamente, un correo.

    Cada una de las formas es un poco más compleja (y certera) que la anterior.

    ¿Cuál deberías usar? Idealmente las tres.

    ¿Por qué las tres? Simplemente porque el aumento de complejidad también implica una menor eficiencia entonces si un dato debería ser descartado, lo mejor es hacerlo lo antes posible de modo de ahorrar recursos de tu servidor y tiempo de tus usuarios.

  • Cómo mostrar tu proyecto PHP sin usar un hosting

    Cómo mostrar tu proyecto PHP sin usar un hosting

    Listo. Terminado. Finito.

    Ah… qué placer, ¿no?

    Después de horas frente a la pantalla, incontables tazas de café y miles de bugs resueltos, por fin llegará el merecido descanso… sólo falta hacer la demo para el cliente.

    Es que si no se hace el cliente no podrá dar su visto bueno y sin él… difícil que haga el último pago :p

    Podrías hacer un despliegue completo en su servidor pero… ¿y si algo sale mal?

    O peor, ¿qué tal si sale todo bien y el código ya está fuera de tu control?

    ¿Cómo hacer para que otra persona vea tu trabajo sin entregarle el código?


    Existen varias opciones según cuál sea tu modo de trabajar.

    Para este artículo asumiré que desarrollas en un servidor local (XAMPP o similar).

    Usar un servidor de pruebas

    Una opción perfectamente válida es utilizar un servidor intermedio en el cual puedas desplegar tu código.

    Idealmente este servidor será tuyo, con lo cual no tendrás que entregar tu código en forma prematura.

    La principal desventaja de este método es que puede tener un costo asociado: el del hosting.

    Por otra parte, preparar este espacio puede no ser una tarea trivial y hacerlo cada vez que tengas que realizar una demostración puede ser una pérdida de tiempo significativa.

    Abrir temporalmente el acceso a localhost

    Otra opción que puede resultar mucho más económica y sencilla es abrir temporalmente el acceso a tu computadora a través de internet.

    Existen diversas herramientas que puedes utilizar para asociar un nombre de dominio a tu dirección IP, de modo de evitarle a tu cliente tener que tipearla en su navegador.

    Un ejemplo de ese tipo de solución es No-IP.

    No es una mala opción, aunque puede ser un poco compleja de administrar (amén de requerir la instalación y ejecución constante de un cliente).

    Otra posiblidad bastante más atractiva es utilizar ngrok.

    Se trata de una utilidad muy sencilla que te permite levantar un túnel hacia tu computadora y pasarle a tu cliente una URL a la cual conectarse.

    Basta con ejecutar un comando como ngrok http 80 para obtener una pantalla como esta:

    Información de ngrok ejecutándose

    Aquí puedes ver claramente cómo la dirección http://7da0fd4f278a.ngrok.io apunta al puerto 80 en mi computadora local.

    Sólo se necesita ingresar a http://7da0fd4f278a.ngrok.io para ver lo mismo que yo veo usando http://localhost.

    No está mal, ¿cierto?

    Nada de FTP, nada de crear servidores temporales ni cosas parecidas.

    ¿Lo más interesante? Al terminar la prueba sólo se necesita dar Ctrl+C y asunto finalizado.

    Terminó la demo y terminó el acceso remoto.

    Simple, rápido y económico (o BBB si lo prefieres :))

    Adelante, la próxima demo que vayas a hacer no te compliques, crea ahora mismo una cuenta en ngrok, configura el cliente y olvídate del problema de los despliegues temporales

  • Guía para solucionar problemas de DataTables con Ajax y PHP

    Guía para solucionar problemas de DataTables con Ajax y PHP

    Usá DataTables te dijeron.

    «Es el mejor plugin que vas a encontrar para mostrar datos en forma resumida»

    «Es súper fácil de usar»

    Si… claro.

    Armar la tabla no es muy complicado, es cierto, pero cuando le empezabas a encontrar el gustito… las cosas se complicaron.

    ¿Y ahora?

    ¿¿Qué pasa que no se cargan los datos??

    Estarás pensando «¿Por qué no me quedé con las viejas tablas HTML que andaban sin problemas?»

    Pero en el fondo sabés por qué.

    Querés un sitio moderno.

    Querés que los visitantes tengan una buena experiencia.

    Querés que el cliente te vuelva a contratar y le cuente a sus amigos.

    Así que más vale hacer un esfuercito más.


    Lo primero que hay que tener claro es que no se puede resolver un problema sin primero diagnosticarlo.

    Es por eso que en este artículo voy a hablar sobre los problemas más frecuentes que te podés encontrar al usar DataTables.

    Aclaración: Aquí hablaré exclusivamente de problemas que impiden que el DataTable funcione. Si tenés un problema de performance mirá esto.

    Andá, buscate un café, yo te espero.

    ¿Listo?

    Bien, arranquemos.

    Partes que hacen al DataTable

    El DataTable se compone de tres elementos:

    • HTML
    • JavaScript
    • PHP

    El HTML del DataTable

    La primera pregunta que cabe es ¿El HTML está correctamente escrito?

    Doy por descontado que los tags <table> y </table> están en su lugar.

    Luego, ¿está puesto el <thead>? ¿Y el </thead>?

    Ok, y dentro del <thead>, ¿hay algún <tr>? y dentro del <tr>, ¿hay elementos <th>?

    En resumen, ¿tu table se ve parecido a este?

    <table id="theTable" class="display" style="width: 100%">
        <thead>
            <tr>
                <th>Id</th>
                <th>Name</th>
                <th>Price</th>
            </tr>
        </thead>
    </table>

    ¿Notaste que no hay <tbody>?

    No, no es un error.

    El <tbody> lo va a armar el JavaScript, ya vamos a llegar.

    El JavaScript del DataTable

    El segundo componente es el código JavaScript que le dará vida al DataTable.

    Lo principal (Donde yo mismo siempre me equivoco):

    ¿Está puesta la referencia a jQuery?

    Es decir, ¿hay en tu código algo como <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>?

    ¿Por qué se necesita esto? Porque jQuery es una dependencia de DataTables (Recordá que DataTables es simplemente un plugin de jQuery).

    A renglón seguido viene: ¿Está puesta la referencia al script de DataTables?

    O, en criollo, ¿hay en tu código algo como <script src="https://cdn.datatables.net/1.10.24/js/jquery.dataTables.min.js"></script>?

    ¿Sí? ¡Perfecto!

    Entonces, lo que queda es saber si la configuración del DataTable es correcta.

    En principio lo único imprescindible en ese escenario es la llamada Ajax, algo como:

    <script type="application/javascript">
        $(document).ready( function () {
            $('#theTable').DataTable({
                ajax: '/get_data.php',
            });
        } );
    </script>

    ¿Qué puede andar mal aquí? Bueno… aparte de errores de sintaxis que siempre puede haber, el id de la tabla podría no coincidir en la función y en el HTML.

    En este caso theTable está definido en la llamada a la función $ (En $('#theTable')) y en la definición de la tabla (<table id="theTable"...>).

    Otro tema conflictivo puede ser la URL a la que debe realizarse la llamada Ajax.

    Si todo esto está bien podrías sospechar de que la llamada Ajax no se está comportando como es debido (Tal vez no está mandando todos los parámetros o encabezados que espera tu PHP).

    Es un caso bastante extremo y, honestamente, no creo que se te presente pero… si desconfiás te recomiendo recurrir a la consola del desarrollador de tu navegador (En la pestaña Network):

    Consola del desarrollador del navegador

    Si todo esto está ok sólo queda un sospechoso posible…

    El PHP del DataTable

    Y al fin llegamos a la parte más jugosa del artículo 🙂

    Aquí realmente no hay muchas reglas que seguir ya que todo depende de cómo obtengas la información que buscas mostrar en tu DataTable.

    Lo que sí es importante es que tengas en cuenta qué es lo que tu FrontEnd espera recibir y cómo espera recibirlo.

    Así que vamos a las preguntas:

    ¿Estás retornando json?

    Si tienes dudas puedes hacer un request directamente a la URL a donde apunta tu Ajax.

    En mi caso sería http://localhost:8000/get_data.php y la respuesta que obtendré será:

    {
      "data": [
        [
          "1",
          "Chair",
          "20.0"
        ],
        [
          "2",
          "Shoe",
          "1.0"
        ],
        [
          "3",
          "Candle",
          "0.75"
        ]
      ]
    } 

    La forma más fácil de generar algo como esto es guardar los datos en un arreglo y luego usar la función json_encode.

    Si estás recibiendo una página en blanco o algún error, probablemente te convenga debuggear tu php antes de continuar.

    Claro que esto es sólo el primer paso.

    Luego debes verificar que ese json tenga la estructura correcta.

    ¿Tiene una propiedad llamada data?

    Y dentro de dicha propiedad, ¿está el arreglo de registros que buscas mostrar?

    Si todavía tienes problemas, otras dos preguntas que pueden orientarte:

    1. ¿La cantidad de datos que contienen los registros coincide con las columnas definidas en el HTML?
    2. ¿El orden de los elementos dentro del arreglo coincide con el de las columnas definidas en el HTML?

    Recapitulando

    DataTables es ciertamente un aliado poderoso, pero también es un cúmulo de partes móviles que deben estar coordinadas cual reloj suizo para que todo salga bien.

    Guárdate este artículo en tus favoritos para tenerlo siempre a mano si te vuelves a encontrar desorientado sobre cómo solucionar tus problemas con un DataTable.

    Simplemente recorre la lista de preguntas y rápidamente tendrás la clave que te ayudará a seguir adelante.

  • Cuál es la mejor librería para hacer PDF desde PHP

    Cuál es la mejor librería para hacer PDF desde PHP

    Apuesto que te habrás topado con la necesidad de emitir reportes en PDF alguna vez, ¿cierto?

    Por ejemplo para generar facturas electrónicas.

    De por sí es un tema integrarte con los webservices del fisco (Léase AFIP, SUNAT, DGI, DIAN, etc…) y cuando pensás que todo está resuelto, te encontrás con que falta subir otra colina más: generar los benditos pdfs.

    Seguro habrás pensado:

    «Tiene que haber una librería que permita hacer pdfs con PHP»

    Y tenés razón… y a la vez no.

    ¿Cómo?

    Pues… es que no existe una librería si no unas cuantas… y las diferencias entre una y otra no siempre están disponibles a simple vista.

    Casi podríamos decir que elegir la librería para generar pdfs es un proyecto en sí mismo.

    Así que, para ayudarte a tomar esta decisión te voy a presentar un cuadro con las opciones más conocidas y sus características principales para que puedas analizarlas en conjunto y elegir la más conveniente para tus circunstancias particulares.

    Código abiertoBasado en HTMLOrientada a ObjetosInstalación vía composerDocumentaciónDependencias
    domPDFBuenaPHP 7.1+
    DOM
    MBString
    php-font-lib
    php-svg-lib
    fPDFNoNoRegular??
    html2pdfBuenaPHP 5.6+
    gd
    MBString
    mPDFBuenaPHP 5.6 a 7.4
    gd
    MBString
    tcpdfNoRegularPHP 5.3+
    PDFLibNoNoNoMuy buena
    setaPDFNoNoMuy buenaPHP 5.6+
    DOM
    iconv o MBString
    OpenSSL
    SPL
    Zlib
    Comparativa entre librerías para hacer PDF con PHP

    Si estás en un ambiente de hosting compartido lo principal es verificar si la versión qué versión de php tienes y qué extensiones están disponibles.

    Luego debes entender qué buscas priorizar: la facilidad de implementar la librería o la eficiencia en el uso de los recursos.

    La mejor librería para generar PDFs de alto contenido gráfico

    Si necesitas generar PDFs con grandes detalles gráficos (Inclusión de imágenes de fondo por ejemplo), lo más probable es que te convenga inclinarte por una librería simple como domPDF, mPDF o html2pdf.

    El problema que tienen estas librerías es que se vuelven poco eficientes cuando se trata de generar muchos archivos en poco tiempo.

    La librería más rápida para generar PDFs con PHP

    Si el principal requisito de tu aplicación es la velocidad de procesamiento tu mejor opción es PDFLib.

    Dado que se trata de una extensión de php, su velocidad difícilmente pueda ser superada por algún competidor escrito en PHP.

    Claro que esta velocidad viene con un precio: se trata de una librería paga y, si estás en un ambiente de hosting compartido, puede ser complejo instalarla.

    Otras herramientas para generar PDF desde PHP

    Algunas otras herramientas que, si bien no son exactamente librerías de php, pueden servirte para generar PDFs en tus aplicaciones:

    Como puedes ver, la elección de la herramienta ideal no es sencilla pero vale la pena hacer un poco de investigación y, sobre todo, comprender la necesidad específica para determinar cuál es la opción más conveniente.

    Coméntame cómo ha sido tu experiencia y qué tuviste en cuenta para elegir la librería que usaste 😉

  • Por qué tus páginas con DataTables tardan tanto en cargar

    Por qué tus páginas con DataTables tardan tanto en cargar

    Una página que tarda mucho en cargar es una página mala.

    Lo sabés vos y lo saben tus usuarios.

    Si tu sitio tarda más de 5 segundos en mostrarse tus visitantes se sentirán así:

    Las páginas que usan DataTables se ven bien y son muy funcionales pero pueden tardar una eternidad en desplegarse.

    Más de la mitad de los usuarios que no encuentran pronto lo que buscan se van a otro sitio.

    Y los que se quedan desconfían de la profesionalidad de quien las desarrolló.

    ¿Y quién quiere contratar a alguien que entrega trabajos de dudosa calidad?

    Más vale dedicar unos minutos a aprender cómo mejorarlas.

    Factores que afectan el tiempo de carga de tu página

    En su forma más simple, un DataTable puede cargarse usando un arreglo JavaScript.

    Basta un código como este:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="//cdn.datatables.net/1.10.24/css/jquery.dataTables.min.css">
        <title>DataTables example</title>
    </head>
    <body>
    <h1>Behold... the power of DataTables!</h1>
    <table id="theTable" class="display" style="width: 100%">
        <thead>
            <tr>
                <th>Id</th>
                <th>Name</th>
                <th>Price</th>
            </tr>
        </thead>
        <tfoot>
            <tr>
                <th>Id</th>
                <th>Name</th>
                <th>Price</th>
            </tr>
        </tfoot>
    </table>
    </body>
    </html>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script src="https://cdn.datatables.net/1.10.24/js/jquery.dataTables.min.js"></script>
    <script type="application/javascript">
        $(document).ready( function () {
            $('#theTable').DataTable({
                'data': [
                    [1, 'Box', 10.0],
                    [2, 'Chair', 20.2]
                ],
            });
        } );
    </script>

    Por supuesto que si toda la información tuviese que estar hardcodeada no sería muy útil la aplicación, ¿cierto?

    Una versión algo más realista sería combinar este código con algo de PHP que levante información de una db, por ejemplo:

    <?php
    
    try {
        $conn = new PDO('sqlite:dt.sq3');
    } catch (PDOException $exception) {
        die($exception->getMessage());
    }
    
    $sql = "SELECT * FROM products";
    $st = $conn
    ->query($sql);
    
    if ($st) {
    $rs = $st->fetchAll(PDO::FETCH_ASSOC );
    ?>
    <script type="application/javascript">
        $(document).ready( function () {
            $('#theTable').DataTable({
                'data': [
                    <?php
                    foreach ($rs as $row) {
                        ?>
                    [ '<?php echo $row['id'];?>', '<?php echo $row['name'];?>', '<?php echo $row['price'];?>' ],
                        <?php
                    }
                    ?>
                ],
            });
        } );
    </script>
    <?php
    } else {
        var_dump($conn->errorInfo());
        die;
    }

    Hasta aquí, todo perfecto.

    El problema se presenta cuando la cantidad de registros es grande.

    La definición de grande es un poco esquiva… podés empezar a notar problemas con 500 registros pero si estás tratando con algo como 150 000 o más definitivamente tus páginas van a tardar una eternidad en mostrarse.

    Aún si tenés páginas pequeñas (Digamos, de 10 registros cada una), el tiempo total de carga no se modifica en absoluto…

    ¿Por qué sucede esto?

    Como en cualquier otro script, esto es, a grosso modo lo que ocurre:

    1. El servidor recibe la petición
    2. Se ejecuta el PHP
    3. Se realiza la consulta a la db
    4. El servidor recibe todos los registros de la tabla
    5. Se genera el HTML y el JavaScript
    6. Se envía todo al cliente
    7. El cliente interpreta el HTML y el JavaScript
    8. Se ejecuta el JavaScript y se dibuja la tabla y todo lo demás

    Como te podrás imaginar, para trabajar con los registros de la primera página no es realmente necesario tener en memoria las otras 500…

    Toda esa información que no se utilizará no hace más que sobrecargar tanto al servidor (especialmente al de bases de datos) como al cliente.

    Es más, si hay muchos usuarios accediendo a la vez el problema se va multiplicando, con lo cual la experiencia de uso es peor para todos 🙁

    Podrías intentar atacar el problema mejorar los recursos de hardware, implementando algún tipo de caché o similar y es posible que todo eso ayudara.

    Sin embargo, existe una solución mucho más al alcance de tus manos.

    La solución: Procesamiento del lado del Servidor

    Y aquí viene la genialidad de DataTables… ¡lo pensaron todo estos muchachos!

    Este plugin admite dos modos de procesamiento: Client-Side (Es decir, todo se hace en el navegador del cliente) o Server-Side (El procesamiento se hace del lado del servidor y sólo se realiza el rendering del lado del cliente).

    El modo por defecto es, por supuesto, Client-Side.

    He ahí la clave del problema.

    Trabajar con Server-Side es un poco más complicado, pero tampoco es terrible.

    Lo más importante es comprender que, en este modo, DataTables hará una llamada Ajax cada vez que se requiera re-dibujar la información de la tabla (Al paginar, ordenar, etc…).

    Para comenzar a usar este modo necesitás cambiar la configuración de tu DataTable por algo como:

    $('#theTable').DataTable( {
        serverSide: true,
        ajax: '/get_data.php'
    } );

    Claro que, a diferencia del ejemplo en que la tabla se carga por Ajax sólo al comienzo (o cuando se refresca), esta versión de get_data.php deberá ser bastante más inteligente para poder responder a las órdenes que le dará el FrontEnd.

    Qué envía el FrontEnd al BackEnd en DataTables Server-Side

    La comunicación entre FrontEnd y BackEnd se realizará a través del envío de parámetros a través de la URL de la llamada (salvo que lo cambies explícitamente).

    Esto implica que tu script php deberá recogerlos desde $_GET.

    A modo de resumen te comento los más importantes (La lista completa la podés encontrar acá):

    start: Número de registro por el que debe comenzar la paginación

    length: Cantidad de registros que se espera que devuelva el servidor. Es decir, a partir del registro start, retorná length registros (Similar al LIMIT X OFFSET Y de SQL)

    search: Arreglo que contiene los parámetros de la búsqueda realizada por el usuario. Esta búsqueda es global, es decir, aplica a todas aquellas columnas marcadas como searchable.

    • value: El texto ingresado por el usuario en el cuadro de búsqueda
    • regex: Booleano que indica si el value debe ser interpretado en forma literal o como expresión regular

    columns: Arreglo con una entrada por cada columna de la tabla.

    Cada entrada de este arreglo será, a su vez, un arreglo con las siguientes claves:

    • data: información de mapeo entre el arreglo de datos y la tabla HTML (En este ejemplo se trata del índice que vincula la columna en la visualización con el campo en la consulta: id => 0, name => 1, price => 2)
    • name: Nombre de la columna como está definido en la propiedad columns.name (En este ejemplo no lo estamos usando así que será un string vacío).
    • searchable: Booleano que indica si es posible realizar búsquedas sobre esta columna
    • orderable: Booleano que indica si es posible realizar ordenamientos basados en esta columna
    • search: Arreglo que contiene los parámetros de la búsqueda realizada por el usuario. Similar a la búsqueda global, sólo que aplicada a esta columna en particular (Un caso algo más avanzado y no muy frecuentemente utilizado).
      • value: El texto ingresado por el usuario en el cuadro de búsqueda
      • regex: Booleano que indica si el value debe ser interpretado en forma literal o como expresión regular

    order: Arreglo que especifica el criterio de ordenamiento que debe aplicarse, contendrá una entrada por cada criterio a utilizar (Para el caso de que se quieran combinar varios). Dentro de cada entrada contendrá un arreglo con dos claves:

    1. column: Número de columna por la que se quiere ordenar
    2. dir: String con la dirección del ordenamiento (asc para ascendente, desc para descendiente)

    Como te podrás imaginar, la URL de la llamada puede ser algo extensa 🙂

    ¿No me creés? ¿Qué te parece esta?

    http://localhost:8000/get_data.php?draw=1&columns%5B0%5D%5Bdata%5D=0&columns%5B0%5D%5Bname%5D=&columns%5B0%5D%5Bsearchable%5D=true&columns%5B0%5D%5Borderable%5D=true&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B1%5D%5Bdata%5D=1&columns%5B1%5D%5Bname%5D=&columns%5B1%5D%5Bsearchable%5D=true&columns%5B1%5D%5Borderable%5D=true&columns%5B1%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B1%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B2%5D%5Bdata%5D=2&columns%5B2%5D%5Bname%5D=&columns%5B2%5D%5Bsearchable%5D=true&columns%5B2%5D%5Borderable%5D=true&columns%5B2%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B2%5D%5Bsearch%5D%5Bregex%5D=false&order%5B0%5D%5Bcolumn%5D=0&order%5B0%5D%5Bdir%5D=asc&start=0&length=10&search%5Bvalue%5D=&search%5Bregex%5D=false&_=1619613946473

    Si la pasás a través de urldecode queda un poquito más entendible:

    http://localhost:8000/get_data.php?draw=1&columns[0][data]=0&columns[0][name]=&columns[0][searchable]=true&columns[0][orderable]=true&columns[0][search][value]=&columns[0][search][regex]=false&columns[1][data]=1&columns[1][name]=&columns[1][searchable]=true&columns[1][orderable]=true&columns[1][search][value]=&columns[1][search][regex]=false&columns[2][data]=2&columns[2][name]=&columns[2][searchable]=true&columns[2][orderable]=true&columns[2][search][value]=&columns[2][search][regex]=false&order[0][column]=0&order[0][dir]=asc&start=0&length=10&search[value]=&search[regex]=false&_=1619613946473

    O, todavía mejor si lo ves desde la consola del navegador:

    De modo que si del lado de tu PHP hacés un var_dump($_GET) te vas a encontrar con:

    array (size=7)
      'draw' => string '1' (length=1)
      'columns' => 
        array (size=3)
          0 => 
            array (size=5)
              'data' => string '0' (length=1)
              'name' => string '' (length=0)
              'searchable' => string 'true' (length=4)
              'orderable' => string 'true' (length=4)
              'search' => 
                array (size=2)
                  ...
          1 => 
            array (size=5)
              'data' => string '1' (length=1)
              'name' => string '' (length=0)
              'searchable' => string 'true' (length=4)
              'orderable' => string 'true' (length=4)
              'search' => 
                array (size=2)
                  ...
          2 => 
            array (size=5)
              'data' => string '2' (length=1)
              'name' => string '' (length=0)
              'searchable' => string 'true' (length=4)
              'orderable' => string 'true' (length=4)
              'search' => 
                array (size=2)
                  ...
      'order' => 
        array (size=1)
          0 => 
            array (size=2)
              'column' => string '0' (length=1)
              'dir' => string 'asc' (length=3)
      'start' => string '0' (length=1)
      'length' => string '10' (length=2)
      'search' => 
        array (size=2)
          'value' => string '' (length=0)
          'regex' => string 'false' (length=5)
      '_' => string '1619615674442' (length=13)

    Y de ahí… bueno, habrá que hacer la consulta que corresponda, por ejemplo:

    <?php
    
    try {
        $conn = new PDO('sqlite:dt.sq3');
    } catch (PDOException $exception) {
        die($exception->getMessage());
    }
    
    $orderBy = " ORDER BY ";
    foreach ($_GET['order'] as $order) {
        $orderBy .= $order['column'] + 1 . " {$order['dir']}, ";
    }
    
    $orderBy = substr($orderBy, 0, -2);
    $where = '';
    
    $columns = $_GET['columns'];
    $fields = ['id', 'name', 'price'];
    $where = '';
    
    foreach ($columns as $k => $column) {
        if ($search = $column['search']['value']) {
            $where .= $fields[$k].' = '.$search.' AND ';
        }
    }
    
    $where = substr($where, 0, -5);
    $length = $_GET['length'];
    $start = $_GET['start'];
    
    $sql = "SELECT * FROM products ".($where ? "WHERE $where " : '')."$orderBy LIMIT $length OFFSET $start";

    (Me tomé una pequeña licencia… asumí que el search no será una expresión regular… te queda como ejercicio :))

    Cómo debe responder el BackEnd al FrontEnd en DataTables Server-Side

    Por otro lado, es importante que tu php sepa qué es lo que DataTables está esperando como respuesta, de otro modo la comunicación no podrá realizarse exitosamente.

    Lo principal: DataTables espera un resultado JSON que debe contener:

    data: El arreglo de los registros que se levantaron de la base

    recordsTotal: Número total de registros que existen en la base (Sin aplicar filtros)

    recordsFiltered: Número de registros que devuelve la consulta (Una vez aplicados los filtros).

    En definitiva, el código php se completa con:

    $countSql = "SELECT count(id) as Total FROM products";
    $countSt = $conn
        ->query($countSql);
    
    $total = $countSt->fetch()['Total'];
    
    $sql = "SELECT * FROM products ".($where ?? "WHERE $where ")."$orderBy LIMIT $length OFFSET $start";
    $st = $conn
        ->query($sql);
    
    if ($st) {
        $rs = $st->fetchAll(PDO::FETCH_FUNC, fn($id, $name, $price) => [$id, $name, $price] );
    
        echo json_encode([
            'data' => $rs,
            'recordsTotal' => $total,
            'recordsFiltered' => count($rs),
        ]);
    } else {
        var_dump($conn->errorInfo());
        die;
    }

    El código completo lo podés descargar de aquí.

    DataTables ServerSide FTW!

    En este artículo aprendiste las razones por las que tu página con DataTables tarda mucho en cargar y cómo podés acelerar los tiempos de carga.

    ¿Siempre te conviene usar ServerSide? No

    Si la cantidad de registros es pequeña (O más bien diría si no hay retrasos visibles) no te conviene hacer una implementación con esta complejidad, pero en cambio, si los DataTables se están volviendo un cuello de botella SeverSide puede ser una buena solución.

    Ahora que lo sabés, andá a acelerar esa página que los usuarios están esperando!

  • Construyendo una tabla dinámica con PHP, MySQL, DataTables y Ajax

    Construyendo una tabla dinámica con PHP, MySQL, DataTables y Ajax

    ¿Cuántas veces te enfrentaste a la necesidad de mostrar información resumida en forma de tablas?

    O, puesto de otro modo: ¿qué aplicación no requiere del uso de tablas?

    Por supuesto que se puede usar HTML puro y quedará algo más o menos aceptable… pero con las herramientas (¡y los usuarios!) que tenemos hoy en día no podemos darnos el lujo de presentar una experiencia de usuario aceptable.

    Debemos dar lo mejor que podamos.

    Y lo mejor hoy en día implica interactividad.

    Los usuarios quieren poder buscar, ordenar, paginar… prácticamente tener un Excel directo en su aplicación.

    Ya te estarás imaginando el delirio de javascript que tendrías que ponerte a escribir para hacer todo esto desde cero, ¿no? De sólo pensarlo me dan ganas de retomar el curso de peluquería que dejé a la mitad 🙂

    Afortunadamente no es necesario re-inventar la rueda.

    Como podrás imaginarte, existen ya varios componentes que pueden adquirirse gratis (o a muy bajo costo) y que te ahorrarán muchísimas horas de trabajo y dolores de cabeza.

    DataTables es un plugin de jQuery que resuelve muy bien esta necesidad, desafortunadamente la documentación es algo compleja de seguir…

    Es por eso que me propuse escribir este artículo donde te presento un ejemplo completo de implementación de una tabla dinámica que utiliza DataTables combinado con un script php que actúa a modo de WebService REST para levantar los datos de una base.

    Cómo se ve un DataTable

    Empecemos por lo más importante: ¿sirve un DataTable para resolver tu necesidad?

    Una imagen vale más que mil palabras, así que te muestro el ejemplo de una tabla en la que se visualiza el inventario de un comercio: una tabla en la que se verán los datos de cada uno de los productos (Id, Nombre, Precio):

    No está mal, ¿no?

    Repasemos lo que tenemos:

    • ¿Paginación? Lo tenemos!
    • ¿Búsqueda? Lo tenemos!
    • ¿Ordenamiento? Lo tenemos!

    ¿Qué más se le puede pedir a la vida? 🙂

    Vamos ahora a ver qué hay detrás de la cortina.

    El FrontEnd

    El frontend consta de un archivo HTML que se ve así:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="//cdn.datatables.net/1.10.24/css/jquery.dataTables.min.css">
        <title>DataTables example</title>
    </head>
    <body>
    <h1>Behold... the power of DataTables!</h1>
    <table id="theTable" class="display" style="width: 100%">
        <thead>
            <tr>
                <th>id</th>
                <th>name</th>
                <th>price</th>
            </tr>
        </thead>
        <tfoot>
            <tr>
                <th>id</th>
                <th>name</th>
                <th>price</th>
            </tr>
        </tfoot>
    </table>
    </body>
    </html>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script src="https://cdn.datatables.net/1.10.24/js/jquery.dataTables.min.js"></script>
    <script type="application/javascript">
        $(document).ready( function () {
            $('#theTable').DataTable({
                ajax: '/get_data.php',
            });
        } );
    </script>

    Analicémoslo un poco:

    <link rel="stylesheet" href="//cdn.datatables.net/1.10.24/css/jquery.dataTables.min.css">

    Esta parte incluye la hoja que aporta los estilos que necesita DataTables.

    La parte principal es la tabla:

    <table id="theTable" class="display" style="width: 100%">
        <thead>
            <tr>
                <th>id</th>
                <th>name</th>
                <th>price</th>
            </tr>
        </thead>
        <tfoot>
            <tr>
                <th>id</th>
                <th>name</th>
                <th>price</th>
            </tr>
        </tfoot>
    </table>

    Es importante que la tabla esté bien formada (Que tenga su sección thead, el tfoot es opcional).

    No hace falta que le pongas un tbody, lo hace todo el plugin.

    Luego tenemos el código JavaScript:

    <script type="application/javascript">
        $(document).ready( function () {
            $('#theTable').DataTable({
                ajax: '/get_data.php'
            });
        } );
    </script>

    Es muy sencillito, se trata de invocar una función cuando el documento está cargado por completo.

    Usando jQuery eso se logra con:

    $(document).ready();

    Y lo que ves dentro es el callback que se invocará cuando este evento ocurra.

    $('#theTable')

    Es la forma de seleccionar el objeto cuyo id es theTable.

    En este caso se trata, obviamente, de la tabla que definimos en el HTML.

    Y al invocar el método DataTable estamos transformando esa simple tabla en un objeto mucho más complejo.

    Por último, este método recibe un parámetro: un objeto que contiene la configuración de la nueva tabla.

    En nuestro caso se ve así:

    {
       ajax: '/get_data.php'
    }

    Lo cual significa que le estamos pasando una configuración con una única propiedad: ajax y su valor (/get_data.php) es la URL de donde se obtendrán, mediante una llamada ajax, los valores con los que se rellenará la tabla.

    El BackEnd

    Ahora que viste cómo funciona el lado cliente, demos una recorrida por la parte del php:

    <?php
    
    try {
        $conn = new PDO('mysql:host=localhost;dbname=ecommerce','root','root');
    } catch (PDOException $exception) {
        die($exception->getMessage());
    }
    
    $sql = "SELECT * FROM products";
    $st = $conn
        ->query($sql);
    
    if ($st) {
        $rs = $st->fetchAll(PDO::FETCH_FUNC, fn($id, $name, $price) => [$id, $name, $price] );
    
        echo json_encode([
            'data' => $rs,
        ]);
    } else {
        var_dump($conn->errorInfo());
        die;
    }

    En este ejemplo estoy usando PDO aunque bien podría usar MySQLi ya que se trata de una base MySQL.

    La primera parte:

    try {
        $conn = new PDO('mysql:host=localhost;dbname=ecommerce','root','root');
    } catch (PDOException $exception) {
        die($exception->getMessage());
    }

    Intenta realizar una conexión y, en caso de fallar emite un mensaje por pantalla para ayudar con el debugging.

    Con este código:

    $sql = "SELECT * FROM products";
    $st = $conn
        ->query($sql);

    Se intenta hacer la consulta de selección de los datos de los productos de la base.

    Si la consulta fue exitosa buscamos el resultado para enviarlo al FrontEnd respondiendo a la llamada Ajax:

    $rs = $st->fetchAll(PDO::FETCH_FUNC, fn($id, $name, $price) => [$id, $name, $price] );
    
    echo json_encode([
        'data' => $rs,
    ]);

    La primera línea se ve algo extraña, ¿no?

    Paso a explicar 🙂

    Un detalle que puede ahorrarte varios dolores de cabeza es comprender cómo DataTables espera recibir los resultados del webservice que le brinda los datos.

    Se trata de un objeto json con este formato:

    {
       data:[
         [ f1, f2, f3 ],
         [ f1, f2, f3 ],
         [ f1, f2, f3 ],
       ]
    }

    Donde f* corresponde a un campo del registro encontrado.

    En este caso, para que todo salga bien, el php debe responder con algo como:

    {
       data:[
         [ 1, 'Chair', 200.0 ],
         [ 2, 'Table', 500.0 ],
         [ 3, 'Shoes', 450.0 ],
       ]
    }

    Y hay que aclarar: DataTables es muy estricto con esto!

    Es por eso que usé PDO::FETCH_FUNC (Bueno, por eso y porque soy un fanático de la programación funcional) para generar, en base a los registros obtenidos, el arreglo correspondiente.

    Y luego es simple: con la función json_encode se genera el string que se requiere.

    En conclusión

    Espero que este pequeño ejemplo te haya ayudado a comprender lo que podés lograr con este plugin y un poco de php.

    La próxima vez que tengas que mostrar resultados en forma de tabla probalo y comentá cómo te fué 🙂

    Ah! Y si querés descargar el código completo podés hacerlo desde GitHub.

  • ¿Es cierto que PHP está muerto?

    ¿Es cierto que PHP está muerto?

    Leo por ahí cosas como:

    «He vuelto a estudiar PHP porque quiero aprender bien a programar con él y no entiendo por qué siempre se termina hablando sobre si PHP está o no muerto, personalmente lo veo muy vivo pero cuando veo tanto vídeo de ese estilo dudo un poco en seguir estudiando PHP«

    «No entiendo mucho de back pero un amiguete siempre me dice que PHP está muerto.«

    Y no puedo dejar de dar mi visión 🙂

    Claramente la pregunta esconde otra, mucho más importante:

    ¿Vale la pena dedicar tiempo y esfuerzo a aprender php?

    Es claro que, si efectivamente PHP estuviese muerto (o muriendo) tendría poco sentido… y enfatizo la palabra poco porque hasta el viejo COBOL tiene su cuotita de mercado que, al menos desde un punto de vista financiero, no es nada despreciable.

    Pero vamos al punto central: ¿está muerto php o no?

    Mi respuesta corta es un rotundo NO, pero voy a intentar fundamentar un poco.

    Para empezar, PHP es el lenguaje que está detrás de nada más ni nada menos que WordPress.

    Podés pensar lo que quieras de WordPress (Yo mismo no soy ningún fanático), pero lo que no podés es ignorar que la enorme mayoría de los sitios web existentes se apoyan en esta tecnología.

    Sólo por eso yo diría que PHP todavía tiene unos cuántos años por delante.

    Claro que, si sólo de WordPress se tratara, bien podría entrar en una suerte de animación suspendida.

    La comunidad de programadores entera podría ponerse de acuerdo en dejar todo como está y simplemente mantener los sitios andando y listo.

    La realidad es bastante diferente.

    Al momento de escribir este post la versión que viene con una nueva instalación de Ubuntu es la 8.0 y ya se está discutiendo qué tendrá la 8.1.

    En cuanto a performance la curva viene en franco ascenso, con un salto muy marcado a partir de la versión 7.

    Los frameworks están super activos, hay conferencias internacionales por todos lados (Cada vez más).

    En definitiva: yo no veo señales de que PHP esté muerto ni mucho menos… más bien todo lo contrario.

    ¿Qué tan popular es PHP?

    Si bien no es exactamente el tema de este post, es una pregunta bastante relacionada así que intentaré contestarla.

    Para empezar, esta medición es bastante subjetiva.

    Alguna gente mida la popularidad de un lenguaje por la cantidad de preguntas que se hacen al respecto de él en sitios como StackOverflow.

    Otros por la cantidad de anuncios de trabajo publicados en sitios de empleo.

    Otra métrica posible sería cantidad de descargas de proyectos de GitHub basados en un lenguaje en particular (PHP en este caso).

    Como para resumir voy a tomar un gráfico de gente que hizo alguno de estos análisis:

    Está claro que PHP no es a día de hoy la opción más popular o sexy (aunque está definitivamente dentro del top 10), pero aún así…

    ¿Es importante elegir un lenguaje propular?

    La última pregunta que deberías hacerte al elegir un lenguaje es qué tan importante o conveniente es decantar por la opción más popular.

    La popularidad es efímera.

    Mirá este video por ejemplo:

    Más allá de las preferencias/gustos que podemos tener nosotros como técnicos, para las empresas no es muy simpático tener que re-escribir sus aplicaciones cada dos años, con lo cual no descartaría un lenguaje que tiene tanta penetración cómo PHP y que ha estado vivo por más de 25 años (y sigue en evolución constante).

    Si te quedan ganas de participar en un debate bastante acalorado que se armó a partir de este tema te invito a ver este meme que armé en LinkedIn.


    Ahora que tenés una idea más clara tal vez te gustaría saber por qué tantos programadores odian PHP.

  • ¿Cuál es la diferencia entre PDO y MySQLi?

    ¿Cuál es la diferencia entre PDO y MySQLi?

    Si me decís que estás desarrollando una aplicación web usando PHP y no me das más datos, cerrando los ojos diría que estás usando una base de datos MySQL y, seguro que en el 80% de los casos acertaría.

    Es un hecho: PHP y MySQL son un dúo muy común en este mundillo.

    Lo que no está tan definido es cuál es el mejor modo de conectarse a MySQL utilizando PHP.

    A priori destacan dos:

    Ambos pueden usarse para armar CRUDs, reportes y cualquier otra necesidad que implique comunicar una aplicación PHP con una base de datos MySQL.

    Entonces… ¿cómo elegir?

    Para hacerte (algo) más fácil la tarea te voy a comentar brevemente de qué se trata cada uno.

    Qué es MySQLi

    MySQLi es una extensión de PHP creada con el propósito de facilitar la comunicación entre un script PHP y un servidor MySQL.

    Es la heredera de la vieja y querida extensión MySQL que ya ha quedado obsoleta.

    MySQLi hizo su aparición junto con la versión 4.1 de MySQL.

    Lo más probable es que nunca te cruces con un servidor que utiliza una versión anterior (Al día en que estoy escribiendo este artículo la versión de MySQL es la 8.0) pero… si llegara a pasar, vas a tener que arreglártelas con la extensión mysql (sin la «i»).

    Su nombre precisamente viene de MySQL improved (Mejorada).

    Una característica interesante de esta extensión es que muestra una API Orientada a Objetos y también una estructurada.

    Por ejemplo, para establecer una conexión a un servidor podés usar tanto:

    <?php
    $link = mysqli_connect("localhost", "root", "", "mydb");

    Como

    <?php
    $mysqli = new mysqli('localhost', 'root', '', 'mydb');

    Si estás migrando un sistema que usaba la vieja extensión y no tenés mucho tiempo para hacer grandes cambios es trivial actualizar al uso de la nueva librería: basta agregar una «i» al nombre de las funciones que venías usando.

    Claro que, si querés dar un salto, te recomiendo hacer el esfuerzo de escribir el código usando objetos.

    Qué es PDO

    PDO también es una extensión pero, a diferencia de MySQLi, PDO está diseñada como una capa de abstracción sobre una amplia cantidad de motores específicos.

    Para que se entienda mejor, esta es la relación entre PDO y los motores específicos:

    Cuál es la ventaja de usar PDO en lugar de MySQLi

    La ventaja principal de usar PDO en lugar de MySQLi es que, llegado el caso de decidir migrar a un motor de base de datos diferente del elegido originalmente, el cambio es trivial.

    Por ejemplo, si tuvieses que pasar de MySQL a SqlServer deberías cambiar:

    $conn = new PDO('mysql:dbname=mydb;host=127.0.0.1');

    Por

    $conn = new PDO('mssql:dbname=mydb;host=127.0.0.1');

    Y el resto de tu código seguiría funcionando sin alteraciones.

    Cuál es la ventaja de usar MySQLi en lugar de PDO

    Pero si PDO es tan potente y versátil… ¿no hay lugar para MySQLi en la actualidad?

    La verdad es que sí lo hay (a diferencia de lo que muchos creen).

    La versatilidad de PDO es un arma de doble filo.

    Como sucede con cualquier solución genérica, quien mucho abarca poco aprieta 🙂

    En términos técnicos, lo que ocurre es que PDO no puede aprovechar las pequeñas diferencias existentes entre los diferentes motores, lo cual lo hace menos eficiente que MySQLi en ciertas situaciones.

    Por otro lado, una capa más de abstracción agrega más overhead.

    Cómo decidir si usar PDO o MySQLi

    No quiero que te vayas de aquí con una confusión mayor de la que traías al comienzo, así que preparé este sencillo diagrama de flujo para ayudarte a tomar la decisión, espero te sirva: