Categoría: Cómo hacer para…

Estos artículos te explicarán cómo resolver problemas específicos usando PHP

  • Cómo pasar una variable PHP a JavaScript

    Cómo pasar una variable PHP a JavaScript

    Un problema muy común es cómo hacer para utilizar información que se tiene en PHP en la ejecución de código JavaScript.

    Detrás de esta inocente pregunta sin embargo, se esconde un error conceptual similar al que tratamos en este artículo: no se domina por completo el esquema de ejecución de una aplicación web.

    Es entendible: la confusión se produce porque en muchas ocasiones se escribe un único texto que se ejecutará en diferentes lugares y momentos. Es un poco como jugar a este juego:

    Para entenderlo bien veamos un ejemplo simple:

    <table>
    <?php
    for ( $i = 0; $i < 5; $i++ ) {
       ?>
       <tr>
          <td><?php echo $i; ?></td>
       </tr>
       <?php
    }
    ?>
    </table>

    Este código está mezclando puro código HTML con PHP, pero el PHP se ejecuta en el servidor, mientras que el HTML es interpretado del lado del cliente

    Recordemos cómo es que funciona un pedido de este estilo:

    El cliente solicita un archivo llamado «pagina.html», pero lo que recibirá no será su contenido si no el resultado de la interpretación de ese archivo por PHP.

    Y ¿qué hace PHP con ese archivo? Todo lo que no esté encerrado entre <?php y ?> será emitido literalmente como se encuentra en el archivo original, mientras que el texto que está entre <?php y ?> será analizado según las reglas del lenguaje y, si genera alguna salida, será adicionado a la salida que se estaba generando.

    En definitiva, después de pasar por el intérprete de PHP, la salida generada será:

    <table>
      <tr>
        <td>0</td>
      </tr>
      <tr>
        <td>1</td>
      </tr>
      <tr>
        <td>2</td>
      </tr>
      <tr>
        <td>3</td>
      </tr>
      <tr>
        <td>4</td>
      </tr>
    </table>
    

    Y será eso lo que el cliente (el navegador probablemente) interprete y genere algo como:

    0
    1
    2
    3
    4

    Bien… y ¿qué hay del JavaScript?

    JavaScript es un lenguaje de programación que se ejecuta del lado del cliente (Salvo en casos como NodeJs, pero para nuestro ejemplo diremos que lo usaremos en su versión clásica).

    Esto quiere decir que, desde el punto de vista del servidor, HTML y JavaScript son indistinguibles (O mejor dicho, la diferencia entre ellos es irrelevante): para el servidor, todo lo que no es PHP es texto que debe ser enviado en forma literal al cliente (Y será el cliente el responsable de decidir qué quiere hacer con ese texto).

    De modo que hacer algo como:

    <?php 
    
    $a = "Hola Mundo!";
    ?>
    <script type="text/javascript">
    alert( $a );
    </script>

    No va a dar el efecto que buscamos (Mostrar en la pantalla del cliente el mensaje «Hola Mundo!» ya que la variable $a es conocida al momento de ejecutar PHP (Es decir, del lado del servidor), mientras que el alert es JavaScript (se ejecutará del lado del cliente).

    Para hacerlo más claro, lo único que el cliente recibirá es:

    <script type="text/javascript">
    alert( $a );
    </script>

    Ya que PHP se ejecutó antes (Cuando se estaba generando la respuesta) y, como todo este código está fuera de los tags de apertura y cierre de PHP, es enviado así como está.

    ¿Cómo se puede resolver este problema? La clave está en comprender bien los tiempos en los que cada evento sucede.

    Si pensamos en que JavaScript es parte del texto que se envía al cliente y que PHP es una herramienta para generar ese código… ¿por qué no generar texto JavaScript usando PHP?

    Algo como:

    <?php 
    
    $a = "Hola Mundo!";
    ?>
    <script type="text/javascript">
    alert( "<?php echo $a; ?>" );
    </script>

    De esta forma lo que recibe el cliente es:

    <script type="text/javascript">
    alert( "Hola Mundo!" );
    </script>

    En definitiva, si lo mirás con detenimiento notarás que no estamos realmente pasando la variable $a a JavaScript, si no que estamos usando su valor (El que tenía al momento de ejecutar PHP) hacia JavaScript para poder usarla (que no es lo mismo pero se acerca bastante 🙂 )

    No es del todo infrecuente usar este tipo de trucos para generar código JavaScript en forma dinámica… sólo hay que ser muy consciente de qué parte de nuestro código se ejecutará en cada momento (Para esto ayuda mucho tener un buen IDE a mano).

    ¿Te quedó alguna pregunta? Me encantará leerla en los comentarios 😉

  • Cómo ser el primero en enterarse de los errores de tu aplicación web

    Cómo ser el primero en enterarse de los errores de tu aplicación web

    Cuántas veces te pasó que te mande un mail un cliente (o peor, te llame por teléfono) para decirte que la aplicación que pusiste en producción hace más de una semana acaba de dar uno de esos errores inentendibles:

    Y vos, estando en cualquier otro tema tenés que buscar en lo más recóndito de la memoria para recordar de qué se trataba y contestar algo medianamente coherente…

    ¿No sería genial poder decirle a tu cliente apenas atender: «Sí, ya sé, hubo un problema con la app, ya lo estoy viendo y enseguida lo tenés solucionado»?

    Hay varios puntos para señalar en este escenario:

    1. Más allá de que «queda feo» que un cliente vea un mensaje en inglés cuando el resto de la aplicación está en castellano (o que vea una pantalla con fondo blanco y despojada de todo branding), esto da a un potencial atacante información que no tiene por qué tener: que tu aplicación está desarrollada usando Symfony1 (y por lo tanto PHP).
    2. Se pierde un tiempo valioso buceando en los logs de errores hasta entender qué pudo haber pasado. Asumiendo que tales logs existan, claro.

    Algo que mejora tanto la seguridad de la aplicación como la experiencia del usuario es simplemente contar una página de error personalizado:

    Fácilmente puede especificarse qué hacer en caso de que la respuesta a enviar no sea un código 200 usando configuración de Apache, NginX o el webserver que sea.

    En este post te voy a mostrar cómo lograrlo sin tocar nada desde la infraestructura, si no todo dentro de tu propio proyecto.

    Lo voy a hacer usando el framework Symfony. Si estás usando otro no va a ser tan literal el tema, pero las ideas te servirán igual:

    En Symfony los errores HTTP se manejan mediante Excepciones, para cada tipo de error existe un template que se utiliza para generar la respuesta a enviar.

    Todo esto en realidad no es de Symfony si no de Twig (el motor de templating que viene junto con el framework).

    Dentro del código de una aplicación Symfony típica vas a encontrar un directorio vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/ donde se encuentran los templates usados en caso de error.

    Como en cualquier otro caso en que necesites modificar comportamientos estándar de los bundles, lo más conveniente es crear tu propia copia de los archivos originales dentro del directorio app/Resources de tu aplicación. En este caso sería el directorio app/resources/TwigBundle y dentro de él views/Exception/

    Hasta aquí resolvimos el problema de lo que el usuario ve pero… ¡todavía sos el último en enterarse de lo que pasó!

    Bien… ¿cómo podrías enterarte de algo que está sucediendo en el servidor sin estar pegado al log de errores 24×7?

    ¿Qué tal una alerta por email?

    Lo primero que podrías pensar es en codear tu propio mecanismo de envío de correos ante la aparición de un error… y por ahí hasta lo lograrías. Pero, como nos gusta decir por aquí, ¿para qué reinventar la rueda?

    ¿Te acordás de lo que te conté sobre Monolog? Vamos a ver cómo se puede usar una de sus características más interesantes: la composición de loggers (o handlers en terminología Monolog).

    De lo que se trata acá no es ni de reemplazar el logging tradicional por el mail ni de llenarte la casilla de mensajes crípticos, si no de tener lo mejor de ambos mundos (Una alerta a tiempo con la información necesaria para reaccionar y los logs completos donde deben estar).

    Una forma de lograr esto es usando inteligentemente la configuración Monolog y de SwiftMailer. Estas configuraciones las encontrás en el archivo app/config/config.yml(y por supuesto en sus hijos config_dev.yml y config_prod.yml).

    Del lado de Monolog debería quedar algo como:

    monolog:
        handlers:
            main:
                type:         fingers_crossed
                action_level: error
                handler:      grouped
            grouped:
                type:       group
                members:    [streamed, deduplicated]
            streamed:
                type:  stream
                path:  %kernel.logs_dir%/%kernel.environment%.log
                level: debug
            deduplicated:
                type: deduplication
                handler: swift
            swift:
                type:           swift_mailer
                from_email:     app@dominio.com
                to_email:       mauro.chojrin@leewayweb.com
                subject:        "App::Error_Exception"
                level:          error
                formatter:  monolog.formatter.html
                content_type: text/html
            console:
                type:  console

    Lo que estamos haciendo acá es decirle a Monolog (a través del sistema de configuración de Symfony) que cada mensaje debe ser procesado por el handler grouped que, como podrás ver, es de tipo group (Una forma muy elegante de hablar de un conjunto de loggers).

    El primero de esos loggers es el clásico: archivo de texto local (Se podría hacer algo más complejo si tuvieses que sincronizar logs de diferentes servidores, pero lo dejamos para otro momento).

    El segundo logger es más interesante: deduplicated (Una suerte de filtro para no llenarte la casilla con el mismo mensaje enviado 1000 veces). Este a su vez usa un handler propio: swift.

    El handler swift es el que efectivamente se encarga de realizar el envío del error a la casilla que le indiques… en realidad, de lo que se encarga es de pedirle a SwiftMailer que haga todo eso.

    Y ahí es donde entra en juego la configuración propia de SwiftMailer… después de todo, esto tampoco es mágico.

    Lo más importante acá es a través de qué medio se va a realizar el envío, es decir qué tipo de transport vas a querer usar.

    En mi ejemplo usé una cuenta de Gmail creada especialmente para esta aplicación (Total son gratis y andan muy bien :)).

    La configuración al final es esta:

    swiftmailer:
        host: smtp.gmail.com
        username:  '%mailer_user%'
        password:  '%mailer_password%'
        encryption: ssl
        port: 465

    Al usar el username '%mailer_user%' y el password '%mailer_password%' lo que estoy diciendo es que los valores de esta configuración se obtengan del archivo app/config/parameters.yml. Como este archivo es específico para cada entorno donde la aplicación esté desplegada (léase: ¡no lo versiones!), esta configuración permite usar diferentes cuentas para desarrollo y para producción.

    Lo único que debés tener en cuenta si vas a usar Gmail (En lugar de algún otro SMTP público) es que vas a tener que permitir el acceso a la cuenta a aplicaciones no seguras.

    Por último, para recibir un mensaje que se vea así:

    En lugar de así:

    Se usan las líneas:

    formatter: monolog.formatter.html

    content_type: text/html

    De la configuración de Monolog.

    Y ahora sí, todo listo, si llega a producirse un error inesperado, el mismo sistema te va a avisar con un email bien formateado.

    Y si todavía querés llevar el tema a un nivel superior, una herramienta sumamente interesante que te recomiendo usar es Honeybadger.

    Con Honeybadger podés tener un panel de administración donde se centralicen todos los problemas u otros mensajes que quieras que tu aplicación envíe.

    De esa forma, la gestión de los problemas puede hacerse todavía más eficiente.

    Te recomiendo que le des una mirada.

  • Cómo consumir un WebService REST con PHP

    Cómo consumir un WebService REST con PHP

    La conexión de tus aplicaciones php con WebServices REST te permitirá ampliar tus capacidades apoyándote en servicios de grandes compañías.

    Los detalles específicos dependen de las definiciones que haya realizado el productor del servicio pero, para no encontrarte con sorpresas, es buena idea tener claras las bases.

    Repasemos los conceptos comunes a todo Servicio Web REST.

    Qué es un webservice

    Un webservice (o servicio web) es una forma de integrar aplicaciones web.

    Básicamente se trata de un servidor que expone parte de su funcionalidad para que sus clientes (que generalmente son otros servidores de diferentes aplicaciones) puedan utilizarlas.

    Las principales ventajas de usar web services son:

    1. La posibilidad de valernos de la capacidad instalada (¡y mantenida!) por terceros
    2. La facilidad para extender la funcionalidad de nuestra aplicación (Basta con realizar las llamadas al servicio web que deseamos)

    La principal desventaja de usar este enfoque es que dependemos de servicios de terceros que, salvo en casos muy puntuales, no podemos controlar.

    Ejemplos de webservices:

    1. Autenticación de usuarios vía Facebook, Google, Twitter, etc…
    2. Generación de mapas en tiempo real
    3. Consulta de cotizaciones de acciones

    Qué es RESTful

    RESTful es un modelo arquitectónico de software que plantea una suerte de volver a las fuentes.

    Su filosofía se basa en el poder del protocolo HTTP (Subyacente en toda aplicación web), el cual incluye los conceptos de verbo (GET, POST, DELETE, PUT), recurso, código de errores (404, 500, etc…) y demás elementos que, en teoría al menos, deberían alcanzar para modelar cualquier tipo de interacción cliente-servidor.

    Es muy común que en una aplicación RESTful los objetos o entidades del modelo de datos sean expuestos casi directamente, teniendo una URL específica para cada uno de ellos.

    Por ejemplo, en una aplicación diseñada para una institución educativa sería esperable encontrarse con una URL de tipo:

    http://miescuela.com/alumnos

    Al realizar una petición de tipo GET a esta URL se esperará recibir información de los alumnos de esa institución.

    A su vez, una URL del tipo:

    http://miescuela.com/alumnos/14

    Debería darme información del alumno cuyo id es 14.

    Existe mucha controversia respecto de qué es una API RESTful y qué no lo es…

    Sin entrar en mayor detalle, la idea es que, para consumir un servicio web basado en REST sólo se requiere conocer su URL (De nuevo, esto es en teoría… en la práctica se utilizan varias formas de modificación del pedido, como ser el envío de headers y demás).

    Siendo que se trata de un servicio RESTful, la comunicación entre cliente y servidor es sencilla (A diferencia de lo que es un WebService SOAP), se trata simplemente de enviar un pedido HTTP y procesar su respuesta.

    Cómo realizar peticiones REST con PHP

    Existen varias opciones disponibles, veamos las más comúnmente utilizadas:

    cURL

    La librería cURL permite realizar peticiones a servidores remotos.

    Si bien funciona, es un método de bastante bajo nivel, con sus pros y sus contras.

    Volviendo al ejemplo que te comentaba antes, una forma de hacer la llamada sería esta:

    <?php
    $ch = curl_init();
    
    curl_setopt($ch, CURLOPT_URL, "http://miescuela.com/alumnos");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $res = curl_exec($ch);
    
    curl_close($ch);

    En la variable $res quedará la respuesta del webservice (Asumiendo por supuesto que no haya habido ningún problema de conexión, transferencia, etc…).

    file_get_contents

    Una opción alternativa (y generalmente más conveniente) es el uso de la función file_get_contents.

    Esta función recibe como parámetro el nombre del archivo que se quiere leer y devuelve todo su contenido como un string.

    Si bien el uso normal de la función es la lectura de archivos locales, php en realidad entiende a los archivos como flujos. Por lo tanto, si está todo bien configurado, una URL puede usarse como nombre de un flujo.

    De este modo nos ahorramos muchas de las complicaciones propias de tratar con una interface de más bajo nivel (y, obviamente, perdemos cierto control, pero generalmente no es necesario hilar tan fino).

    El ejemplo sería:

    <?php
    $res = file_get_contents("http://miescuela.com/alumnos");

    Cómo se procesan los resultados

    Los resultados se procesan de acuerdo al formato de la respuesta.

    Usualmente los Servicios Web devuelven XML o JSON (Existen otras posibilidades, claro, pero estas son las más comunes).

    En el caso de tratarse de un XML, lo mejor es usar la biblioteca SimpleXML, si se trata de json, con json_decode será suficiente.

    Si se trata de otro formato habrá que estudiar el caso puntual, pero en última instancia, siempre se trata de procesar un string…

    Ejemplo de consumor de un WebService REST con PHP

    MercadoLibre tiene una API RESTful que permite interactuar con el sitio de forma simple.

    Veamos un ejemplo de una llamada de acceso público: la que nos da información básica de un usuario.

    La URL que se utiliza es https://api.mercadolibre.com/users/USERID/

    Por ejemplo https://api.mercadolibre.com/users/226384143/ retorna:

    {
      "id": 226384143,
      "nickname": "TETE9928972",
      "registration_date": "2016-08-25T11:36:00.000-04:00",
      "country_id": "AR",
      "address": {
        "city": "Palermo",
        "state": "AR-C"
      },
      "user_type": "normal",
      "tags": [
        "normal",
        "test_user",
        "user_info_verified"
      ],
      "logo": null,
      "points": 100,
      "site_id": "MLA",
      "permalink": "http://perfil.mercadolibre.com.ar/TETE9928972",
      "seller_reputation": {
        "level_id": null,
        "power_seller_status": null,
        "transactions": {
          "canceled": 0,
          "completed": 0,
          "period": "historic",
          "ratings": {
            "negative": 0,
            "neutral": 0,
            "positive": 0
          },
          "total": 0
        }
      },
      "buyer_reputation": {
        "tags": []
      },
      "status": {
        "site_status": "active"
      }
    }

    Un string JSON.

    El código completo para obtener esta información con php es:

    <?php
    
    echo file_get_contents('https://api.mercadolibre.com/users/226384143/');

    Claro que si quisiéramos hacer algo con esta información sería más conveniente hacer algo como:

    <?php
    
    $data = json_decode( file_get_contents('https://api.mercadolibre.com/users/226384143/'), true );
    
    echo $data['nickname'];

    ¡Listo! Ya estás en condiciones de integrar cualquier WebService RESTful en tu aplicación.

    Más información sobre WebServices con PHP

    Como te decía al comienzo, otro protocolo muy utilizado a la hora de conectar aplicaciones a través de Servicios Web es SOAP. Este protocolo es bastante más complejo que RESTful, principalmente porque se basa en intercambios de XML con un formato bastante particular.

    Puedes intentar leer e interpretar estos XML por tus propios medios aunque lo más recomendable es utilizar las herramientas que PHP ofrece para ello.

    De hecho, si te enfrentas a WebServices de basados en SOAP php tiene un soporte nativo muy completo.

  • Cómo usar CC y BCC con PHPMailer

    Cómo usar CC y BCC con PHPMailer

    ¿Qué es PHPMailer?

    Comencemos por el principio: PHPMailer es una librería que permite enviar emails desde PHP (Podés consultar algunas opciones acá).

    Si bien no es la única (De hecho, mi preferida es SwiftMailer), su principal ventaja es que suele estar disponible en entornos de hosting compartido, con lo cual, su uso es bastante popular.

    ¿Cómo se usa?

    Su uso es bastante simple: basta con crear una instancia de PHPMailer para tener acceso a una gran cantidad de funcionalidad:

    <?php
    use PHPMailer\PHPMailer\PHPMailer;
    use PHPMailer\PHPMailer\Exception;
    
    require_once 'vendor/autoload.php';
    
    $mail = new PHPMailer();
    
    $mail->isSMTP();                                      
    $mail->Host = 'mail.google.com';
    $mail->SMTPAuth = true;                               
    $mail->Username = 'usuario@gmail.com';                
    $mail->Password = 'miSuperPassword';                           
    $mail->SMTPSecure = 'tls';                            
    $mail->Port = 587;                                   
    
    $mail->setFrom('acedmy@leewayweb.com', 'Leeway Academy');
    $mail->addAddress('juan.perez@yahoo.com', 'Juan Perez'); 
    
    $mail->Subject = 'Este es el asunto';
    $mail->Body    = 'Este el cuerpo del mensaje';
    
    if(!$mail->send()) {
        echo 'No se pudo enviar el mensaje...'.$mail->ErrorInfo;
    } else {
        echo 'El mensaje se envió!';
    }

    Claro que, antes de poder usarla, la librería debe estar instalada… En este ejemplo vemos cómo se utiliza asumiendo que se ha usado composer para incorporarla al proyecto.

    Para agregar otros destinatarios en copia (CC) o copia oculta (BCC), simplemente debemos agregar estas líneas:

    $mail->addCC('copiado@hotmail.com');
    $mail->addBCC('copia_oculta@outlook.com');

    Antes de realizar el envío.

    Por útlimo, como siempre, no hay que olvidar el $mail->send().

    ¿Alguna pregunta? ¡Deja un comentario!

     

  • Cómo definir la configuración de la sesión en Symfony

    Cómo definir la configuración de la sesión en Symfony

     

    Ante todo, una aclaración:

    PHP maneja las sesiones a través de cookies (Antiguamente también se podía propagar el ID de sesión vía URL, aunque es una práctica muy poco segura y, sinceramente, hace mucho que no lo veo).

    Bien, ahora… ¿qué cosas podrías querer cambiar de la configuración de la sesión? Varias.

    1. El nombre de la cookie
    2. El tiempo de duración
    3. El lugar donde se almacena la información del lado del servidor

    Sobre la segunda y la tercera, acá tenés un ejemplo de por qué querrías hacerlo 🙂

    Respecto de la primera, más que nada se trata de un tema de seguridad. Fijate esta captura de pantalla de la consola del navegador:

    El nombre PHPSESSID es el nombre por defecto que se le asigna a la cookie de sesión de una aplicación hecha en PHP, eso significa que, si te encontrás con una cookie con este nombre es altamente probable (Por no decir seguro) que del otro lado se encuentre una aplicación desarrollada en PHP.

    Ese tipo de información es super útil para un atacante, ya que al conocer qué software está corriendo el servidor se le facilita enormemente la tarea de buscar una forma de explotarlo…

    Bien, ahora que te convencí de que es una buena idea cambiar el nombre de la cookie, te voy a mostrar cómo hacerlo en Symfony:

    Es muy simple, se trata de agregar un par de líneas al archivo config.yml:

    framework:
        session:
            # http://symfony.com/doc/current/reference/configuration/framework.html#handler-id
            handler_id:  session.handler.native_file
            save_path:   "%kernel.root_dir%/../var/sessions/%kernel.environment%"
            cookie_lifetime: 28800
            name: MIAPPSID

    Se puede configurar todos los parámetros de la sesión mediante este mecanismo y después, por ejemplo, se puede modificar algunos valores para el entorno de desarrollo definiendo un archivo config_dev.yml con algo como:

    imports:
        - { resource: config.yml }
    
    framework:
        session:
            save_path:   "/var/lib/php/sessions"

    Listo. Ahora tu aplicación es un poco menos vulnerable que antes :).

    P.D.: Si querés incorporar un framework maduro como Symfony a tu herramental, el curso Desarrollo de Aplicaciones Web Profesionales con PHP te puede interesar.

  • Cómo enviar mails con formato usando SwiftMailer

    Cómo enviar mails con formato usando SwiftMailer

    Ya he comentado en otros artículos sobre las diferentes posibilidades para enviar emails usando PHP. De todas las que he probado hasta el momento, SwiftMailer es la que me resulta más cómoda y conveniente, sin embargo, he tenido algunas peleas a la hora de enviar mails con formato (es decir, mails que contengan HTML que necesito que sea entendido como tal y no como mero texto).

    La verdad es que es bastante simple lograrlo, sólo hay que conocer el método adecuado 🙂

    En general, lo que uno hace cuando quiere enviar un email es algo como esto:

    $message = (new Swift_Message())
      ->setSubject('Este es el asunto')
      ->setFrom(['mauro.chojrin@leewayweb.com' => 'Mauro Chojrin'])
      ->setTo(['destinatario@dominio.com' => 'Gran Amigo'])
      ->setBody('Este es el importante mensaje que quiero enviarte!!')
      ;

    El problema (si puede llamársele así) es que nos hemos olvidado que el mail acompaña a Internet desde sus inicios, con lo cual, el modo default de envío de correos es texto plano (Aunque muy poca gente lo utilice así hoy en día).

    Para enviar correos utilizando HTML se debe agregar una parte al correo que así lo especifique. Es decir, en lugar de usar setBody debemos usar addPart:

    $message = (new Swift_Message())
      ->setSubject('Este es el asunto')  ->addPart('<p>Este es el <strong>importante</strong> mensaje que quiero enviarte!!</p>')
      ->setFrom(['mauro.chojrin@leewayweb.com' => 'Mauro Chojrin'])
      ->setTo(['destinatario@dominio.com' => 'Gran Amigo'])
      ->addPart('<p>Este es el <strong>importante</strong> mensaje que quiero enviarte!!</p>')
      ;

    Si queremos ser muy puritanos, deberíamos incluir ambas versiones (La HTML y la de texto plano) para no perder a aquellas personas que no tienen habilitado el HTML en su cliente de correo (Aunque sinceramente, es muy raro que así sea y si queremos lograr un buen efecto visual, nada como el HTML, CSS y demás).

  • Cómo definir relaciones Cero-a-Uno con Doctrine

    Cómo definir relaciones Cero-a-Uno con Doctrine

    En general cuando uno comienza a estudiar Bases de Datos Relacionales se habla de un tipo de relación Uno-a-Uno que, en la práctica se usa muy poco. Sin embargo las relaciones tipo Cero-a-Uno tienen muchísimo sentido.

    Es el caso de que se quiera modelar un sistema donde una entidad es un caso especial de otra (Algo similar al concepto de herencia de POO), como por ejemplo la relación entre personas y actores:

    • Todos los actores son personas
    • No todas las personas son actores

    La forma de implementar esto en una base de datos relacional es poner un campo tipo clave foránea en la tabla hija, el cual a su vez tendrá un índice único (Para reforzar la cardinalidad de la relación).

    En el caso del ORM Doctrine, todo esto parte de la definición de la clase Entity.

    Pongo un ejemplo de un proyecto en el que trabajé: hay una clase User (De FOSUserBundle) y una clase Taller. Cada taller tiene un responsable, pero no todos los usuarios son responsables de algún taller.

    En la clase User está este código

    /**
     * @var Taller
     * @ORM\OneToOne(targetEntity="AppBundle\Entity\Taller",mappedBy="responsable")
     */
    protected $taller;

    Y en la clase Taller este:

    /**
     * @var User
     * @ORM\OneToOne(targetEntity="AppBundle\Entity\User", inversedBy="taller")
     */
     private $responsable;

    Como podés observar, ambas clases tienen una referencia a la otra, pero en la base de datos, sólo la tabla Taller tiene una referencia a User.

    Esto se debe a cómo están escritas las definiciones del vínculo OneToOne.

    Más específicamente, se debe a qué clase tiene el mappedBy y el inversedBy: El lado 1 de la relación (es decir, la entidad que siempre existirá) es el que debe llevar inversedBy.

    Si bien este tipo de relaciones no son lo más común, ciertamente viene bien conocerlas y la posibilidad de definirlas usando las herramientas de Doctrine ayuda mucho.

    ¿Alguna duda? ¡Dejala en un comentario!

  • Cómo usar el intérprete de PHP de una VM desde PhpStorm

    Cómo usar el intérprete de PHP de una VM desde PhpStorm

    Una práctica muy buena a la hora de programar (y ahorrarse dolores de cabeza) es el uso de máquinas virtuales.

    Muchos desarrolladores usan este esquema, sin embargo, también es bastante común que tengan sus propias versiones del software usado para ejecutar sus aplicaciones instalado en su máquina física (por ejemplo PHP).

    Si estás trabajando en varios proyectos a la vez, los cuales están deployados en servidores diversos, es muy probable que en cada VM tengas instalada una versión diferente de PHP (La misma que tenés en el servidor de producción), con lo cual, al menos una VM debe tener instalada una versión diferente de PHP que la que tenés instalada en tu máquina.

    Como usás una VM no deberías tener mayores dificultades a la hora de deployar, pero a la hora de desarrollar, también sería bueno que tu IDE estuviese ejecutando la misma versión de PHP que va a dar vida a tu código…

    Una característica muy interesante de mi IDE preferido (PhpStorm) es la posibilidad de analizar el código (mientras lo escribo) usando el intérprete de PHP que está en la VM (y no el que tiene el propio IDE o el que tengo instalado en mi computadora).

    Para configurarlo tenés que ir a la pantalla de settings:

    Y configurar el intérprete de línea de comandos (CLI Interpreter):

    Por defecto vas a ver a la izquierda los intérpretes que tengas instalados en tu computadora, pero podés agregar uno:

    Que puede ser accedido remotamente (Por ejemplo a través de Vagrant).

    Obviamente, para que esto funcione la VM de la que querés sacar el intérprete tiene que estar encendida…

    Y listo, con esto te asegurás de tener un IDE que entienda exactamente la versión de PHP que vas a usar en tu entorno de desarrollo y en producción.

    Si querés ver más detalles podés consultar acá.

  • Cómo hacer debug en php

    Cómo hacer debug en php

    Una de las tareas que más tiempo consume durante el desarrollo de una aplicación (web o no) es el debugging. Ese momento en que estás seguro de que todo anda bien, pero por las dudas… hay que probarlo.

    Y, como siempre, algo no sale exactamente como esperabas.

    Es entonces cuando te toca arremangarte, buscar una nueva taza de café, silenciar el teléfono y hurgar.

    El modo «común» de encarar esta tarea en el universo php es usar mucho la función var_dump (o sus parientes cercanos: print_r, echo, etc…).

    La verdad es que, si bien eso puede funcionar, está bastante lejos de ser óptimo.

    Herramientas de debugging PHP

    Una herramienta que vengo usando hace bastante y que ha sido el día y la noche en mi trabajo es xdebug.

    Se trata de un complemento para php que permite, entre otras cosas:

    • Ejecutar el código paso por paso
    • Evaluar el contenido de las variables en tiempo real
    • Establecer puntos de corte

    Históricamente instalarlo (y dejarlo funcionando!) era una tarea sólo apta para valientes… hoy en día, por suerte, las cosas son mucho más simples.

    Para usarlo de una forma cómoda y práctica necesitarás a su vez un IDE.

    En este caso, te mostraré cómo se hace con PhpStorm pero realmente es muy similar en otros.

    Cómo configurar PHPStorm para usar XDebug

    Desde dentro del IDE podés lanzar tu aplicación usando el botón de play (El triángulo verde):

    Lo que ves a la izquierda (En este caso app.php) es el nombre de la configuración de ejecución (Un concepto propio de PhpStorm que define un poco qué es lo que esperás que haga el IDE cuando hagas click en el botón).

    Lo primero que tenés que conocer entonces es este cuadro de diálogo (Al que accedés al editar la configuración de debugging):

    A la izquierda tenés todas las posibles configuraciones base (Debuggear una aplicación web, un script de CLI, etc…).

    A la derecha los detalles particulares. Lo más importante es configurar correctamente el servidor:

    Dentro de este cuadro tenés que mirar especialmente los campos:

    • Host: El servidor donde está desplegada tu aplicación (Si usás Xampp o algo así será localhost, si no puede ser la IP de la computadora donde esté tu código, dependerá de tu entorno particular el valor que debés ingresar).
    • Port: El puerto donde está escuchando el servidor web al que te vas a conectar (Por lo general será el 80)
    • Debugger: Qué software de debugging usarás (Los estándares son XDebug y Zend Debugger, si no usás ZendFramework te recomiendo usar el primero).

    Qué son los path mappings

    Por último, muy importante es configurar correctamente los mapeos de directorios. Este concepto puede solar un poco extraño al principio, pero es bastante claro: se trata de indicar dónde está, en el sistema de archivos local, el código que se está ejecutando en el servidor.

    Para entenderlo un poco mejor: el código que estás viendo en tu IDE está guardado en tu disco local (salvo que estés editando directamente sobre el servidor, algo que, salvo que uses una máquina virtual sería muy mala idea).

    El código que se ejecuta está en el webserver que tiene instalado el debugger.

    Por eso, para poder ir corriendo paso-a-paso el IDE necesita conocer qué archivo debe mostrar en base a la comunicación con el debugger.

    De eso precisamente es se tratan estos mapeos.

    Una vez terminada esta configuración basta volver a la barra de arriba

    Pero esta vez hacer click en el botón del «bichito» (Bug) y, si todo sale bien, verás una pantalla como esta en la parte de abajo de tu IDE:

    Y ahora se trata sólo de colocar breakpoints en algunos lugares estratégicos del código (Simplemente haciendo click a la izquieda de la línea seleccionada):

     Y recargar la página para que, cuando llegue a este lugar de la ejecución se vea algo como:

    En la parte del código la línea azul marca el código que está a punto de ejecutarse, pero la parte más interesante es la de abajo a la derecha, donde podés ver el contenido de las variables en tiempo real.

    De esta forma podés debuggear tu código sin temer dejar olvidado algún var_dump por ahí.

    ¿Te gustaría ver una sesión de debugging en vivo? Acá hay algo que puede interesarte.

  • Cómo funciona el conversor de parámetros de Symfony

    Cómo funciona el conversor de parámetros de Symfony

    Cada vez que conozco más del framework Symfony, más me gusta :).

    Esto que te voy a mostrar a continuación me pareció un acto de magia cuando me lo crucé por primera vez: el conversor de parámetros.

    En la mayoría de los Controllers (especialmente cuando se trata de CRUDs), se recibirá algún parámetro que será la clave para encontrar el objeto sobre el que se quiere realizar la operación, por ejemplo:

    public function showAction(Request $request)
    {
      $client = $this->getDoctrine()->getRepository('AppBundle:Client')->find($request->getParameter('id');
    
      if ( $client ) {
        ...
      } else {
        // 404
      }
    }

    Es muy común ver código de este tipo. De hecho, si lo miramos desde un poco lejos notaremos que hay una estructura en común en estas operaciones:

    1. Buscar el objeto
    2. Si se encontró, procesar normalmente
    3. Si no se encontró generar un error 404

    La idea del conversor de parámetros es simplificar este proceso recurriendo a un uso muy interesante de las annotations:

    /**
     * @Route("/client/{id}")
     * @ParamConverter("client", class="AppBundle:Client")
     */

    La magia se produce en la combinación de las annotations @Route y @ParamConverter.

    La primera define el patrón que debe seguir una URI para ser mapeada a esta acción, la segunda define cómo deben interpretarse los parámetros de modo de generar obejtos.

    En definitiva el código quedaría algo así como:

    /**
     * @Route("/client/{id}")
     * @ParamConverter("client", class="AppBundle:Client")
     */
    public function showAction(Client $client)
    {
        ...
    }

    Notá cómo se simplificó el método (Toda la parte de la búsqueda la realiza directamente el framework y el método sólo es llamado cuando efectivamente se obtuvo el objeto buscado).

    Pero además de ahorrar código (y dolores de cabeza varios), esta metodología tiene un efecto colateral más que interesante: hace mucho más simple el testing automatizado. ¿Por qué? Porque fuerza a armar métodos basados en al inyección de dependencias.

    Más allá del framework de testing que uses, podés imaginar que es muy simple hacer algo como:

    $controller = new ClientController();
    
    $controller->showAction( new Client() );

    Obviamente el new Client() puede ser reemplazado por un objeto creado a tu conveniencia, sin tener que entrar en enroscados tests con bases de datos espurias.

    Todo lo que leíste hasta acá es en realidad un caso particular de conversor de parámetros (El de Doctrine), pero existen más (¡Incluso podés definir los tuyos propios!).

    No es que vayas a pasarte el día inventando tus propios conversores de parámetros, pero es importante saber cómo funcionan las herramientas que uno usa para poder sacarles el máximo jugo posible.

    Si te interesa aprender más sobre este gran framework el curso Introducción a Symfony puede ayudarte.