Cómo desplegar una aplicación Symfony 4 en tu servidor

En otros artículos hemos hablado de los motivos por los que utilizar Symfony como nuestra herramienta de desarrollo, buenas prácticas o cómo crear nuestro primer proyecto con Symfony 4.

Una vez nos hemos decidido a desarrollar nuestra aplicación con Symfony y la tenemos lista para desplegar en producción debemos tener en cuenta una serie de cosas tanto cuando se trata de un primer despliegue como cuando se trata de una actualización.

¿Qué opciones tenemos para desplegar los cambios en una aplicación?

Actualmente hay 2 alternativas principales, por un lado podemos ver la aplicación como un conjunto de ficheros de código, configuración, base de datos, dependencias y demás artefactos. En ese caso necesitaremos tener una estrategia clara para que los nuevos cambios en el conjunto de nuestra aplicación que se suele describir mediante build scripts.

docker

Otra aproximación que se está extendiendo rápidamente son las aplicaciones empaquetadas en contenedores.

Actualmente es docker la herramienta que se ha posicionado como estándar de facto y que nos permite, en lugar de utilizar scripts que manejan los cambios en la aplicación, empaquetar la aplicación en una imagen que contiene todas las dependencias y aísla la ejecución de otras aplicaciones que se estén ejecutando en el servidor.

La ventaja es que para cada versión de nuestro software podemos construir la imagen necesaria en nuestro servidor de CI/CD y publicarla en el registro correspondiente para que la actualización sea tan sencilla como levantar otro contenedor que sirva la aplicación en base a la imagen nueva y detener la ejecución del contenedor con la versión antigua.

Normalmente durante el desarrollo se debe tener en cuenta si el objetivo será usar docker ya que puede condicionar algunas decisiones que tomemos para facilitar después el uso de contenedores. Por ejemplo cuando usamos contenedores las aplicaciones deben ser stateless (no deben mantener estado, es decir, debe poder destruirse el contenedor de un nodo del clúster y levantarse otro usando la misma imagen sin que el servicio se vea afectado).

¿Qué vamos tratar nosotros en este artículo?

Vamos a seguir la aproximación clásica, tenemos un servidor en el que reside la aplicación, con un estado actual y queremos poder desplegar nuestras nuevas versiones de la manera más sencilla y automatizada posible.

Para ello, como ya hemos anticipado, no vamos a entrar en las posibilidades del mundo de los contenedores porque se trata de un tema algo más avanzando, tampoco en los zero downtime deployments en los que el servicio no se interrumpe.

Lo que sí pretendemos es sentar las bases de qué cambios son los más comunes que sufre una aplicación cuando está en desarrollo o mantenimiento y necesita ser actualizada para que a partir de ahí cada equipo profundice en las técnicas avanzadas relacionadas con sus necesidades.

¿Qué piezas componen una aplicación cuando afrontamos su despliegue?

Código de la aplicación

Es el código que nosotros escribimos y por tanto del que nos responsabilizamos, en nuestro caso se trata fundamentalmente del contenido de las carpetas assets, config, src y templates.

Cuando queremos desplegar una nueva funcionalidad por lo general tendremos nuevo código o modificaciones en el código de nuestra aplicación y transmitir estos cambios desde nuestro entorno de trabajo al servidor de producción puede ser tan sencillo como subir mediante FTP los ficheros correspondientes. Esto es un proceso manual y como todo proceso manual es propenso a errores, es por ello que si ponemos en una balanza simplicidad, comodidad y calidad del proceso (que sea repetible, fiable, la posibilidad de rollback) una opción sólida que equilibra las cualidades que queremos maximizar es el uso de git.

Ya deberíamos estar utilizando git como pieza central en nuestro flujo de desarrollo y como sistema de control de versiones distribuido nos va a permitir tener otro repositorio en producción que utilizaremos únicamente para desplegar cambios.

Esto puede hacerse de manera más o menos automatizada según queramos complicar el proceso. Si usamos un servicio de CI/CD podemos automatizar el envío a nuestro servidor de producción de los cambios, pero si no queremos dar el paso podemos trabajar con 2 remotes en nuestro proyecto, uno para nuestro repositorio central (github, gitlab, etc) y otro para el servidor de producción..

Para cambiar la versión del código de producción lo único que tendremos que hacer es pushear los nuevos commits al remote que tengamos configurado.

Configuración

Los parámetros que configuramos en una aplicación Symfony deberían encontrarse en los ficheros .env*, es cierto que al desplegar los cambios en el código habremos actualizado nuestro fichero .env pero tendremos que realizar manualmente cualquier cambio en .env.local.

A pesar de ser un paso manual en entornos en los que queremos tener un flujo sencillo no debería ser un problema ya que es una de las zonas de la aplicación que menos cambia. Además por la propia naturaleza de la información almacenada muchas veces no podemos incluirla en nuestros repositorios por lo que las opciones disponibles no son muchas, prácticamente nos quedan los gestores de secretos como vault o la configuración manual.

Si a esto le sumamos un esfuerzo por nuestra parte en tener valores por defecto sensatos nos encontraremos muchas veces sin la necesidad de sobrescribir el valor por defecto de .env y por tanto no tendremos que realizar ninguna acción manual. En otras ocasiones tendremos que configurar los valores de manera obligatoria, por ejemplo cuando tenemos que añadir un token de autenticación de un sistema de terceros.

Bases de datos

En una aplicación basada en Symfony es muy común que cuando estemos trabajando en nuevas funcionalidades nos encontremos trabajando con nuevas entidades o cambios en las ya existentes. Para ese caso de uso hay una opción muy potente, se trata del paquete doctrine/doctrine-migrations-bundle.

Una vez empecemos a utilizar migraciones nuestro flujo de trabajo durante el desarrollo contará con un paso más, cada vez que modificamos la persistencia de las entidades generaremos una migración. Esto se traducirá en un nuevo fichero php con la habilidad de ejecutar el SQL necesario para hacer el cambio en la entidad y el SQL necesario para deshacer el cambio.

Por tanto tendremos en nuestro sistema de control de versiones un sistema de control de versiones de la estructura de la base de datos. Podemos dejar que sea el paquete quien se encargue de generar el SQL necesario pero también podemos modificarlo para reflejar otro tipo de cambios o generar migraciones vacías en las que seremos nosotros quien defina el cuerpo de los métodos up y down.

Cuando esas nuevas migraciones se encuentren en el servidor de producción tendremos que ejecutarlas para que realice los cambios necesarios a en la base de datos.

piezas-aplicacion

Dependencias

Si estamos usando Symfony y Webpack Encore contaremos con 2 gestores de paquetes, por un lado Composer para las dependencias php y por otro Yarn para las correspondientes al front.

Cada vez que realicemos cambios en los ficheros que describen las dependencias que maneja Composer tendremos que ejecutar en nuestro servidor composer install –no-dev –optimize-autoloader para que la herramienta descargue las versiones correctas de nuestras dependencias y las instale teniendo en cuenta las optimizaciones que se pueden llevar a cabo en producción.

De igual manera, cada vez que realicemos cambios en los ficheros correspondientes a Yarn tendremos que ejecutar yarn install en nuestro servidor.

Artefactos

En el caso que planteamos, y siguiendo la misma filosofía que con las dependencias, si hay cualquier cambio en nuestros assets éste debería reflejarse en los artefactos construidos con Webpack.

Para ello tenemos 2 opciones, construir esos artefactos en otra máquina y enviarlos al servidor de producción o construirlos en el propio servidor de producción. La decisión aquí implica cambios en cómo instalamos nuestras dependencias con Yarn en el servidor de producción, si queremos utilizar Webpack en el servidor de manera que lancemos yarn encore production en producción para generar nuestros artefactos no podremos utilizar el flag –production con valor true, de lo contrario no tendremos instalados los paquetes necesarios

Caché

Por último cuando tenemos una aplicación Symfony configurada para ejecutarse en modo producción tanto el framework como algunos paquetes almacenan información en caché, cuando hacemos cambios es posible que tengamos que purgar la caché de Symfony para que realmente se ejecute nuestro nuevo código.

Desde cambios en las entidades a los cambios en las plantillas que se almacenan en caché precompiladas pasando por las traducciones forman parte de la lista de elementos que hacen uso intensivo de la caché del proyecto. Para esto solo tendremos que ejecutar php bin/console cache:clear en el entorno de producción, la caché quedará purgada y a partir de ese momento empezará a cachearse información en base a nuestro nuevo código.

Conclusión

Como hemos visto el proceso de manera global puede ser todo lo sencillo o todo lo complejo que queramos optando por diferentes técnicas y herramientas. Además cada paso del proceso de manera individual puede a su vez automatizarse en mayor o menor medida dependiendo del tipo de herramientas que usemos.

Con el planteamiento que realizamos debería ser suficiente escribir un script para que todas las partes automatizables se realicen de manera autónoma, a partir de ahí podemos saltar hacia el uso de herramientas especializadas como deployer, a servidores de CI/CD que orquesten el proceso y a flujos de trabajo basados en contenedores que es la dirección que está tomando la industria.

Dependiendo del tamaño de nuestros proyectos y de la cantidad de recursos que tengan asignados tendremos que tomar las decisiones que consideremos, nuestro consejo es que analicéis siempre en qué estado se encuentra vuestro proyecto en cuanto a proceso y defináis cuales son los puntos críticos para automatizarlos e ir avanzando.

Guía básica de Webpack 2

Los empaquetadores, comúnmente llamados en términos anglosajones, bundlers, son ya una herramienta imprescindible en cualquier entorno web moderno que se precie. Con la reciente ganancia de popularidad de Javascript, gracias a Nodejs, estas herramientas han proliferado en forma de utilidad de consola. Nodejs permite crear con gran agilidad este tipo de herramientas, e incluso escribir los propios módulos.
Grunt y Gulp, fueron los que sentaron las bases de lo que son actualmente herramientas como webpack. Tenían por función la ejecución tareas de cualquier tipo, con plugins para ampliar la funcionalidad. Actualmente, están proliferando diferentes soluciones al problema mas específico de agrupar recursos, como puede ser rollup o jspm entre otros. Todos ellos están escritos en Nodejs.

Nodejs, de ahora en adelante, node, ha supuesto un cambio en la forma de desarrollo de la web por lo que el lado del servidor corresponde. De la propia de definición de su página oficial: usa un node ha supuesto un cambio en la forma de desarrollo de la web por lo que el lado del servidor corresponde. De la propia de definición de su página oficial: usa un  modelo de operaciones Entrada/Salida sin bloqueo y orientado a eventos, que lo hace liviano y eficiente y programación funcional. Cuenta con una comunidad muy activa que desarrolla todo tipo de funcionalidades en forma de paquetes que reside en npm. La equivalencia podría ser packagist, en este caso, respecto de paquetes de php.  La proliferación de páginas SPA, con el uso intensivo de red o RTA combinado ha propiciado la amplia expansión y evolución. Además de poder unificar la lógica en  cliente y servidor, y usar el mismo lenguaje de programación para ambos.

¿Qué es Webpack?

Webpack como se define en web oficial: is a module bundler for modern JavaScript applications. When webpack processes your application, it recursively builds a Webpack como se define en web oficial: is a module bundler for modern JavaScript applications. When webpack processes your application, it recursively builds a  dependency graph that includes every module your application needs, then packages all of those modules into a small number of bundles – often only one – to be loaded  by the browser. O lo que es lo mismo, se encarga de generar gráficos de dependencias y empaquetarlos para que la aplicación sea capaz de funcionar correctamente.

La herramienta, nos permite ampliar su funcionalidad a través de una amplia lista de loaders que nos permiten aplicar transformaciones a los archivos que le pasemos.  Entre sus categorías, cuenta con funcionalidades básicas(xml, script, raw), empaquetadores(minificar, modernizr, web-components), específicos(imágenes, svg, base64, etc),  transpilers(coffescript, es6, typescript), plantillas(handlebars, jade, mustache), estilos(less, sass, stylus), traducciones(po, po2mo) o auxiliares(documentación, tests). También cuenta con una sección de plugins, mas limitada, que nos permite extender la funcionalidad mas allá de una simple transformación, pudiendo ejecutar lógica adicional, como es el caso de HotModuleReplacementPlugin, el cual nos permite regenerar los assets generados en función de cambios en los archivos originales.

Beneficios

  • Es una herramienta que nos permite automatizar tareas repetitivas.
  • Modularizar las diferentes secciones de nuestro proyecto.
  • Posibilidad de elección en función del tipo de asset(minificación de imágenes, etc).
  • Tiene una comunidad muy activa, y cuenta con un gran apoyo por parte de empresas como egghead.io, rollbar, etc.
  • Aunque en su primera versión era uno de los principales inconvenientes, en su segunda versión cuenta con una documentación de calidad.
  • Optimiza la velocidad de carga de la web.

Desventajas

  • En proyectos en los que la gestión de los assets no represente un problema, o el proyecto sea pequeño.
  • La separación entre las secciones sea difusa o difícilmente modularizable.
  • La curva de aprendizaje es elevada, necesita mucha configuración y es compleja al principio.

Objetivos del artículo

  • Usar código en ES2015 y transcribirlo a javascript legible para el navegador.
  • Loaders de css.
  • Extraer el css en archivos css.
  • Servidor de webpack Hot-Reloading.

Instalación de la herramienta

Para instalar webpack, debemos tener primero node instalado en el equipo. Puedes encontrar la información sobre cómo instalarlo en el enlace de la página oficial. Ahora que ya tenemos lo tenemos instalado, podemos continuar con la instalación de Webpack desde el terminal. A través del siguiente comando, le indicamos a npm, el gestor de paquetes de node, que queremos instalar en la carpeta actual, y guardarlo como dependencia del entorno de desarrollo:

Con la ejecución de este comando, ya tenemos el entorno preparado para proceder con Webpack.

Utilización

Javascript con ES2015

En primer lugar, vamos a instalar un transpiler para ES2015, aprovechando la implementación de babel, un loader capaz de llevar a cabo tal tarea con la ayuda de webpack.

Esto nos instalará en la carpeta donde lo ejecutemos babel-loader. Ahora creamos un archivo javascript con un ejemplo sencillo de un fragmento de código escrito en ES2015:

Este código javascript, ahora mismo no es funcional en el navegador, porque no entiende las nuevas características que hemos utilizado. Para que webpack empaquete nuestro código y lo reescriba en un estándar compatible con los navegadores, vamos a usar una configuración específica para que antes ejecute el loader de babel con el siguiente archivo de configuración:

En el siguiente enlace, está explicada toda la configuración usada aquí, pero explicaremos brevemente cada apartado. Entry, es el archivo de entrada, output, son todas las opciones referentes a la salida de los recursos, en este caso, usamos el sistema umd, con el nombre bundle.js y lo escribiremos en el directorio /web/js/dist. Por último, le damos la configuración necesaria para que use el loader de babel y nos escriba javascript válido en el  navegador. Si abrimos el archivo(en función de la versión de webpack y de sus loaders puede cambiar el contenido), podemos ver como las primeras lineas,  son las que nos introduce la lógica de webpack. A partir de la línea 86, podemos ver nuestro código, con las porciones de ES2015 reescrito:

Ahora, ya tenemos todos los elementos necesarios para ejecutar webpack, para que genere la salida que estamos buscando, nuestro archivo app.js compilado, con el nombre de bundle.js:

El ejecutable, se encargará de buscar el archivo de configuración(que debe estar donde lo ejecutamos),e interpretarlo para realizar las tareas convenientes.

CSS en javascript

El siguiente paso será usar un loader para parsear el css. Webpack hace uso del concepto de Web Components. Es por ello que su fin es el de empaquetar todos los recursos necesarios para el correcto funcionamiento en un archivo javascript, para su posterior reutilización. Sin entrar en detalles del porque, en el siguiente enlace, se exponen algunos de los motivos por los cuales, existe el concepto de CSS en javascript.

Para poder usar el loader de css, necesitamos instalarlo de la siguiente forma, como hemos hecho anteriormente con el loader de babel:

Acto seguido, cambiamos la configuración de webpack, para que quede así:

Con esta nueva regla, y el nuevo archivo de entrada, le estamos diciendo a webpack que queremos que lea y parsee el css que contiene el archivo style.css. Ahora, si abres el  fuente generado, podrás ver como ha incluido mucha mas lógica encargada de hacer la integración entre el fuente generado y el proyecto, además de los estilos css que contenía el archivo que creamos anteriormente. Podemos seguir ampliando nuestro set de herramientas para ser integradas con webpack, con sass, compresores de imágenes y un largo etcétera. Te aconsejamos que mires la documentación de webpack, para tener una visión mas profunda de el ecosistema.

Extracción del css

Para finalizar, con la sección de loaders y plugins, vamos a explicar como se pueden extraer los archivos css. Puesto que ya hemos visto como incluir el css en el archivo final bundle.js, haremos uso de un plugin que nos permite extraerlo y exportarlo a un archivo css común. Esta vez, lo que haremos no es instalar un loader, como lo hicimos previamente, puesto que necesitamos algo más que transformar las líneas. Ahora vamos a necesitar lógica para evaluar que hacer con los estilos que le indicamos a webpack que cargue, instalando un plugin.  En primer lugar, instalaremos el plugin:

Cambiando la configuración, de la siguiente forma, ya podremos incluir el archivo como app.bundle.css, tras la ejecución de webpack, como habíamos hecho anteriormente. Ahora podrás ver como en la carpeta /dist, se ha generado un nuevo archivo de css.

Servidor Web de recursos

Como no podía ser menos, el uso de estas herramientas, se vuelve lento y pesado. Cuando estamos trabajando contínuamente en los mismo recursos, vas a tener que ejecutar en el terminal la herramienta debido a los cambios efectuados y la necesidad de volver a generar el compilado. Antes este problema, con webpack, podemos usar un pequeño servidor que viene integrado, el cual nos compilará los nuevos cambios a través de watchers.

Con el siguiente comando, arrancaremos el servidor local de assets de webpack, que podremos incluir en nuestro proyecto.

En la configuración, añadiremos las siguientes líneas. Por una parte, el path público al que acceda el navegador para solicitar el bundle.js, nombre descrito en el archivo de configuración. Por otra parte, añadiremos en el array de plugins el encargado de comunicar los cambios al navegador. Con la última opción, –hot, le estamos indicando a webpack que queremos que haga uso del hot reloading, el cual, nos refrescará la web automáticamente, cada vez que se lleve a cabo un cambio.

Por último, este pequeño fragmento de código a nuestro app.js para que acepte las conexiones entrantes de hot reloading:

Conclusiones

Webpack es una herramienta muy potente que nos permite empaquetar todo tipo de recursos ya sean estáticos o dinámicos de una forma eficiente a través de sus gráficos de  dependencias. Tener todos los recursos en un mismo archivo de javascript, permite disminuir los tiempos de carga de la web, evitando peticiones innecesarias. Es cierto que al tratarse de javascript, tendremos un retardo en la carga de toda la lógica, pero si gestionamos de forma óptima los recursos, podemos evitar que los tiempos sea superiores respecto a las múltiples peticiones que puede realizar una página hacia el servidor. Así mismo, nos permite regenerar todos los recursos a través de su funcionalidad nombrada anteriormente, para evitar el paso por la consola en tiempo de desarrollo. En este artículo se ha intentado dar una visión básica y funcional de como usar esta herramienta. Asimismo, si estás interesado en la web oficial de webpack, tienes una sección completa de guías.

Buenas prácticas y consejos para mejorar tu código en Symfony

Symfony es un framework PHP que recientemente ha cumplido los 500 Millones de descargas y es conocido por su gran ecosistema de componentes. También es la base de proyectos como drupal, magento y prestashop(en sus fases iniciales al port hacia symfony) entre otros. Cuenta con una comunidad muy activa, y su creador está entre los miembros del PHP-FIG, es por ello que desde acceseo os queremos mostrar en este articulo una serie de buenas prácticas y consejos para mejorar vuestro código en Symfony.

Una de las principales características, conjuntamente con el sistema de componentes, es la calidad de su documentación, así como un amplio repertorio de test que le proporcionan una gran aceptación por parte de la comunidad y una base muy sólida para empezar cualquier proyecto sin importar su magnitud.

Vamos a citar en primer lugar algunos tips and tricks, y acto seguido, algunas buenas prácticas, extraídas directamente desde la propia sección.

Trucos y consejos para optimizar Symfony

Existen multitud de plataformas que hacen uso de las variables de entorno para almacenar los credenciales de las aplicaciones. Recientemente, symfony (en su versión 3.3) ha publicado un componente llamado Dotenv, el cual nos permite parsear archivos .env. En nuestra aplicación, podemos añadir las variables como parámetros:

// app/AppKernel.php

public function registerContainerConfiguration(LoaderInterface $loader)
{
    $loader->load($this->getRootDir().'/config/config_'.$this->getEnvironment().'.yml');

    try {
        (new Dotenv\Dotenv(__DIR__.'/../'))->load();
        $envParameters = $this->getEnvParameters();
        $loader->load(function($container) use($envParameters) {
            $container->getParameterBag()->add($envParameters);
        });
    } catch (Dotenv\Exception\InvalidPathException $e) {
        // don't do much at the moment, possibly the env variables are set differently (e.g. AWS)
    }

En su versión 3.2 de symfony, se integró el uso de la función env, encargada de obtener el valor de la variable de entorno desde la configuración en yaml con el siguiente formato:

# app/config/config.yml
doctrine:
    dbal:
    # ...
        password: "%env(DB_PASSWORD)%"

En la versión 2.6 fue añadido LockHandler, y en su versión mas reciente, la 3.3, han anunciado el componente llamado Lock component, el cual nos permite bloquear el acceso a los recursos a través de diversos métodos, tales como flock de php, PHP Semaphore extension, Redis y Memcache.

Yaml, human friendly data serialization standard, mantenido por Clark C. Evans, es un standard que tiene por objetivo la serialización de la información de forma descriptiva. En los últimos años ha ganado popularidad, debido a la facilidad de parseado frente a alternativas como XML. Herramientas como Docker, también lo usan para expresar sus archivos de configuración. Symfony cuenta con un componente llamado YAML Parser. Desde la versión 3.3, ahora se nos permite crear etiquetas personalizadas.

$yaml = "!my_tag { foo: bar }";
$config = Yaml::parse($yaml, Yaml::PARSE_CUSTOM_TAGS);

$tag = $config->getTag(); // $tag = 'my_tag'
$value = $config->getValue(); // $value = array('foo' => 'bar')

Lo podemos usar para añadir funcionalidades específicas para la configuración de nuestro bundle, por ejemplo.

Como última novedad, han incorporado la carga de archivos locales con patrones globales. Ahora podemos hacer lo siguiente:

const CONFIG_EXTS = '.{php,xml,yaml,yml}';

// ...
protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader)
{
    $confDir = dirname($this->getRootDir()).'/etc';
    $loader->import($confDir.'/packages/*'.self::CONFIG_EXTS, 'glob');
    $loader->import($confDir.'/packages/'.$this->getEnvironment().'/**/*'.self::CONFIG_EXTS, 'glob');
    $loader->import($confDir.'/container'.self::CONFIG_EXTS, 'glob');
}

Existe una característica del componente Config no muy conocida, que nos permite hacer override de los archivos que hemos cargado:

# app/config/config.yml
imports:
    - { resource: 'parameters.yml' }
    - { resource: '/etc/sites/mysite.com/parameters.yml', ignore_errors: true }

De esta forma, podemos tener en el servidor de producción un archivo con los parámetros correspondientes. Sin embargo, en local, como no existe dicho archivo y le hemos indicado que no lance excepción seguirá el flujo habitual de la aplicación.

Por lo que a conexión con capa de persistencia de datos se refiere, symfony usa Doctrine, un ORM (Object Relational Mapper). Los bundles con los que viene symfony, por regla general, vienen con una gran cantidad de configuración por defecto que nosotros podemos modificar según nuestras necesidades. Por lo que respecta al naming de las tablas, resulta común que la estrategia varíe según los requisitos del proyecto. Para tal propósito, podemos forzar la estrategia de naming con una configuración, para evitar la redundancia de escribir el parámetro en cada valor de la entidad en caso de pretender modificar el comportamiento habitual:

    #app/config/config.yml
        config:
            doctrine:
                dbal:
                # ...
                orm:
                    # ...
                    entity_managers:
                        default:
                            naming_strategy: doctrine.orm.naming_strategy.underscore

También podremos hacer nuestras propias estrategias de naming haciendo uso de la clase: Doctrine\ORM\Mapping\NamingStrategy según indican en su documentación.

En lo referente a su motor de plantillas, twig, hay interesantes mejoras que podemos utilizar.

Disponemos de la posibilidad de evaluar si existe un bloque, a través de la siguiente sintaxis:

{%if 'block_name' is block%}
   Esto es un bloque
{% endif %}

Cuando creamos nuestros propios filtros, y necesitamos proporcionarle un número arbitrario de parámetros de entrada, podemos usar la opción is_variadic

$filter = new Twig_Filter('thumbnail', function ($file, array $options = array()) {
// ...
}, array('is_variadic' => true));

Para finalizar, podemos usar lo que llaman, named arguments. Nos confían una mayor legibilidad en el código:

{{ data|convert_encoding(from='iso-2022-jp', to='UTF-8') }}

En cuanto a los logs, symfony cuenta con su propio componente, que nos ayuda identificar los estados por los cuales pasa nuestra aplicación. Es un gran desconocido,
pero cuenta con muchas funcionalidades que nos pueden ayudar identificar problemáticas futuras.

Podemos crear canales personalizados, de forma muy sencilla:

    # app/config/config.yml
    monolog:
        channels: ['foo', 'bar']

Ahora symfony ya permite pasar el canal de log a través de un argumento:

#app/config/config.yml
services:
    some_service:
        class: Namespace\Some\Service
        arguments: ["@monolog.logger.alert"]

Podemos crear formatters personalizados desde la configuración, sin necesidad de implementar una clase de formateo:

#config.yml
services:
    app.log.formatter:
        class: Monolog\Formatter\LineFormatter
        public: false
        arguments:
            - '%%level_name%% [%%datetime%%] [%%channel%%] %%message%%\n' #mensaje a escribir en el archivo de log
            - 'H:i:s' #formato
            - false #ignorar \n en los logs

Configurando donde queremos usar dicho formato:

#app/config/config.yml
monolog:
    handlers:
        main:
            formatter: app.log.formatter

Gestión de correos en entorno de desarrollo

Cuando se trata de correos, son una parte básica de cualquier aplicación, sobretodo si la aplicación tiene usuarios y deseamos verificar el correo de registro, por ejemplo. En entornos de desarrollo, es importante que los emails, sigan el flujo habitual. Así como la posibilidad de poder visualizarlos. Nosotros lidiamos con este problema con una herramienta llamada Mailhog, el cual cuenta con repositorio en github. Podemos ir a la sección releases de la documentación para encontrar la última versión compilada de la misma. Está dotado con un servidor de stmp y un panel web para visualizar todos los correos salientes de la aplicación. Es una gran opción que no necesita de instalar daemons en el caso de linux, tan sólo ejecución en el momento en que necesitemos de la funcionalidad.

En symfony tenemos una guía para organizar los entornos con diferentes archivos de configuración de manera jerárquica en función de los directorios como se muestra en la documentación. Esto nos puede servir para cargar diferentes configuraciones, como por ejemplo en este caso, que nos interesa cambiar los datos del servidor smtp para poder evaluar la aplicación al completo.

Buenas prácticas en Symfony

Por último vamos a citar algunas de las buenas prácticas a modo de resumen, que nos proporcionan desde la sección de best practices.

Configuración:

  • Define toda la configuración de la infraestructura en el archivo parameters.
  • Define todos los parámetros en parameters.yml.dist. Ten en cuenta que composer evalua este archivo para regenerar el parameters.yml.
  • Define la configuración de la aplicación en config.yml.
  • Usa constantes para definir la configuración.
  • El naming de los parámetros debería ser lo mas corto posible.

Organizar la lógica de negocio:

  • Usa nombres lo mas cortos posibles para los servicios del bundle.
  • Escribe la configuración de los servicios en archivos YAML
  • Como hemos comentado anteriormente, mueve los datos sensibles fuera de la aplicación con variables de entorno.

Controladores:

  • Los controladores deben extender siempre del controlador base de FrameworkBundle.
  • No uses la anotación @Template.
  • Usa ParamConverters para obtener la entidad en el controlador.
  • Por norma general, como indican en la documentación, los controladores deberían seguir la regla 5-10-20. 5 líneas variables o menos, 10 acciones o menos y 20 líneas o menos

Como conclusión final, symfony tiene una curva de aprendizaje en sus inicios algo elevada, pero cuando se domina el framework en sus aspectos más básicos nos puede ayudar con un ágil desarrollo con poco esfuerzo y una gran calidad del código, siguiendo unas normas básicas de buenas prácticas.

Cómo usar composer y aumentar la productividad de tu equipo

Composer se ha convertido en una herramienta fundamental del ecosistema PHP. Se trata de un gestor de dependencias que nos facilita la siempre tediosa tarea de lidiar con las dependencias de nuestro proyecto. Los proyectos crecen y tienden a acumular grandes cantidades de dependencias y no siempre es fácil tenerlas actualizadas a la última versión, decidir qué versión instalar o instalar las dependencias de nuestras dependencias para que nada falle en esa cadena de confianza que se genera entre proyectos y librerías.

 

El uso de composer orbita alrededor de los ficheros composer.json, composer.lock y el directorio vendor, de manera que en nuestro proyecto únicamente tendremos que incluir el autoloader para cargar todas las dependencias. Una de las cosas que ha facilitado tanto la existencia como la rápida adopción de composer es la creación de estándares de autoload por parte del PHP-FIG (PSR-0 y más tarde PSR-4).

Vamos a ver paso a paso cómo instalar composer, establecer las dependencias de nuestro proyecto y cargarlas con una sola línea. A lo largo del artículo describiremos y utilizaremos algunos comandos, los relativos a composer son independientes del sistema operativo pero la instalación está basada en la máquina de vagrant ubuntu/xenial64 siguiendo los pasos del artículo Crea y gestiona tus entornos de desarrollo con Vagrant del que hablamos hace algún tiempo.

Instalar composer paso a paso

Para instalar la herramienta en Ubuntu tendremos que ejecutar únicamente un comando. Hecho esto vamos a hacer disponible el binario de manera global. Desde el terminal ejecutamos las siguientes instrucciones.

curl -s https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer

Una vez ejecutado debemos comprobar que el paquete está listo para su uso, podemos ejecutar por ejemplo composer –version y la salida nos mostrará algo similar a lo siguiente

Composer version 1.3.2 2017-01-27 17:23:41

¿Cómo actualizar de composer?

Composer es una herramienta que pese a haber alcanzado la versión 1.0 en 2016 sigue en desarrollo y por tanto deberíamos actualizar con cierta frecuencia. Esto se consigue lanzando el comando composer self-update.

Empezando a usar composer

Para empezar a usar composer en un proyecto necesitaremos generar un fichero composer.json en el que especificaremos las dependencias del proyecto. Dentro del json usaremos la clave require para especificar nuestras dependencias que composer descargará usando packagist.

Packagist es el repositorio central en el que encontraremos el catálogo de paquetes disponibles para composer identificados usando cadenas del tipo vendor/package, por ejemplo la librería monolog la encontraremos referenciada como monolog/monolog. Si accedemos a la página correspondiente de la librería en packagist podremos ver información adicional como el comando para instalar el paquete usando composer, la url del repositorio en github, estadísticas del proyecto como instalaciones o paquetes que dependen a su vez de éste, sus requerimientos, versiones, etc.

El contenido mínimo del fichero composer.json con el que podríamos definir monolog como dependencia de nuestro proyecto para instalarla con composer sería el siguiente:

{
    "require": {
        "monolog/monolog": "1.*"
    }
}

Con este fichero en la carpeta podemos lanzar el comando composer install que nos mostrará paso a paso qué va sucediendo durante la instalación. En nuestro caso se instalará el paquete psr/log como dependencia de monolog y posteriormente el propio monolog/monolog que es nuestra dependencia.
Tras los paquetes instalados aparece una serie de paquetes sugeridos. Desde la página de packagist también podemos consultar estas dependencias sugeridas que normalmente nos permiten ampliar la funcionalidad del paquete instalado. Por ejemplo entre las sugerencias de monolog aparece mongodb/mongodb que nos permitiría enviar nuestros logs a un servidor mongodb.

Al final de la salida se muestran dos líneas más referentes a los últimos pasos que realiza composer al instalar:

Writing lock file
Generating autoload files

Con estas dos líneas nos especifica que se ha creado el fichero composer.lock en base a la instalación y el autoload del que hablabamos más arriba cuando mencionabamos PSR-4 como uno de los pilares fundamentales de composer que permite un uso tan cómodo de la herramienta.

Cuando ejecutamos composer install la aplicación trata de leer el fichero composer.lock en el que se especifica una serie de información, probablemente la más relevante sean las versiones exactas a instalar. Si no existe el fichero se resuelven las dependencias usando la información de composer.json y se genera el fichero composer.lock en base a la instalación realizada. En nuestro caso aparecerán entradas referentes a monolog/monolog 1.22.0 y a psr/log 1.0.2.

El fichero composer.lock nos va a permitir reinstalar exactamente las mismas dependencias una y otra vez. Pongamos por ejemplo que en el repositorio de nuestro proyecto incluimos el fichero composer.json, en unos meses se une un compañero al equipo e instala el proyecto desde cero en su máquina para empezar a desarrollar, si hay una versión más reciente que cumpla con la expresión 1.* será esa la que se le descargue, por ejemplo la 1.5.1 pudiendo ser incompatible con nuestro proyecto. En su caso se generaría un fichero composer.lock con la versión instalada que sería distinto a la nuestra.
Por el contrario si le facilitamos ambos ficheros al ejecutar composer install su proyecto contará exactamente con las mismas dependencias que el nuestro o que el servidor de producción.

Hasta aquí hemos realizado manualmente la edición de composer.json para especificar nuestras dependencias, hemos creado el fichero y hemos añadido las líneas necesarias pero podemos delegar estas acciones mecánicas en el propio composer.

Para generar el fichero composer.json usaremos el comando composer init que nos guiará a través de la creación del fichero, en nuestro caso lo único que haremos es darle nombre a nuestro proyecto como acceseo/composer-test y obviar el resto de pasos como aparece en nuestro terminal:

ubuntu@ubuntu-xenial:~$ composer init

Welcome to the Composer config generator
This command will guide you through creating your composer.json config.

Package name (<vendor>/<name>) [ubuntu/ubuntu]: acceseo/composer-test
Description []:
Author [, n to skip]: n
Minimum Stability []:
Package Type (e.g. library, project, metapackage, composer-plugin) []:
License []:

Define your dependencies.

Would you like to define your dependencies (require) interactively [yes]? no
Would you like to define your dev dependencies (require-dev) interactively [yes]? no

{
"name": "acceseo/composer-test",
"require": {}
}

Do you confirm generation [yes]? yes

Una vez confirmada la generación del fichero nos habrá generado el siguiente composer.json mínimo con el nombre del proyecto y sin dependencias:

{
"name": "acceseo/composer-test",
"require": {}
}

Para llegar al punto que hemos alcanzado antes necesitamos establecer como dependencia de nuestro proyecto la librería monolog, y descargar nuestras dependencias. Esto lo haríamos con el comando composer require de la siguiente manera: composer require monolog/monolog:1.*

La aplicación nos mostrará una salida similar a la de composer install especificando qué paquetes y versiones se han instalado, las sugerencias, generación del lock, etc como ya hemos tratado, pero además nos especificará que se está actualizando el fichero composer.json.

Utilizando nuestras dependencias

Hasta ahora hemos hablado de composer como tal, pero la finalidad de gestionar nuestras dependencias con composer es por un lado ahorrarnos gestionar manualmente el árbol de dependencias y por otro facilitar el uso de las dependencias en nuestro código olvidándonos de decenas de require necesarios en cada fichero.

Una vez instaladas las dependencias se ha generado la carpeta vendors de la que he no hemos hablado hasta ahora, esa carpeta es la que almacena los paquetes descargados y los ficheros necesarios para el autoload.

Para empezar a usar monolog únicamente tendríamos que importar en nuestro código el fichero /vendors/autoload.php, vamos a usar el ejemplo que hay disponible en el fichero readme.md de monolog para ver cómo haríamos esto. En el mismo directorio que tenemos el fichero composer.json creamos el fichero composer-test.php con el siguiente contenido.

En este fichero con una sola línea podemos cargar todas las dependencias y utilizarlas sin realizar ningún paso extra más.

<?php
require __DIR__.'/vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// create a log channel
$log = new Logger('name');
$log->pushHandler(new StreamHandler(__DIR__.'/monolog.log', Logger::WARNING));

// add records to the log
$log->warning('Foo');
$log->error('Bar');

Al ejecutar el script se nos generará el fichero monolog.log correspondiente sin errores de clases o funciones no definidas.

Aunque hemos tratado un ejemplo sencillo a lo largo del artículo composer se utiliza extensivamente en los proyectos PHP más importantes del mundo, ya que nos permite hacer muchas más cosas que quedan fuera del alcance de la introducción que hemos llevado a cabo.

¿Tienes alguna incidencia?

Cuéntanos qué ocurre
y nos pondremos con ello lo antes posible.

Este sitio está protegido por reCAPTCHA, y la Política de privacidad y Términos de servicio de Google.
Sucríbete a
nuestra newsletter

para estar al día en el mundo online

¡Cuéntanos tus ideas!
+34 96 653 19 14
+info@acceseo.com
He leído y acepto la política de privacidad

Este sitio está protegido por reCAPTCHA, y la Política de privacidad y Términos de servicio de Google.