Módulo de redirecciones 301 en Symfony

Módulo de redirecciones 301 en Symfony

Symfony es un Framework PHP cuya base ha servido para la construcción de aplicaciones tales como Drupal, phpBB y eZ Publish (Ver fuente: https://symfony.com/what-is-symfony).

La presentación de hoy consiste en implementar un módulo de redirecciones 301; es decir, permanentes, para un sitio web. ¿Cuál es la razón de ello? Sucede lo siguiente: Tengo mi sitio web, en el cual la página de “Somos” tiene la siguiente URL: /somos. Sin embargo, opto por construir una nueva web donde ahora la misma información irá en la siguiente URL: /acerca-de-nosotros. Entonces, la idea es que cuando el usuario acceda a la ya conocida ruta /somos no se vea con el bochornoso error 404, sino que pueda visualizar el contenido de siempre, pero ahora quizá con otra estructura, con mejor color, forma, etc. pero en otra URL, sin que él se dé cuenta de ello.

¿Cómo lo hacemos en Symfony? Veámoslo a continuación:

Instalación del proyecto

En esta URL:  podrán encontrar una base Symfony a partir de la cual haremos la presentación del día de hoy.

Recuerden que una vez hecho fork al proyecto, deben clonarlo a su máquina local para trabajar con él. Asimismo, la aplicación de composer install es necesaria para descargar todas las dependencias del proyecto. Para saber más sobre fork pueden revisar la documentación oficial de Githubhttps://help.github.com/articles/fork-a-repo/https://help.github.com/articles/fork-a-repo/

1

Cuando termina de ejecutarse la sentencia, les solicitará añadir información sobre su base de datos y host mailer, los cuales, para el ejemplo de ahora, recomiendo que sean los siguientes:

2

Veamos los items uno por uno:

  1. database_host: Por defecto es localhost o 127.0.0.1. Cuando aparezca esta opción solo presionen para que tome el valor por defecto, a excepción que tengan un sistema distribuido y deseen apuntar hacia otra IP pero eso sí implica configuración en su otro host para permitir llamadas a esa base de datos desde otras IPs que no sea localhost.
  2. database_port: Recomiendo presionar para que asuma el puerto 3306, a excepción que tengan otro donde esté corriendo su servidor de base de datos.
  3. database_name: Creen una base de datos cualquiera en su servidor y la asignan aquí. Por ejemplo: bdredirecciones.
  4. database_user: El usuario con el que se accederá a la base de datos. Si se presiona toma por defecto a root.
  5. database_password: Es la clave del usuario.

Los siguientes valores solicitados no son necesarios por ahora así que a todo se le aplica hasta el final. Todas estas acciones generan el archivo parameters.yml en la ruta app/config.

Ahora, lo que toca es hacer las migraciones respectivas mediante el ORM Doctrine. Los comandos a ejecutar son:

php app/console doctrine:generate:entities Admin

php app/console doctrine:schema:update –force

3

Ahora sí, a levantar nuestro proyecto!!

php app/console server:run 0.0.0.0:9000

La IP 0.0.0.0 indica que desde cualquier equipo de la LAN podrán acceder a mi proyecto tan solo colocando mi IP y seguidamente el puerto, en este caso 9000.

4

Como en mi post anterior también es posible crear un Virtual Host para no tener que aplicar el comando server:run desde consola, pues como sabemos si seguimos este comando solo podremos ver la web mientras esté activa la sentencia anterior. Si aplicamos Ctrl + C dejará de funcionar, algo que no pasará con un Virtual Host.

Ahora bien, mi IP es 192.168.1.5, a la cual accedo desde el navegador de la siguiente manera:

http://192.168.1.5:9000/

Sin embargo, no veo nada. ¿Por qué? Nos dirigimos hacia el archivo home.html.twig ubicado en: src/WebBundle/Resources/views/Default/home.html.twig e identificamos que hay un código por defecto, pero el archivo _base.html.twig al cual se hace referencia desde home está vacío, por ende no se visualiza el contenido en la web. Frente a ello, optamos por colocar un código en duro que haga referencia a una página en construcción:

Archivo home.html.twig antes del cambio

5

Archivo home.html.twig después del cambio

6

Y listo!!!

7

Vayamos a lo divertido: La creación del módulo de redirecciones.

Creación del módulo de redirecciones

La migración realizada en el paso anterior involucró la creación de una tabla denominada redirecciones, la cual posee los campos desde y para, donde el primero representa a la antigua URL, la llamaremos OLD, mientras que el segundo, a la nueva, NEW.

8

La funcionalidad de identificar una URL y hacer la redirección a otra es una tarea que va a realizarla el Kernel de Symfony; es decir, ejecutará una acción luego de un KernelRequest. Para situaciones como estas se necesitan de Listeners.

Es por tal motivo que tenemos que crear una carpeta denominada EventListener dentro de src/WebBundle.

9

En esta carpeta irá un archivo llamado RedirectionListener.php. El código dentro de este documento hace referencia a una clase llamada RedirectionListener, la cual tiene un constructor donde se inicializan variables de sesiones, seguridad, etc. (Para este caso especial son opcionales). Luego, el método setEntityManager sirve para inicializar la variable $em, que permitirá acceder al ORM y por ende a la base de datos.

10

El último método es la acción a ejecutarse frente al evento onKernelRequest. Explico paso a paso ello:

  1. Hacemos referencia a la variable $em, la cual permitirá comunicarnos con la base de datos.
  2. Obtenemos la ruta actual; es decir, la que el usuario coloca en el navegador.
  3. Buscamos la ruta en la tabla redirecciones, exactamente en la columna desde.
  4. Si encuentra la ruta busca su consiguiente columna para, a la cual hará la redirección 301.

11

Esta configuración no basta, pues debemos registrar el Listener. Así que nos dirigimos hacia la ruta app/config/services.yml. Allí es necesario agregar el siguiente código:

12

Primero, se hace referencia a la clase que contiene la funcionalidad: RedirectionListener. El argumento es un service container; es decir, un servicio adicional, personalizado que se desea consumir. Como tags se tiene que mencionar a los eventos del kernel y el método que llamaremos: onKernelRequest. Finalmente y para que sea posible la comunicación con la base de datos, se tiene que hacer referencia al entity manager y el método que lo inicializará: setEntityManager.

Para probar si todo está bien al menos debemos registrar una fila en la base de datos y una vista para la ruta a la que se hace referencia.

13

El archivo de la vista lo creamos en src/WebBundle/Resources/views/Default. Éste se llamará newurl.html.twig y su contenido es el siguiente:

14

La ruta para el archivo se añadirá en src/WebBundle/Controller/DefaultController.

15

Finalmente, si en este momento en el navegador se coloca (para este caso en especial):

http://192.168.1.5:9000/old-url/

Debería llevarme a:

http://192.168.1.5:9000/new-url/

16

Éxito! Lo conseguimos. En el título de la presentación indico que es un módulo de redirecciones y pues sí, una vez que sea llamado desde el administrador de contenidos se convertirá en 100% en un módulo. Dado que no es el tema en sí, crearé un usuario administrador desde consola, y con éste accederé a los módulos del CMS.

El usuario que creé fue admin y la clave igual al nombre.

17

La URL del administrador de contenidos es:

http://192.168.1.5:9000/ingresar

Una vez dentro podemos ver en el sidebar el módulo de Redirecciones y al hacer clic aparecerá el listado de los items ya almacenados en base de datos previamente. Además, se pueden agregar más, modificarlos o eliminarlos. Es indispensable mencionar que la ruta del campo para debe ser una que exista en sus rutas creadas, por ende, para el ejempo de ahora lo que podrían cambiar sería el campo desde, salvo que por su cuenta empiecen a crear nuevas rutas o modificar la existente.

18

19

El proyecto lo pueden encontrar en mi repositorio de Github y cualquier duda quedo a disposición.

Saludos para todos y que estén muy bien 🙂

Repo: https://github.com/jbragui/symfony-redirections

Creación de RESTful API con Lumen

Creación de RESTful API con Lumen

Lumen es un Micro-Framework de Laravel, el cual dada su rapidez de respuesta a las peticiones (1900 por segundo según su página oficial) lo hacen muy útil a la hora de consumir APIs.
A continuación, les comparto un proyecto básico que realicé con Lumen en el cual construí una API para consulta y actualización de información.
El caso fue el siguiente: El aplicativo XYZ de la empresa ABC debe permitirme listar, crear o eliminar trabajadores así como registrar su ingreso y salida de la empresa en el horario de trabajo.
En esta oportunidad el sistema operativo sobre el cual trabajé fue Windows. Es por ello que tengo instalado Git bash (si tuvieras alguna distro Linux solo usa la Terminal).

Creación del proyecto.
Primero, cerciórate de tener instalado composer. Si al colocar el comando composer en tu terminal te aparece la imagen de abajo entonces todo va bien. De lo contrario, tienes que instalarlo antes de continuar con esta presentación.

composer


Luego, creamos una nueva carpeta lument-projects donde irán nuestros proyectos basados en lumen.

cd c:
cd lumen-projects


El proyecto fue creado con el comando composer create-project laravel/lumen lumen-api. Una vez culminada la instalación me dirijo a la carpeta del proyecto desde la consola. Su nombre es lumen-api.

composer create-project laravel/lumen lumen-api


Levantar el proyecto en la web.
El proyecto tiene que levantarse para verlo en la web. Tenemos dos opciones: Línea de comandos o un VirtualHost.

Línea de comandos.
Para levantar un proyecto Laravel se emplea el comando php artisan serve. Por ende, lumen, al estar basado en el primero hereda este y otros comandos. Al ejecutarlo, nos dimos con la sorpresa del siguiente error: Class “Memcached” not found. Este problema ocurrió con lumen 5.4.


Frente a lo cual la solución fue cambiar la palabra memcached por file en el archivo .env de la raíz del proyecto.

CACHE_DRIVER=file


Lumen 5.2 en adelante no tiene habilitada la sentencia php artisan serve por lo cual la forma de levantar el proyecto debe ser la siguiente: 

php -S localhost:8000 -t ./public (Ver: https://lumen.laravel.com/docs/5.4).

php -S localhost:8000 -t ./public


Una vez hecho eso se pudo ver la web en el puerto el cual le indicamos: 8000.


VirtualHost.
La otra forma de levantar el proyecto es mediante un host virtual que apunte a un puerto específico y a la carpeta public. En mi caso (Windows) me dirijo al archivo de VirtualHosts de Apache. La ruta es la siguiente: C:\newxampp\apache\conf\extra. Aquí encontramos el archivo httpd-vhosts.conf. Dentro de éste debemos añadir la siguiente configuración antes de crear cualquier host virtual:


AllowOverride All
Require all granted
Allow from all

Donde C:/lumen-projects representa a la carpeta sobre la cual se aplicará esa directiva de forma que pueda acceder al contenido de toda carpeta o archivo dentro de ésta.


Ahora, continuamos con la creación del host. Debido a que he empleado el puerto 88 para levantar mi servidor web debo indicar ese puerto en la configuración. Si ustedes emplean el 80 solo cambien donde diga 88 por ese número.


DocumentRoot C:/newxampp/htdocs
ServerName localhost


DocumentRoot C:/lumen-projects/lumen-api/public
ServerName app.lumenapi


De las líneas marcadas, lo primero es para garantizar que a todo proyecto ubicado en C:/newxampp/htdocs se pueda acceder mediante el navegador de la siguiente forma: localhost:88. Mientras que las líneas siguientes sirven para que únicamente el proyecto lumen-api sea accedido mediante el navegador desde la ruta app.lumenapi:88. Pero, ¿De dónde salió esa ruta? Para mi caso especial (Windows) debo modificar el archivo hosts, el cual solo podré cambiar si abro mi editor de texto como Administrador y luego llamo desde éste al archivo hosts. La línea agregada fue:

127.0.0.1 app.lumenapi


Reiniciamos apache. Luego, vamos al navegador y listo. Pudimos acceder sin necesidad de aplicar línea de comandos y mediante un nombre de dominio elegido por nosotros.


Creación de modelos y controladores.
Continuamos con la configuración de la base de datos en el archivo .env. El gestor de base de datos usado fue MySQL y el nombre de la base de datos dbemployees.


Necesitamos crear la tabla employees. Para ello aplicamos desde línea de comando el siguiente código: php artisan make:migration create_employees_table –create=employees.

php artisan make:migration create_employees_table –create=employees

NOTA: Si por motivo alguno eliminan manualmente el archivo de la migración generado previamente y luego quieren volver a crearlo con el comando make:migration les aparecerá un error como el siguiente:


Frente a ello deben ejecutar el comando composer dump-autoload el cual vuelve a generar los archivos autoload y permitirá ejecutar el comando make:migration con normalidad.

composer dump-autoload
make:migration

Ahora, debemos modificar el archivo de la migración con los datos que deseamos añadir. Para el trabajador tendremos los campos nombres, apellidos, área de trabajo y estado (activo o no).


En la tabla detalle donde se registrará su ingreso y salida irán como campos: fecha, hora entrada, hora salida, trabajador. Para ello también se debe crear la migración: php artisan make:migration create_employees_marking_table –create=employees_markings.

php artisan make:migration create_employees_marking_table –create=employees_markings


Ejecutamos la migración a la base de datos: php artisan migrate.

php artisan migrate


Registré un par de trabajadores a la base de datos para tener información con la cual trabajar. Asimismo, la generación de los días en la tabla employees_markings es un proceso automático que debe ejecutarse todos los días a la media noche de tal forma que los registros por trabajador ya existan en la tabla de marcación antes que los web services consuman los servicios de actualización de entrada y salida. Este proceso automático implica la llamada de un cron job que también deberá consumir un servicio.
Ahora, toca la creación de los modelos y controladores necesarios. El modelo Employees es el primero y va dentro de App: Employees.php. Su controlador será EmployeesController.php y estará en App/Http/Controllers.

Archivo Employees.php


Archivo EmployeesController.php


Se aplica lo mismo para EmployeesMarkings.

Archivo EmployeesMarking.php


Archivo EmployeesMarkingController.php


Creación de servicios web.
El listado de rutas y acciones están en la siguiente tabla:

#

Método

URL

Controlador@metodo

Acción

1 GET http://app.lumenapi:88/api/v1/marking/ EmployeesMarkingController@createMarkingList Generación de marcaciones por trabajador para el día actual.
2

GET

http://app.lumenapi:88/api/v1/employees/

EmployeesController@listEmployee

Listado de todos los trabajadores.

3

GET

http://app.lumenapi:88/api/v1/employee/{id}/

EmployeesController@getEmployee

Muestra la información de un trabajador.

4

POST

http://app.lumenapi:88/api/v1/employee/

EmployeesController@createEmployee

Registro de un nuevo trabajador.

5

PUT

http://app.lumenapi:88/api/v1/employee/in/{id}/

EmployeesController@updateEmployeeMarkingIn

Registrar ingreso de trabajador el día actual.

6

PUT

http://app.lumenapi:88/api/v1/employee/out/{id}/

EmployeesController@updateEmployeeMarkingOut

Registrar salida de trabajador el día actual.

7

DELETE

http://app.lumenapi:88/api/v1/employee/{id}/

EmployeesController@deleteEmployee

Eliminar trabajador por id.

Servicio #1.
Esta tarea debe generar para el día actual las marcaciones que serán actualizadas cuando el trabajador ingrese o salga del trabajo. Por ejemplo: Si tenemos 2 trabajadores entonces deben generarse 2 registros en esta tabla por día (En el repositorio que incluiré al final de la presentación está el backup de la base de datos).

En el archivo web.php ubicado en la carpeta routes añadimos lo siguiente:

$app->post(‘marking’, ‘EmployeesMarkingController@createMarkingList’);

Las rutas se agruparán por prefijos, pues en todas las rutas se repite api/v1.


En el controlador al que llamamos se coloca lo siguiente:


Es probable que cuando deseen visualizar el funcionamiento de este servicio web obtengan el error siguiente:

http://app.lumenapi:88/api/v1/marking/


Para solucionarlo es necesario colocar esta sentencia: $app->withEloquent() dentro del archivo app.php de bootstrap/app.php.

$app->withEloquent();

Quedaría de la siguiente forma:


En este caso como manejamos campos tipo fecha y hora entonces es necesario configurar la zona geográfica de nuestra aplicación. Eso lo hacemos en el archivo .env:

APP_TIMEZONE=America/Lima


Antes de continuar con la creación de los otros servicios ocurrió que necesitaba setear como nulos a ciertos campos de las tablas o cambiar su tamaño por lo cual se tuvo que crear otra migración con la actualización.

La primera fue para employees:


La segunda fue para employees_marking:


Sin embargo, al llamar la migración sale un error: Changing columns for table “employees” requires Doctrine DBAL; install “doctrine/dba.


Frente a este problema se tiene que agregar la línea: “doctrine/dbal”: “^2.5” dentro del composer.json:

“doctrine/dbal”: “^2.5”


Luego, ejecutar composer require doctrine/dbal. Al terminar se aplica la migración y todo corre normal:

composer require doctrine/dbal


¿De qué se encarga este servicio web?

Primero se evalúa si ya se generó el listado de marcaciones por empleado para el día actual, si ya se hizo se salta todo el proceso y se devuelve el mensaje de que ya se realizó el proceso, de lo contrario se genera la marcación por cada empleado. Si para el día 09-04-2017 aún no se han registrado las marcaciones el resultado es el siguiente:

http://app.lumenapi:88/api/v1/marking/


Si ya fueron creadas obtendríamos:


Servicio #2.
Listado de todos los trabajadores. Añadimos la ruta en el archivo web.php:

$app->get(‘employees’, ‘EmployeesController@listEmployee’);


El método en el controlador es:


Como resultado obtenemos:

http://app.lumenapi:88/api/v1/employees/


Servicio #3.
Para mostrar la información de un trabajador específico agregamos la ruta siguiente:

$app->get(‘employee/{id}’, ‘EmployeesController@getEmployee’);


En el controlador el código queda como se ve a continuación (Con un parámetro extra que es el id del trabajador):


El resultado es el siguiente para un trabajador que sí existe:

http://app.lumenapi:88/api/v1/employee/1


Para uno que no existe:

http://app.lumenapi:88/api/v1/employee/20


Servicio #4.
Para el registro de un nuevo trabajador se requiere un parámetro extra: Request. Éste llevará todos los datos de la vista al controlador y luego a la base de datos. Se agrega la nueva ruta:

$app->post(‘employee’, ‘EmployeesController@createEmployee’);


El controlador queda como se ve a continuación:


Por razones que no tenemos un aplicativo para probar el registro desde una vista no se mostrará el resultado de la acción aquí. Más adelante se probará con la herramienta POSTMAN que el registro se hace correctamente.

Servicio #5.
Registro de la hora de entrada del trabajador. La ruta queda así:

$app->put(‘employee/in/{id}’, ‘EmployeesController@updateEmployeeMarkingIn’);


El método del controlador tiene ahora dos parámetros. El primero es Request para los campos que vienen desde la vista y que serán actualizados en el objeto actual y el otro es el id, el cual servirá para buscar al objeto trabajador en la base de datos para el cual se actualizarán sus datos (en este caso se actualizará la hora de ingreso del trabajador para el día actual, por ende el parámetro Request no es necesario pero lo mantendremos en caso lo vayamos a usar más adelante).


El resultado queda así:

Para el usuario 2 no tenemos aún su marcación de entrada:


Ahora, llamamos al servicio:

http://app.lumenapi:88/api/v1/employee/in/2


Después, verificamos la base de datos y efectivamente ya actualizó el horario de entrada.


Si volvemos a ejecutar el comando no debería hacer nada, por lo cual envía un mensaje de que no encuentra resultados a la consulta hecha, pues la hora de marcación para ese día ya se actualizó.

http://app.lumenapi:88/api/v1/employee/in/2


Lo mismo si buscamos un empleado que no exista:

http://app.lumenapi:88/api/v1/employee/in/20


Servicio #6.
Para la salida del usuario se aplica el mismo procedimiento, solo que ahora se actualizará el campo marking_out. La ruta es:

$app->put(‘employee/out/{id}’, ‘EmployeesController@updateEmployeeMarkingOut’);


El controlador queda como:


Los resultados de las pruebas son parecidos a los hechos en el servicio #5.

Servicio #7.
La eliminación del trabajador implicará llevarse consigo todos los registros de marcación que tenga en la tabla employees_markings, es por ello que durante la creación de la tabla colocamos un delete on cascade.
La ruta para este servicio es la siguiente:

$app->delete(‘employee/{id}’, ‘EmployeesController@deleteEmployee’);


El controlador queda así:


La prueba de este método también se hará desde un formulario que crearemos en otra presentación del blog. Pero, más adelante también se probará con la herramienta POSTMAN.

Testing de servicios web.
Éste se puede realizar mediante el comando CURL y también con POSTMAN. Por ahora no estamos usando ningún tipo de autenticación.

Servicio #1.
Para el día 09-04-2017 ya se crearon las marcaciones, por ende el resultado del servicio web es que ya se crearon.

http://app.lumenapi:88/api/v1/marking/


Servicio #2.
http://app.lumenapi:88/api/v1/employees/


Servicio #3.
Para un empleado que no existe.

http://app.lumenapi:88/api/v1/employee/20


Para uno que sí existe.
http://app.lumenapi:88/api/v1/employee/1


Servicio #4.
Creación de nuevo trabajador.

http://app.lumenapi:88/api/v1/employee/?name=Mariana&last_name=Cotrina&state=1


En la base de datos ya aparece:


Servicio #5.
Actualización de ingreso de trabajador. Como ya se registró su ingreso para el día 09-04-2017 sale ese mensaje que se ve en la imagen de abajo.

http://app.lumenapi:88/api/v1/employee/in/1


Servicio #6.
Actualización de salida de trabajador. Como ya se registró su salida para el día 09-04-2017 sale ese mensaje que se ve en la imagen de abajo.

http://app.lumenapi:88/api/v1/employee/out/1


Servicio #7.
La eliminación del usuario que creamos en el servicio #4.

http://app.lumenapi:88/api/v1/employee/3


Vemos la base de datos. Ya no existe.


Aquí finaliza esta presentación sobre cómo crear una API RESTful con Lumen. En una próxima entrega se realizará un aplicativo que los consuma. A continuación, el repositorio donde está el código fuente del proyecto: https://github.com/jbragui/lumen-api
Finalmente, quizá algunos se pregunten cómo hacer que los resultados JSON se vean con un estilo bonito en el navegador web. Pues, es gracias a una extensión que cada navegador puede incorporar. El nombre de ésta es: JSONView.
Esto es todo amigos, tengan un gran día!

Bienvenida

Bienvenida

Bienvenidos todos!

MyTechValley es un blog creado para compartir los conocimientos que voy adquiriendo en cuanto al rubro de programación, pues es algo que me apasiona. Ello no impide que me anime a contarles experiencias vividas, pues no todo es código.

Amigos, espero que sea de su agrado!

Nos vemos en mi próximo post.