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

Comenzando con Symfony 5

Introducción

Desde hace mucho tiempo no escribo entradas para este blog. No es que me haya alejado de Symfony, sino que mis programas hasta el día de hoy seguían usando la versión 2.8, y no encontré nuevas cuestiones para principiantes que me interesara incorporar al blog.

Advertencia inicial: esta es una nota para principiantes, si se considera un usuario avanzado de Symfony 5, este artículo no es para Usted.

Hoy comienza una nueva etapa en mi experiencia de programación con Symfony. Trataré de aprender a usar la versión Symfony 5, que no solo es la última en ser desarrollada, sino que además, como dice su principal responsable Fabien Potencier en su libro «Symfony 5: La Vía Rápida»(1) se constituye en un hito, en la culminación de varios años de progreso de Symfony que parece haber alcanzado su pleno desarrollo con esta versión. En palabras textuales de Potencier: «Con la versión 5, es probable que el proyecto haya alcanzado su madurez. Siento que todo lo que hemos hecho en los últimos 5 años forma un gran conjunto. […]Nunca a sido tan divertido usar Symfony para un proyecto»(2)

Como vamos a utilizar Apache 2.4 como servidor web local, necesitamos instalar el paquete de Symfony apropiado para este tipo de servidor:

composer require symfony/apache-pack

Mi idea es, siguiendo el libro de Potencier, ir agregando entradas con mis comentarios acerca de los hallazgos, dificultades y avances en la compresión de la última versión de este potente software.

La Caja de Herramientas

Symfony puede correr en los sistemas operativos más utilizados, sea que tenga instalada alguna versión de Linux, MacOS o Windows. El primer requisito, obviamente, es disponer de una PC con alguno de estos sistemas operativos instalados.

Yo poseo una PC de escritorio con Windows 10 Pro version 1903 de 64 bits,  con 8 GB de RAM y procesador Intel Core i5 3.00 Gb. Como servidor web, instalé Xampp para Windows con la versión de PHP 7.4.7. Contar con una versión Windows 10 Pro, es indispensable para poder instalar con éxito Docker. Aunque hay una versión de Docker para Windows 7 y Windows 10 Home, no funcionan bien o es difícil de configurar.Potencier aconseja primero que nada, instalar una serie de herramientas de software que considera fundamentales para una mayor eficiencia en la programación con Symfony 5, e incluso para que el ejemplo que desarrolla a lo largo de su libro pueda funcionar. Pero lamento avisarte, que deberás hacer algún trabajo previo para llegar a tener en tu PC todas las herramientas de Symfony para desarrollar el ejemplo del libro, ya que no todo está tan a mano y hay que buscar en la Web. Las herramientas de software que sugiere Potencier en su libro son las que se enumeran a continuación.

Motor de Base de Datos y Software para gestionar mensajes

Potencier se inclina por elegir como motor de base de datos PostgreSQL y como gestionador de colas  de mensajes a RabbitMQ. Yo por mi parte, seguiré utilizando MariaDB ahora en su versión 10. Tengo toda mi experiencia de programador desarrollada con este motor de base de datos y me ha dado buenos resultados. No cabe duda que PostgreSQL es más robusto, cumple con mayor rigor que MySQL o MariaDB con una serie de requisitos de un buen motor de base de datos, pero como mis trabajos de programación hasta el momento los he desarrolado sin problemas con MariaDB, seguiré usando este motor de base de datos.

Necesitamos también un servidor web local que se complemente bien con MySQL o MariaDB, en mi caso elijo utilizar Apache 2.4. Instalo todo (motor de base datos y servidor web) a través del paquete Xampp, para mi caso, en su versión de 64 bits para Windows 10 y con PHP 7.4.

Como vamos a utilizar Apache 2.4 como servidor web local, necesitamos instalar el paquete de Symfony apropiado para este tipo de servidor:

composer require symfony/apache-pack

Este paquete, como lo explica el sitio de Symfony, instala el archivo .htaccess en la carpeta public/ que contiene las reglas necesarias para que un proyecto desarrollado con Symfony funcione bien con este servidor. Para más detalles puedes ver: https://symfony.com/doc/current/setup/web_server_configuration.html

Una vez instalado Xampp en tu PC con Windows 10, necesitas cambiar la configuración de dos archivos: php.ini y en Apache httpd.conf, a los que puedes acceder desde el panel de control de Xampp. En php.ini hay que habilitar las extensiones que exige el libro de Potencier para su correcto funcionamiento. También sería conveniente que configures la zona horaria de tu país. Y el puerto, si no deseas usar el que viene por defecto que es el puerto 80.

En el archivo de Apache httpd.conf, al final de todo, deberías incorporar esta configuración mínima que funciona bien para proyectos desarrollados con Symfony 5:

IDE (Entorno de Desarrollo Integrado)

En mi caso, como trabajo con Windows, sigo el consejo del autor del libro y elijo como IDE VisualStudioCode lo buscamos en la Web  lo descargamos e instalamos. Su uso es sin costo.

Yo por mi parte estoy acostumbrado a trabajar desde hace años con la IDE NetBeans que ya va por la versión 12. Tiene funcionalidades muy similares a VisualStudioCode y es también gratuita.

Terminal

Potencier aconseja, para los usuarios de Windows, con el fin de tener una terminal que trabaje bien, al programa Hyper. De nuevo: lo buscamos en la Web  lo descargamos e instalamos. Su uso es sin costo. También es muy útil usar el WindowsPowerShell que viene como parte de Windows.

Git

En Windows instalamos Git bash.Como lo recomienda Potencier en su libro, conviene tener un backup de nuestro proyecto en algún servidor remoto. Las copias y actualizaciones de archivos, las haremos en parte usando desde la linea de comandos, los comandos de Git, pero también resulta útil, si vas usar como respaldo a GitHub, descargar e instalar GitHubDesktop, un programa que te facilitará el respaldo de los archivos del proyecto en tu repositorio en GitHub.

PHP

Potencier sugiere tener instalado de modo local PHP, yo lo hago a través de la instalación de Xampp que en su última versión disponible al momento de escribir esta entrada contiene además del servidor Web Apache, la versión PHP 7.4.7. El autor nos pide que comprobemos (y si no están, instalarlas o habilitarlas) la existencia de las siguientes extensiones de PHP: intl , pdo_pgsql , xsl , amqp , gd , openssl y sodium También sugiere instalar o habilitar redis y curl. Instalaremos las extensiones amqp y redis ya que son necesarias para desarrollar el ejemplo del libro. En Xampp no vienen las librerias amqp y redis que habrá que buscar en repositorios de PHP en la Web.¿Dónde encontrar una extensión?Las extensiones de PHP generalmente se llaman “php_*.dll” (donde el asterisco representa el nombre de la extensión) y se localizan bajo la carpeta “php\ext”. En mi caso, como estoy usando PHP dentro de Xampp, la ruta completa sería: C:\xampp\php\extPHP se distribuye con las extensiones más útiles para la mayoría de desarrolladores. Se les llama extensiones del “núcleo”.En cualquier caso, si necesita una funcionalidad que no proporciona ninguna de las extensiones del núcleo, podrá buscarla en PECL. La Biblioteca de la Comunidad de Extensiones de PHP (PECL) es un repositorio de extensiones PHP, que proporciona un directorio de todas las extensiones conocidas, y aloja utilidades para descargar y desarrollar extensiones de PHP.Por ejemplo. La extensión amqp, la encuentras en:https://pecl.php.net/package/amqpUn artículo muy útil para instalar en forma exitosa amqp, lo encuentras aquí: https://dev.to/goceb/how-to-install-the-amqp-php-extension-on-windows–xampp-55fg Una vez descargadas estas librerías, hay que instalarlas, en el caso de Xampp, en la carpeta C:\xampp\php\ext y no olvidar habilitar las mismas introduciendo en el arhivo de configuración php.ini, las líneas:

extension=php_amqp.dll
extension=php_redis.dll

Para la extensión sodium, escribir la siguiente línera:

extension=php_sodium.dll

Composer

Otra herramienta indsipensable para trabajar con Symfony es Composer, un software que en forma automática gestiona las dependencias en cualquier programa que desarrollemos con Symfony. Otra vez: lo buscamos en la Web  lo descargamos e instalamos. Su uso es también sin costo.

Docker y Docker Composer

Programas que sirven para gestionar los servicios. Está en la Web como un solo paquete, y tiene su versión para Windows 10. Siguiendo la sugerencia de Potencier, lo decargamos e instalamos. Si no tienes totalmente actualizado Windows 10 Pro, este es uno de los programas cuya instalación más tiempo te llevará ya que requiere actualizar varias librerias de Windows, ya sea a través de Windows Update o directamente ejecutadas por el programa de instalación de Docker.

Si eres como yo un desarrollador que trabaja, en la mayoría de sus proyectos en solitario y no vas a usar la base de datos PostgreSQL ni tampoco piensas desplegar tu proyecto en SymfonyCloud, como lo sugiere Potencier. Puedes saltearte este punto.

Symfony CLI

Proporciona un servidor Web local, tiene integración completa con Docker, todo lo cual, según afirma Potencier, aumentará nuestra productividad. Puedes descargalo del sitio oficial de Symfony. En el Paso 3 del libro, más precisamente en el punto “3.5 Preparando para producción”, Potencier sugiere como una buena práctica desplegar los proyectos, incluso en la fase de construcción, en algún servidor remoto. Él sugiere contratar como servidor remoto para desplegar nuestro proyecto a SymfonyCloud, que te costará 10 dólares al mes, y si bien cualquier servidor que acepte PHP puede utilizarse, todo el libro se desarrolla suponiendo que estás trabajando con el servidor de SymfonyCloud, por lo que nos decidimos a contratarlo, por lo menos al comienzo. Una ventaja desde el incio, es que si bien se trata de un servidor remoto que permitirá desplegar tu proyecto, no es un sitio público, con lo cual solo tú puedes ver como va funcionando tu proyecto en la Web.

Instalar Autoridad de Certificación

Otro uso importante que le damos a Symfony CLI, ya desde el comienzo, es que nos permite instalar una Autoridad de Certificación (CA)  para usar localmente HTTPS y para habilitar el soporte TLS. Se debe ejecutar el siguiente comando:

symfony server:ca:install

También deberás instalar el programa Yarn que aunque no está mencionado en este primer capítulo, es requerido al chequear los programas instalados. Para poder instalr Yarn, primero necesitas instalar Node.js con todas las librerias adicionales. Para testear que tienes todas las libreria necesarias para comenzar a trabajar con el caso del libro, conviene correr desde la terminal de Windows que estemos usando (Potencier aconseja instalar para Windows, Hyper como ya dijimos antes) el siguiente comando desde la carpeta donde hallamos instalado Symfony (en mi caso he creado la carpeta C:\Symfony): symfony book:check-requirements Si todo sale bien y tienes instaladas todas las librerias que se necesitan para el ejemplo del libro, al correr el comando anterior, verás en la pantalla de tu PC algo como esto: C:\Symfony>symfony book:check-requirements
[OK] Git installed
[OK] PHP installed version 7.4.7 (C:\xampp\php\php.exe)
[OK] PHP extension “curl” installed – optional – needed only for chapter 17 (Panther)
[OK] PHP extension “xsl” installed – required
[OK] PHP extension “amqp” installed – required
[OK] PHP extension “openssl” installed – required
[OK] PHP extension “zip” installed – optional – needed only for chapter 17 (Panther)
[OK] PHP extension “json” installed – required
[OK] PHP extension “ctype” installed – required
[OK] PHP extension “redis” installed – optional – needed only for chapter 11
[OK] PHP extension “pdo_pgsql” installed – required
[OK] PHP extension “intl” installed – required
[OK] PHP extension “mbstring” installed – required
[OK] PHP extension “sodium” installed – required
[OK] PHP extension “gd” installed – optional – needed only for chapter 23 (Imagine)
[OK] PHP extension “session” installed – required
[OK] PHP extension “tokenizer” installed – required
[OK] PHP extension “xml” installed – required
[OK] Composer installed
[OK] Docker installed
[OK] Docker Compose installed
[OK] Yarn installed
[OK] Congrats! You are ready to start reading the book. 

Referencias:(1) “Symfony 5: La Vía Rápida”. Fabien Potencier. 2020. Se consigue en varios idiomas, incluyendo por supuesto el castellano, en formato PDF en el sitio oficial de Symfony por 30 euros. Está también en foma gratuita una versión del libro en el sitio oficial de Symfony pero en formato HTML. El link para adquirir la versión en español en PDF está aquí: https://leanpub.com/symfony5-la-via-rapida
(2) Symfony 5: La Vía Rápida” página 27

MYSQL: Algunos detalles importantes en las consultas con fechas

Introducción

Si trabajamos con MySQL y PHP, los campos de tipo fecha suelen presentar algunas dificultades en su manejo, cuando queremos realizar una consulta a una tabla que contenga este tipo de campo.
Mostraremos a través de dos ejemplos los detalles que hay que atender cuando queremos realizar una consulta a una tabla o conjunto de tablas relacionadas, y en dicha consulta este involucrado un campo de tipo fecha.

Ejemplo 1: Consulta de selección

Veamos la consulta que muestra la figura siguiente:
consulta_de_seleccion

Vemos que la consulta se realiza sobre tres tablas relacionadas: Contratos, Docentes y Materias. Queremos seleccionar los registros que estén dentro un intervalo de fechas que llamaremos ‘fecha_desde’ al valor menor, y ‘fecha_hasta’ al mayor valor o sea la fecha más próxima en el tiempo. Por ejemplo:

‘fecha_desde’ = ‘2016-05-01’
‘fecha_hasta’ = ‘2016-06-01’

Previamente el usuario llena un formulario, donde indica desde que fecha hasta que fecha quiere realizar la consulta de selección. Los valores seleccionados por el usuario, son enviados a la consulta por medio de la variable $filtro

Mediante el uso de la condición WHERE le pedimos a MYSQL que seleccione los registros que cumplen la condición.

Es muy importante que la forma en que están escritas las fechas límites, sea igual a la estructura del campo fecha sobre el que se va actuar. En este caso, el campo fecha solo tiene año, mes y día (no hay datos de tiempo como horas, minutos o segundos). Por otro lado el separador es el guión corto y el orden de los elementos es: Y-m-d Es decir año (con cuatro cifras)-mes-día, como podríamos verlo por medio PHPMyAdmin

Ejemplo 2: Consulta de actualización

La estructura de la consulta cambia significativamente cuando queremos actualizar o modificar algún valor de un campo fecha. Veamos la siguiente consulta:

consulta_de_actualizacion

En esta consulta queremos reemplazar el valor de un campo fecha de nombre fechaArchivo por la fecha actual si se cumple la condición WHERE. El campo tiene cuatro lugares para el año, dos lugares para el mes y dos lugares para el día. No usamos datos de tiempo.
En primer lugar guardamos la fecha actual en la variable $fecha_actual  y observemos un detalle muy importante: el formato elegido es: Ymd y no Y-m-d como teníamos en la consulta de selección. Si no hacemos esto, la fecha no se actualizará correctamente y la consulta no arrojará ningún mensaje de error, pero veremos en la tabla que los registros afectados, en lugar de tener la fecha actual, muestran. en PHPMyAdmin, una fecha con el valor ‘0000-00-00’ Esto es claramente un síntoma de que la forma en que pasamos el valor de la fecha no es entendida por MYSQL y entonces no actualiza el campo con la fecha correcta. Vemos que el nuevo valor se asigna mediante la orden SET y el valor se pasa mediante el formato Ymd.