Symfony 6 y Chart.js: un aporte

Una de las principales novedades de Symfony 6, es una mayor integración con librerías JavaScript (JS). El paquete Chart.js que sirve para construir gráficos en JS puede usarse integrado a cualquier proyecto desarrollado con Symfony 6. Unos de los principales desafíos, es configurar desde Symfony los gráficos de Chart.js.

A continuación, desarrollo un ejemplo donde podemos en los gráficos:

  1. Elegir el formato del gráfico.
  2. Cambiar el color de fondo del gráfico.
  3. Cambiar los colores del gráfico.
  4. Cambiar el tamaño.
  5. Cambiar el tamaño de las leyendas en los ejes.
  6. Cambiar el tamaño de la leyenda principal.

El punto de partida

Trabajaré con las siguientes herramientas que Usted deberá tener instalada para obtener el mismo resultado que yo desarrollo en este ejemplo:

  1. Windows 10
  2. Scoop (ver instrucciones en el sitio de symfony.com)
  3. Symfony CLI (desde el sitio oficial del symfony.com)
  4. Symfony 6.3
  5. PHP 8.1
  6. Encore
  7. npm
  8. yarn
  9. El bundle Symfony UX con Charts.js

Instalamos Scoop, luego Symfony CLI tal como se explica en la página oficial de Symfony.

Creamos un proyecto nuevo con el nombre de «grafico», para ello, en nuestra terminal de Windows escribimos y ejecutamos:

symfony new –webapp grafico

Instalamos sucesivamente desde la terminal de Windows:

composer require encore

yarn install

npm install

composer require symfony/ux-chartjs

Con todas estas herramientas ya instaladas, podemos empezar a escribir el código de nuestro gráfico. Elegimos hacer un gráfico de barras que refleje las ventas de una empresa.

El controlador

Procedemos a crear con nuestro editor de texto favorito un nuevo controlador que llamaremos GraficoController.php con el siguiente código que explicaremos a continuación:

En esta primera parte del controlador, vemos en especial las líneas 7 y 8 que fueron añadidas por Symfony y Composer cuando instalamos UX-chartjs y que activan las librerías necesarias para generar el gráfico.

Elegimos construir un gráfico de barras, esto se define en la línea 15 del gráfico anterior.

No trabajaremos con datos de una base de datos sino que definiremos dos arrays con los datos a graficar, uno para el eje horizontal o eje x, que se define como labels en la fila 17 y otro array para el contenido de cada barra, que puede verse en la imagen siguiente en la línea 23 con el nombre de data. La etiqueta para los datos se define en la fila 20 con el nombre de label, en la línea 21 definimos el color de las columnas: azul (blue) y en la fila 22 el color de borde (blanco en este caso). Con esta información tenemos los datos básicos para construir el gráfico, pero para personalizarlo aún más nos falta un poco de trabajo adicional.

A partir de la línea 29 definimos las opciones adicionales. En la línea 36, definimos el tamaño del font de la etiqueta o label que denominamos «Ventas». A partir de la línea 43 las escalas y tamaño de la fuente para el eje vertical (valor máximo de 4000 y tamaño de la fuente en 20) y para el eje horizontal, también le asignamos una fuente de tamaño 20 (ver fila 58 en la tercera imagen).

Finalmente, enviamos a nuestra plantilla denominada grafico.html.twig los valores contenidos en el array chart que incluye las opciones que decidimos para los ejes:

La plantilla de Twig

Enviados los datos a la plantilla grafico.html.twig mostramos a continuación su contenido que es muy sencillo. Lo básico y esencial está en la línea 10 de la siguiente imagen donde renderizamos el gráfico con los datos provistos por el controlador:

El archivos base.html.twig también contiene información esencial para configurar correctamente el gráfico que queremos construir. Los datos claves están en las líneas 7 y 11 de la siguiente imagen:

Finalmente, para visualizar el gráfico, desde la línea de comandos iniciamos el servidor de symfony haciendo:

symfony serve -d

Y luego arrancamos yarn, ejecutando desde la linea de comandos:

yarn watch

Para ver el gráfico vamos a nuestro navegador preferido, en mi caso Firefox, y en una ventana nueva escribimos la siguiente dirección local: localhost:8000/grafico

Finalmente obtenemos:

Otro tipo de gráfico que podemos utilizar para estos datos es el gráfico de línea. Cambiamos el color en el controlador para el borde a red (línea 22 «borderColor => «red»):

En el gráfico de arriba se pueden apreciar los puntos azules con la medición de cada mes y la línea roja, que definimos como color de borde. Si acercamos el puntero del mouse a cada punto del gráfico, podemos ver los valores exactos de las ventas de cada mes.

Symfony 6: Tres videos imperdibles

Si Usted es como yo y prefiere una visita guiada cuando se trata de aprender nuevo software o nuevas versiones del software que ya viene usando, le recomiendo tres videos del sitio SymfonyCasts.com para ingresar de lleno en el mundo de Symfony 6. Los títulos son los siguientes:

Dictados con gran destreza por Ryan Weaver (Chief Executive Officer SymfonyCasts), con un toque de humor yanqui sumamente original y con subtítulos en inglés y español, son cursos que nos hacen la vida más fácil en el mundo de la programación.

Symfony 6, Docker y Postgres DB

Al menos desde la versión 5, la gente de Symfony sugiere el uso de la base datos Postgres. Una base de datos que tendría más velocidad, capacidad y variedad de campos que por ejemplo MySQL.

También se sugiere el uso del contenedor Docker, aunque de manera básica.

Si a Usted como a mi, le gusta visualizar los datos, ver el contenido de las tablas y sus relaciones gráficamente, así como hacer backups y manejarlos por fuera del sistema que esté programando, como podía hacerlo con PHPMyAdmin cuando utilizaba MySQL o MariaDB, tiene disponible un conjunto de aplicaciones entre las que elegí DBeaver que es gratuita, potente y puede bajarse de aquí: https://dbeaver.io/download/

Requisitos

Antes de continuar, le aclaro que el desarrollo que sigue supone que Usted cuenta con una PC con Windows 10, ya tiene instalado Symfony 6 con sus principales librerías, Symfony CLI, Composer, está desarrollando alguna aplicación (en mi caso el ejemplo de un «Guestbook» expuesto por Fabien Potencier en su libro «The Fast Track»), Docker y Docker-compose para Windows, y por supuesto el programa DBeaver

Este último programa perimte crear una nueva conexión en Postgres, la cual tiene sus particularidades cuando se está utilizando Docker y allí se tiene alojada la base de datos.

La conexión en DBeaver

Para poder crear con éxito la conexión entre DBeaver y Docker, necesitamos que Docker esté iniciado en su PC y la base de datos ya cargada sin problemas. Esto lo comprobamos con el comando: docker-compose ps ejecutándolo en la terminal de Windows (yo utilizo Power Shell). Si todo está en orden verá una pantalla similar a la siguiente:

Comando docker-compose ps y su resultado

En la imagen anterior, debemos prestar atención a dos columnas en especial, la que se denomina State que nos muestra en este caso que los dos componenetes de Docker contenidos en el stack se encuentran ejecutándose sin problemas al estar en Up. La otra columna a la cual debemos prestar atención especial, es la última designada como Ports De esa columna sale un dato muy importante para nuestra conexión y es el número 49555. Este número cambiará cada vez que apague Docker y lo vuelva a iniciar. La base de datos de Postgres, lleva el nombre aquí de guestbook_database_1

Nuestra base de datos tiene los siguientes parámetros, siguiendo el ejemplo de Potencier:

Nombre: app

Usuario: symfony

Password: ChangeMe

Siendo el puerto a utilizar 49555, y no el tradicional 5432 asignado por defecto a Postgres.

Ingresamos a DBvear donde ya tenemos creada la conexión pero que debe ser editada porque está con el número de puerto de la puesta en marcha anterior de Docker. Cambiamos entonces ese viejo puerto por el número nuevo:

Aquí hay otro parámetro clave para que funcione adecuadamente la conexión: el host. El host no es el típico localhost o 127.0.0.1 sino otro cuya identificación es: 192.168.100.30 ¿De donde sale este dato? Es el host correspondiente a la dirección IPv4 del conector de red que estemos usando en nuestra PC.

¿Donde obtengo este número? De la configuración de nuestra PC entrando a, Configuración->Internet y Redes->Propiedades y bajamos hasta el final donde vemos el dato que necesitamos. En mi caso, me conecto a Internet por Wi Fi y mi conexión se llama Giorgio.

La dirección IPv4 para esta conexión de red es:

Vamos entonces a DBeaver colocamos este número, y hacemos clic en «Probar conexión» resultando exitosa como lo muestra el cuadro siguiente:

Y ahora, dentro de DBeaver podemos ver los datos desplegados de la tabla Conference que forma parte del sistema Guestbook.

Como puede verse, nada complicado resulta este modo de crear una conexión en DBeaver para una base de datos Postegres y con Docker funcionando. Solo que debemos saber de donde obtener los datos correctos.

Symfony 5: La Vía Rápida. Otra vez el Paso 9

Reescribiendo el Paso 9

El objetivo de esta entrada, es tratar de explicar como instalar y configurar EasyAdmin 3 con Symfony 5, que a esta altura (Octubre de 2020), ya va por la versión 5.1.7 y parece pronta a salir la versión 5.2, y aplicarlo al proyecto Guestbook.

Habíamos comentado en una entrada anterior, que quienes sigan el libro de Potencier «Symfony 5: La Vía Rápida», se iban a encontrar con un problema al intentar aplicar las instrucciones del libro, que está pensado para la versión de EasyAdmin 2.* y no para la versión 3.

Quedan entonces dos opciones: instalar esta versión más vieja de EasyAdmin que es la que se desarrolla en el libro, o intentar adecuar la nueva versión de EasyAdmin, la número 3, al contexto del proyecto desarrollado por Potencier en su libro. Yo elegí finalmente la segunda opción, y trataré de explicar a continuación como procedí para instalar, personalizar y configurar EasyAdmin 3, en el marco del proyecto Guestbook desarrollado en el libro, «Symfony 5: La Vía Rápida». Los comentarios que permitan mejorar estas explicaciones, son bienvenidos. Ya hay circulando en Internet videos y explicaciones escritas sobre la instalación de EasyAdmin 3, pero ninguna adaptada al caso del libro. Ese vacío trataré de llenar con esta entrada.

Instalar EasyAdmin 3 en el proyecto Guestbook

Partimos del supuesto que hemos terminado el Paso 8 del libro, donde se instaló Doctrine ORM, fueron creadas las entidades Conference y Comment y enlazamos las mismas, vinculando cada comentario a una conferencia en particular. Finalmente, se realizó la migración de la base de datos, dando lugar cada entidad a una nueva tabla con el mismo nombre, en nuestra base de datos. Gráficamente:

Imagen 1

Ahora vamos a instalar el bundle EasyAdmin 3. Posicionados en la carpeta del proyecto, y tal como lo pide el libro, ejecutamos desde la CLI el comando:

symfony composer req admin

A continuación, debemos instalar al menos un Dashboard. Lo hacemos con el comando siguiente:

symfony console make:admin:dashboard

En el proceso de instalación se nos hacen un par de preguntas, contestamos las opciones por defecto. El proceso queda graficado en la imagen siguiente:

Imagen 2

Como vemos en la propia imagen, nos invitan a generar los controladores CRUD. En nuestro caso son dos como mínimo, para gestionar cada entidad que tenemos que son Conference y Comment.

Pero antes cabría preguntarse que significa CRUD. Encontramos esta sencilla explicación en la web, (sin que hallamos guardado la fuente para citarla):

El concepto CRUD está estrechamente vinculado a la gestión de datos digitales. CRUD hace referencia a un acrónimo en el que se reúnen las primeras letras de las cuatro operaciones fundamentales de aplicaciones persistentes en sistemas de bases de datos:

  • Create (Crear registros)
  • Read bzw. Retrieve (Leer registros)
  • Update (Actualizar registros)
  • Delete bzw. Destroy (Borrar registros)

En pocas palabras, CRUD resume las funciones requeridas por un usuario para crear y gestionar datos. Varios procesos de gestión de datos están basados en CRUD, en los que dichas operaciones están específicamente adaptadas a los requisitos del sistema y de usuario, ya sea para la gestión de bases de datos o para el uso de aplicaciones.

Luego de instalar el primer Dashboard, podemos escribir en nuestro navegador la dirección:

http://127.0.0.1/admin

Se cargará una pantalla similar a la siguiente (se muestra una parte), donde nos ayudan a seguir adelante con la generación de los CRUD controllers:

Imagen 3

Primero tenemos que completar el contenido del archivo editándolo:

src\Controller\Admin\DashboardController.php

Nos pide incorporar varios métodos que en realidad ya se instalaron automáticamente cuando generamos el Dashboard, siendo los que faltarían, los siguiente:

use EasyCorp\Bundle\EasyAdminBundle\Router\CrudUrlGenerator;
use App\Entity\Conference;
use App\Entity\Comment;

Creamos ahora los CRUD controllers:

symfony console make:admin:crud

Nos preguntará para cual de las dos entidades que tenemos creadas, deseamos crear un CRUD. Elegimos Conference tal como muestra la imagen siguiente (opción 1), y luego nos realizan dos preguntas, donde optamos por las opciones por defecto:

Imagen 4

repetimos el comando para crear ahora el CRUD para los comentarios, o sea para la entidad Comment:

symfony console make:admin:crud

La siguiente captura de pantalla, muestra el proceso completo de generación de los dos CRUD por consola:

Imagen 5

Ahora que contamos con los archivos básicos, tenemos que proceder a adaptarlos a nuestras necesidades. Empezaremos por trabajar con el archivo que contiene el Dashboard: DashboardController.php

Continuamos leyendo las instrucciones que teníamos en la página:

http://127.0.0.1/admin

En especial, nos interesa ahora esta parte:

Imagen 6

En la imagen siguiente está el código del Dashborard que en la sección de los métodos ya tiene incorporado:

use EasyCorp\Bundle\EasyAdminBundle\Router\CrudUrlGenerator;

Faltan agregar:
use App\Entity\Conference;
use App\Entity\Comment;

Como vemos, hay tres funciones ya creadas en forma automática: index, configureDashboard y configureMenuItems, y sobre las tres tenemos cambios que realizar:

Imagen 7

En la primera función, cambiamos el contenido return parent::index(); por este otro (sugerido en las instrucciones que vimos más arriba):

$routeBuilder = $this->get(CrudUrlGenerator::class)->build();    
return $this->redirect($routeBuilder->setController(ConferenceCrudController::class)->generateUrl());

Es decir, que elegimos como página de inicio del Administrador, el CRUD de Conference.

En la segunda función solo vamos cambiar el título de la página de inicio de del Administrador. Elegimos Guestbook Admin

En la tercera función, como su propio nombre lo indica, tenemos que configurar los items que compondran el menú del Administrador. Ponemos tres: un link a la página principal del proyecto. Un segundo link al CRUD de conferencias (Conference) y un tercer link al CRUD de comentarios (Comment). El resultado completo de los cambios del archivo DashboardController.php se muestra en las imágenes 8 y 9. La imágen 8, muestra los métodos que tienen que estar habilitados. La imagen 9, muestra el código de las funciones.

Imagen 8

El resultado del nuevo código para las funciones es:

Imagen 9

Las dos imágenes combinadas, muestran el archivo completo DashboardController.php

Este código se ve así en http://127.0.0.1/admin

Imagen 10

Mostramos la página con varios registros ya caragados como muestra. Aquí hay mucho para mejorar, pero hemos logrado que se visualice una de las entidades, Conference, que nos permite: crear una nueva conferencia, editar una conferencia existente, borrar o eliminar (Delete). Y también contamos con un buscador. Podremos construir filtros y personalizar las columnas. Esa será nuestra siguiente tarea.

Personalizando los CRUD

Hemos generado como parte del proceso de instalación del Bundle EasyAdmin 3, dos CRUD, que son archivos con los controladores, para manejar cada una de las entidades desde el administrador. Uno de los archivos es para la entidad Conference y otro para la entidad Comment. Ambos archivos se encuentran aquí:

src\Controller\Admin\ConferenceCrudController.php

src\Controller\Admin\CommentCrudController.php

Empecemos por analizar el archivo ConferenceCrudController.php tal como fue instalado por el Bundle:

Imagen 11

La Imágen 11, muestra el aspecto incial que presenta después de generarlo desde la CLI. Las columnas que aparecen en la Imágen 10, son seleccionadas automáticamente, pero nos gustaría personalizarlas. Por ejemplo: Cambiar los títulos de las columnas. Sacar la columna ID, que no debería ser vista ni modificada por nadie, ni siquiera el administrador, ya que alteraría todo el sistema. Por otro lado, nos gustaría que la columna «Is International» (recordemos que es un campo tipo Boolean), no tenga el número 1 para cuando es verdadero el valor y esté vacía cuando es falso el valor. Nos gustaría, además, para más información, agegar el total de comentarios de cada conferencia como una columna adicional. ¿Desde donde se hace todo esto? Editando el archivo que estamos analizando. Una vez hecho estos cambios, la pantalla de la conferencias en nuestro browser, tendrá el siguiente aspecto:

Imagen 12

Y esto se debe al siguiente código:

Imagen 13

Es muy importante notar que cada ítem que queremos personalizar, tiene una denominación del tipo de campo que se trata, seguido del título de la columna. Por ejemplo, para la primer columna que muestra la ciudad de la conferencia, que se trata de un campo texto (varchar) escribimos dentro de la función configureFields:

yield TextField::new(‘city’, ‘City’);

A su vez, en la parte superior donde se definen los métodos, agregamos:

use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;

Y así con cada tipo de campo. Debemos especificar en la función configureFields el tipo de datos que contiene la entidad (y su tabla) y en los métodos definidos en parte superior del controlador, la sentencia que llame al método correspondiente a ese tipo de campo. Para cada tipo de campo, necesitamos definir un método correspondiente.

Veamos ahora el caso de la entidad Comment. El CRUD original está contenido en el archivo CommentCrudController.php y su código es el siguiente:

Imagen 14

Pero así como está, si queremos visualizar el listado de los comentarios, nos lleva al siguiente error:

Imagen 15

Este es un caso más complejo. Tiene al menos dos complicaciones, 1) formatear adecuadamente el campo fecha createdAt para evitar el error que se muestra en la Imágen 15, y 2) definir un tratamiento distinto para el campo de texto photoFilename, según que estemos en la opción de listado (donde queremos ver la foto) o edición (donde queremos ver el nombre del archivo de la foto). Como primer paso, vemos que hay que modificar el formato del campo fecha para que pueda ser adecuadamente mostrado.

yield DateTimeField::new(‘createdAt’, ‘Created’)->setFormat(‘dd-MM-y HH:mm:ss’)
->setSortable(true)->setFormTypeOption(‘disabled’,’disabled’);

Notamos aquí una gran diferencia respecto a la configuración de los campos de la entidad Conference. Vemos que es posible personalizar en varios aspectos los campos que lo requieran. En el caso del campo fecha createdAt especificamos el formato de fecha y hora que queremos visualizar en el listado con:

->setFormat(‘dd-MM-y HH:mm:ss’)

Habilitamos la posibilidad de ordenar la columna en ambos sentidos, ascendente o descendente:

->setSortable(true)

Y, finalmente, determinamos que cuando se pase al modo de edición, el campo no se puede editar. Es de solo lectura. Esto lo hacemos con:

->setFormTypeOption(‘disabled’,’disabled’)

Otro campo de manejo bastante complejo que requiere nuestra atención es photoFilename. Este campo, es un campo texto, que lleva el nombre de los archivos con las imágenes subidas por los visitantes que dejaron un mensaje en Guestbook. Suponemos que queremos otorgar al administrador, en el modo edición (Edit) el poder de decidir si una imagen es pertinente y queda tal como la subió el visitante, o por el contrario, no se la considera adecuada y por lo tanto eliminamos su nombre de archivo para que no sea visible la imagen.

Para poder hacer esto con el campo photoFilename necesitamos definir dos variables auxiliares, que llamamos:

$avatar = ImageField::new(‘photoFilename’)->setBasePath(‘uploads/photos/’)->setLabel(‘Photo’);
$avatarTextFile = TextField::new(‘photoFilename’);

Estas dos variables la usamos del siguiente modo:

if (Crud::PAGE_INDEX === $pageName) {
yield ImageField::new(‘photoFilename’)->setBasePath(‘uploads/photos/’)->setLabel(‘Photo’);
} elseif (Crud::PAGE_EDIT === $pageName) {
yield TextField::new(‘photoFilename’)->setLabel(‘Photo’);
}

Recordemos también que para cada tipo de campo, tenemos que definir un método al inicio del archivo.

El nuevo código se muestra en las tres siguientes imágenes:

Imagen 16
Imagen 17
Imagen 18

Y lo visualizamos en nuestro Browser así, en el modo listado (Read):

Imagen 19

Mientras que en el modo edición (Edit) se vería así el formulario (por ejemplo para el tercer registro del listado):

Imagen 20

Vemos que conseguimos lo que pretendíamos: el campo de fecha es de solo lectura y el campo photoFilename pasa a mostrar el nombre del archivo y no la imagen.

Pero un verdadero CRUD no estaría completo si no podemos hacer alguna selección de los registros. Por ejemplo, seleccionar solo los registros de una determinada conferencia y/o según el estado del comentario. Crearemos estos dos filtros y mostraremos su funcionamiento.

El manejo de los filtros para los comentarios, se hace dentro del archivo CommentCrudController.php a través de la función:

public function configureFilters(Filters $filters): Filters
{
return $filters
->add(‘conference’)
->add(‘state’);
}

Y su aspecto es el siguiente( mostramos dos imágenes) una con los filtros desplegados y seleccionados pero todavía no aplicados, y la siguiente imagen, con el filtro aplicado donde hemos seleccionado las condiciones siguientes: para la conferencia: Amsterdan 2019 y para el estado: submitted (o sea, los comentarios enviados pero todavía no aprobados para su publicación).

Imagen 21

Una vez hecha la selección de las condiciones de filtrado, hacemos clic con el mouse en botón Apply y quedara el nuevo listado acotado solo a los registros que queremos ver:

Imagen 22

En la imagen 22, puede verse recuadrado en rojo, el sector de la pantalla desde donde se manejan los filtros.

Bien, con esto hemos visto lo básico del Bundle EasyAdmin 3 y su incorporación al proyecto Guestbook, desarrollado en el libro «Symfony 5: La Vía Rápida».

Quedaría por asegurar el acceso al administrador definiendo un usuario y contraseña, pero esto se hace igual que en la versión anterior del Bundle, por lo que el capítulo 15 del libro, titulado: «Paso 15: Asegurando el panel de administración» sigue siendo válido para esta versión del Bundle.

Symfony 5: La Vía Rápida. Pasos 20.6 y 20.7

Como de costumbre a lo largo del libro, estos son otros puntos que están incompletos. Es necesario aclarar que además de cambiar el archivo docker-compose.yaml se debe instalar MailCatcher, que es un programa de webmail independiente de Symfony. Pero incluso antes de instalar MailCatcher, deberemos instalar Ruby, con todas sus librerias, de otro modo no funcionará MailCatcher. ¿Costaba tanto esfuerzo hacer estas aclaraciones estimado Sr. Potencier?

Solo bajo estas prescripciones puedes ejecutar después con éxito el comando:

symfony open:local:webmail

Esta orden abrirá una pestaña en tu browser o navegador Web, en el puerto 32768, como lo muestra la siguiente imagen:

Luego Potencier, nos dice que también podremos abrir el Webmail, directamente desde la barra de depuración web de nuestro proyecto, y va acompañada esta frase de una captura de pantalla, donde MailCatcher NO aparece. En consecuencia, no hay posibilidad de ejecutarlo o llamarlo desde allí.

Por último, nos invita a enviar un comentario, asegurándonos que recibiremos un correo electrónico en la interfaz de webmail. Pues bien, lamento decirlo, pero con el código escrito hasta aquí, no recibirás nada. En otras palabras, el sistema no funciona. Y eso que después del envió del comentario de prueba, puedes ver una respuesta «HTTP status: 200 OK» al pie de la pantalla de tu proyecto.

Al final de la página 220 (versión en español), nos invita a revisar los registros de logs, para ver si todo funciona como se espera. Nos fijamos y no aparecen mensajes de error de ningún tipo, pero el mensaje con el comentario enviado, no aparece en nuestra bandeja de entrada de MailCatcher.

Como vemos, una seguidilla de errores y omisiones que hacen poco productivo estos pasos.

Symfony 5: La Vía Rápida. Paso 18.5

El Paso 18 del libro de Potencier «Symfony 5: La Vía Rápida», está dedicado a mostrarnos la utilidad y la forma de volver asincrónicos a los comentarios de los visitantes del ejemplo del Guestbook.

A partir del Paso 18.5, titulado «Volviendonos aisncrónicos de verdad» se explica como configurar y usar RabbitMQ, una herramienta que sirve justamente para volver asincrónicos los mensajes de los comentarios.

Pero…. como ya nos tiene acostumbrado Potencier, la información no está completa y no se aclaran algunos detalles para los programadores que desarrollan su proyecto en el entorno de Windows 10, con un servidor Apache y base de datos MySQL o MariaDB, como es mi caso.

Trataremos de dar algunas pistas aquí, para mejorar la experiencia del lector de este capítulo o Paso 18 y el uso de RabbitMQ.

Un primer detalle a hacer notar en el Paso 18.8 «Consumiendo los mensajes», es que si haz actualizado la versión de tu proyecto a Symfony 5.1, el comando que aparece en la página 197 (edición en español) que es presentado para Symfony 5.0, cambia la sintaxis:

symfony console messenger:consume async -vv (para Symfony 5.0)

symfony console messenger:consume async-normal -vv (para Symfony 5.1)

El punto 18.9 se titula «Explorando la interfaz de administración web de RabbitMQ», y está incompleto. Si ejecutas desde Windows 10 el comando que propone Potencier, para abrir la interfaz en la web local de RabbitMQ, no funciona y arroja un error. Potencier nos pide que ejecutemos:

symfony open:local:rabbitmq

Este comando simplemente no funcionará si no realizas primero los siguientes pasos:

  1. Debemos instalar la siguiente libreria directamente desde composer.json, en la sección «require»: agregando la siguiente línea:

«php-amqplib/php-amqlib»:»^2.6″

Y luego haciendo: composer update

  1. Necesitarás instalar antes de RabbitMQ el software Erlang/OTP para luego poder instalar RabbitMQ en Windows 10.
  2. Ahora sí, descargas e instalas RabbitMQ para Winodws 10.
  3. Luego, desde la CLI de RabbitMQ, que se instala junto con el programa, debes instalar todos los plugins, ejecutando el comando:

rabbitmq-plugins enable rabbitmq-management

Luego debes hacer el siguiente cambio en el archivo php.ini

Habilitar:

allow_url_fopen set to «on»

Finalmente, para ver la interfaz de RabbitMQ en tu navegador o browser, bajo servidor Apache, escribes la dirección: localhost:15672

Aparecerá la pantalla para loguearse. Utiliza Usuario: guest, Password: guest

Luego verás la interfaz de RabbitMQ:

Symfony 5: La Vía Rápida. Paso 19.1

Este es otro ejemplo de una explicación parcial e incompleta, que supone que todos los programadores que estamos leyendo y desarrollando el ejemplo del libro de Potencier, trabajamos bajo alguna versión de Linux, y no hay siquiera una advertencia, de que la propuesta desarrollada en este punto puede no funcionar, por ejemplo, bajo Windows 10 como es mi caso. Faltan, otra vez, más explicaciones para lograr claridad expositiva.

1º) El libro no aclara que primero que nada debe instalarse la librería ejecutando desde la CLI el comando:

composer req graphp/graphviz^0.2.2

2º) Ingresando como administrador a la CLI de Windows y posicionado en la carpeta donde tienes el proyecto, para habilitarlo se debe ejecutar el comando(1):

dot -c

y verificar con el comado dot -v

3º) Ahora sí, se puede ejecutar con éxito el comando que aparece en el libro:

symfony console workflow:dump comment | dot -Tpng -o workflow.png

4º) El resultado de ejecutar el comando anterior es que podremos generar nuestro gráfico:

______________________________________________________________________________

(1) Agradezco la idea al usuario Siva Prakash en el sitio stackoverflow.com, más precisamente en este link: https://stackoverflow.com/questions/35688320/graphviz-seems-doesnt-support-png-and-map

Symfony 5: Paso 16

16.3 Diseñando una clase verificadora de spam

16.6 Comprobando comentarios en busca de spam

Seguimos comentando y haciendo nuestras modestas acotaciones al caso desarrollado por Fabien Potencier en su libro «Symfony 5: La Vía Rápida» edición en español (versión original en inglés v1.0.14, versión de la traducción: v1.0.12)

Aclaración importante del 31/08/2020: Hoy he llegado al Paso 19, y veo que allí Potencier desarrolla con profundidad, varias de las carencias de este capítulo 16. Pero en este Paso 16, nunca aclaró que se perfeccionaría con el Paso 19.

Abordaremos en conjunto los dos pasos, 16.3 y 16.6, porque van encadenados, uno depende del otro. Hubiera quedado más claro en el libro, desde el punto de vista didáctico, poner uno a continuación del otro.

El punto 16.3 se desarrolla en las páginas 164 y 165 del libro. Tal como está escrito el código se pueden recibir 3 valores de respuesta y se resumen en la página 165:

  • Si el valor de respuesta es 0: El comentario no es spam (ham).
  • Si el valor es 1: El comentario podría ser considerado spam (En realidad para Akismet es Spam, pero lo deja a consideración final del administrador del sitio).
  • Si el valor es 2: Estaríamos en un caso de un comentario que entraría en la categoría de flagrante spam (blatant spam) y sería directamente rechazado, y no grabado en la tabla de comentarios para consideración del administrador .

Sin embargo la información que recogemos en el sitio de Akismet, plantea que la API devuelve solo dos valores: (1) verdadero, cuando hay spam con alta probabilidad , o (0) falso (ham), cuando podemos considerar con alta probabilidad que el comentario enviado no es un spam. Se puede ver la explicación con más detalle en el sitio Askimet, más precisamente aquí:

https://akismet.com/development/api/#comment-check

Tambien puede leerse con provecho este link relacionado en el sitio de Akismet:

¿De donde saca Potencier 3 valores (0, 1 y 2)?

Lo que en realidad sucede, es que Akismet, es capaz de enviar, al menos, tres tipos de respuestas. Dos se engloban en la categoría de spam, y una en la categoria de no spam.

Las dos subcategorías de spam (podría ser spam y blatant spam), responden a variables distintas, y así está explicado en la página de Akismet cuyo link está citado arriba. Esas variables diferentes, son las que aprovecha Potencier para distinguir dos valores posibles para el spam.

Veámoslo un poco más en detalle. Abajo una capturadel código de la clase SpamChecker, del código de Potencier tal como está en su libro (pp. 164-165):

El tip que Potencier propone inmediatamente abajo de la enumeración de los tres resultados posibles, en la página 165, no funciona si no instalamos:

symfony composer req symfony/mime

Por cierto, desde el punto de vista didáctico, el tip está mal ubicado, ya que dicho tip debería estar al final del punto 16.6, donde recién se puede probar. De todas maneras, con ese tip no podemos probar si el código funciona cuando se devuelve el valor 2 en la función getSpamScore. El caso con valor 2, nunca se daría escribiendo solo la dirección de mail de prueba de spam como akismet-guaranteed-spam@example.com, ya que como dijimos, Akismet devuelve solo dos valores y nosotros esperamos probar tres valores. Según la lógica de Potencier, solo en el caso que la función getSpamScore reciba de Akismet la respuesta que genera el valor 2, el mensaje será rechazado, mientras que si da el valor 1, el mensaje igual es «aprobado» y no existe nada en la lógica de la función show en el archivo ConferenceController.php que advierta al administrador que se encuentra frente a un posible spam (valor 1: maybe spam) . Pero incluso si se diera el valor 2, como respuesta, no hay como probarlo en la etapa de desarrollo, así como está el código y con la dirección de prueba akismet-guaranteed-spam@example.com nunca tendríamos el caso de un valor igual a 2. No nos queda otra posibilidad que forzar a la función show para asumir el valor 2, y luego ver la respuesta del código escrito. Cuando hacemos que se ejecute el código en página 168 pero forzando a que se cumpla la identidad que está dentro del if (2 === $spamChecker->getSpamScore($comment, $context)) { throw new \RuntimeException (‘Blatant spam, go away!’)} Obtenemos una página como la siguiente, nada elegante por cierto, aunque quizás un spammer se la merezca:

Pero no cuesta mucho un mensaje más prolijo con este código, por ejemplo:

Que nos lleva a esta pantalla:

Pero aún tenemos más para decir. Si el valor que devuelve la función getSpamScore fuera 1, le podríamos facilitar el trabajo al administrador, cambiando el valor por defecto del estado del mensaje que es ‘submitted’, a un valor como por ejemplo: ‘may be spam’ Codificando, podría ser algo como lo que muestra el recuadro en rojo:

El resultado del cambio del código, se refleja en la lista de comentarios, donde ahora el comentario sospechoso de ser spam, está idetificado claramente en su columna de estado (State) como ‘may be spam’:

Bien, hasta aquí llegamos en este Paso 16 del libro. Esto es lo que queríamos aportar, espero les haya resultado de utilidad.

Symfony 5: Paso 7 y Paso 8

 

En el libro de Potencier que venimos siguiendo, el «Paso 7», está dedicado a explicar como instalar y configurar el motor de base de datos PostgreSQL. Como yo decidí trabajar con MySQL- MariaDB, las configuraciones cambian respecto a las que están en el libro.

El archivo docker-compose.yaml debería tener el siguiente contenido:

docker-compose

El archivo .symfony/services.yaml debería incluir las siguientes líneas:

db:
         type: mariadb:10.4
         disk: 1024
         size: S

El archivo .symfony.cloud.yaml incluiría, al final  las siguientes líneas:

relationships:
        database: «db:mariadb»

Y en el mismo archivo, en la sección runtime: extensions:, hablitamos MySQL del siguiente modo:

runtime:
        extensions:
               – pdo_mysql

En el libro de Potencier que venimos siguiendo, el capítulo 8 o «Paso 8» está dedicado a la estructura de datos. Debemos configurar los archivos necesarios para trabajar e interactuar con la base de datos elegida. Potencier, elige como motor de la base de datos a PostgreSQL. Yo voy a trabajar con MySQL, siendo el tipo de servidor MariaDB. En consecuencia, las configuraciones de los archivos mencionados en este capítulo del libro deben adecuarse a este motor de base de datos.

Primero que nada, tal como lo indica el libro, debemos instalar Doctrine, «un conjunto de librerías que ayudan a los desarrolladores a gestionar base de datos:

symfony composer req orm

En el archivo .env debemos habilitar la siguiente línea:

DATABASE_URL=mysql://user:password@127.0.0.1:3306/guestbook-jorge_database_1?serverVersion=mariadb-10.4.13

Donde user, debe reemplazarse por el usuario correcto, lo mismo que el password y la versión de mariaDB, que en mi caso la obtengo de PHPMyAdmin instalado junto con Xampp.

 

Symfony 5: Paso 9. Una aclaración importante

Continuando con mis observaciones al libro de Fabien Potencier, «Symfony 5: La vía rápida» (versión del 18 de Abril de 2020 en castellano), hay un capítulo que puede constituirse en un verdadero quebradero de cabeza sin algunas aclaraciones que no están en el libro, porque los hechos que generan la confusión no habían ocurrido aún.

Para ser más claro, en el «Paso 9: Configurando un panel de administración» se propone el uso del bundle EasyAdmin. A la hora de escribir el libro, este bundle, desarrollado por Javier Eguiluz, estaba en su versión 2. Justo en Junio de 2020, se actualizó a la versión 3, muy distinta a la 2. En consecuencia, cuando ejecutas el comando para instalar el bundle, tal como está en el libro:

symfony composer req admin

Potencier supone que estás instalando la versión 2 (la versión 3, no existía aún), pero en realidad composer instala la versión 3, de EasyAdmin. Luego, todo lo que se dice en ese capítulo como: «Accede al panel de administración generado en /admin. ¡Boom! Ya dispones de una interfaz de administración…» no funciona.

Update: Hoy, 23 de Octubre de 2020, he descubierto que la solución que se explica en los párrafo siguientes en color verde, ha dejado de ser efectiva. Porque aunque se trate de instalar la versión 2 de EasyAdmin, recibirás mensajes de error de composer de un conflicto con la versión de Doctrine. Además, desde que el libro salió, ya está disponible la versión 5.1 de Symfony y pronta a salir la versión 5.2, ¿por que seguir trabajando con la versión 5.0 que, en apariencia, es la única compatible con EasyAdmin 2?.  Entonces, según mi opinión, no queda otra solución que usar EasyAdmin versión 3, con lo que todo lo que está explicado en el libro «Symfony 5: La Vía Rápida», en el Paso 9 deja de funcionar. Trataré a la brevedad, de hacer una nueva entrada en este blog, para ver como instalar, y sobre todo configurar, EasyAdmin 3 en el contexto del proyecto Guestbook desarrollado en el libro.

Al día de hoy (31 de Julio de 2020) la forma correcta de proceder, para evitar confusiones al instalar el bundle EasyAdmin, si queremos seguir el desarrollo del ejemplo del libro, es ejecutar el siguiente comando:

composer require easycorp/easyadmin-bundle:2.*

O según la versión digital del libro que se encuentra en este link: https://symfony.com/doc/current/the-fast-track/es/9-backend.html

symfony composer req "admin:^2.0"

El mérito de esta solución es de un usuario del sitio  Stack Overflow, que se identifica como:

usuariostack

Además de su respuesta, es interesante leer toda la entrada, porque también hay otro usuario que propone una solución alternativa, si quieres usar la versión 3 de EasyAdmin en el contexto del libro de Potencier. La entrada completa se encuentra aquí:

https://stackoverflow.com/questions/62485384/symfony-5-easyadmin-3-0-admin-route-not-found

Esto soluciona el problema, pero deberás hacer un trabajo adicional, ya que con el comando anterior que usa composer, no se crean de modo automático los archivos siguientes, que tendrás que agregarlo manualmente:

config/packages/easy_admin.yaml

config/routes/easy_admin.yaml

Más todos los otros cambios que propone el libro en este capítulo.

Si ya tenías instalada la versión 3 de EasyAdmin, deberás desinstalarla primero:

composer remove admin