Objects de Liferay y Remote Apps: ¿Cómo podemos conectarlos?

Introducción

El desarrollo en Liferay ha ido cambiando a lo largo de los años con sus distintas versiones. Con las versiones 6.x teníamos los portlets de tipo WAR. Cuando llegó la versión 7.0 los módulos OSGi pasaron a ser el estándar. Y ahora, 7.4 nos ha traído algo totalmente nuevo: Remote APPs. Si queréis una explicación más detallada sobre Remote Apps podéis echar un vistazo a este otro blog  y a la documentación oficial.

 

Pero, ¿qué son estas Remote Apps? Como el nombre nos sugiere son aplicaciones que no están desplegadas dentro de Liferay Portal/DXP. Utilizan la infraestructura front-end de Liferay para registrar aplicaciones externas en la plataforma y renderizarlas como widgets. Por ello, como hacen uso de la infraestructura front-end de Liferay, estas aplicaciones pueden ser desarrolladas utilizando un amplio abanico de tecnologías y/o frameworks, como React, Angular o Vue. Genial, ¿no?

 

Otra funcionalidad que hizo su aparición con 7.4 es Objects. ¿Recordáis Service Builder? Bien, Objects es su hermano. Un hermano pequeño de momento, pero en el futuro será su hermano mayor. Con Objects puedes crear entidades que son persistidas en la base de datos, igual que se hace con Service Builder. La diferencia principal es que con Objects puedes hacer esto desde la interfaz de usuario, sin nada de código y sin despliegues. Además, cada Object creado no solo añadirá su propia tabla a la base de datos, también dispondrán de una API Rest que podrás utilizar para crear, modificar o eliminar estas entidades.

 

Habiendo comentado estos dos aspectos quizá sepas de lo que voy a hablar en este blog (si no lo habías adivinado ya por el título, claro). Tradicionalmente, cuando se quería modelar una entidad y trabajar con ella en Liferay tenías que implementar un módulo de tipo Service Builder para crear y gestionar dicha entidad y después crear otro módulo de tipo MVC para crear la parte de la vista. Eso hace un total de tres módulos (módulos api y servicio de Service Builder y el módulo MVC) que deben ser desplegados, probados, actualizados, mantenidos… Una cantidad de tiempo importante.

 

Con Objects puedes dejar la parte de modelado de datos y mantenimiento de los mismos  a Liferay, y con Remote Apps puedes utilizar la tecnología de tu elección para crear la vista. La mejor parte de todo esto es que no es necesario realizar despliegues en absoluto. No hay necesidad de paralizar todo para actualizar tu aplicación. Pero, ¿cómo se hace todo esto? ¿Y cómo podemos conectar nuestras Remote Apps a las APIs de Objects? Vamos a averiguarlo, ¿os parece?

 

Lo primero de todo es comentar qué clase de Remote App vamos a crear. En este caso vamos a crear una Remote App con React que muestre una serie de marcadores en un mapa de España. Como el territorio de España se divide en comunidades autónomas, y estas a su vez en provincias, esta jerarquía es un buen ejemplo de cómo podemos relacionar Objects y de las consultas que podemos ejecutar. ¡Comencemos!
 

Arrancar Liferay y crear los objetos necesarios

La versión de producto que voy a utilizar en este blog es Liferay DXP 7.4 U27. Y para utilizar esta versión voy a hacer uso de su imagen docker. Por ello, para arrancar nuestra instancia de Liferay DXP ejecutaremos el siguiente comando:

docker run --name liferay-7.4-u27 -p 8080:8080 -d liferay/dxp:7.4.13-u27


Una vez nuestro servidor haya arrancado nos autenticaremos para poder acceder al Panel de control y de ahí a la sección de Objects (Control Panel → Object → Objects).

 

Desde esta interfaz haremos click en el botón azul con el símbolo de suma para poder añadir nuestros Objects. El primero que vamos a crear es el objeto  Autonomous Communities (Comunidades autónomas), por lo que pondremos “Community” (Comunidad) en su etiqueta y “Communities” (Comunidades) como su etiqueta en plural. Hacemos click en guardar y veremos que se ha creado nuestro nuevo objeto con un estado “draft” (borrador).

A continuación vamos a añadirle algunos campos. Para ello, hacemos click en la pestaña “Fields” (Campos) y después en el botón con el símbolo de suma. Se mostrará otro formulario donde podremos añadir la información sobre el campo a añadir. El primero, Label (etiqueta), es simplemente el nombre del objeto que veremos en la interfaz de usuario. El segundo, Field Name (nombre de campo), es el nombre que se utilizará para acceder al campo en el código. El siguiente, Type (tipo), es un desplegable donde podremos elegir el tipo de campo (numérico, texto, etc) que queremos almacenar en nuestro objeto. Por último, tendremos una casilla de verificación donde indicar si el campo que estamos añadiendo es obligatorio o no. Para nuestro objeto Community vamos a añadir un campo obligatorio de tipo Text llamado Name.

Añadido este campo podemos volver a la pestaña Details (detalles) y modificar el campo Entry display para que utilice el campo Name que acabamos de crear. Una vez hecho este cambio también vamos a modificar el campo Panel category key a “Applications > Content” de forma que podamos ver este objeto en esa sección del panel de control. Realizados estos pasos podemos publicar nuestro objeto para que esté totalmente disponible. Algo importante a tener en cuenta es que todos los campos creados mientras el objeto esté en estado draft no se podrán eliminar una vez se haya publicado, por lo que hay que tener cuidado con los campos añadidos antes de su publicación.

 

El siguiente objeto que vamos a crear es el objeto Provinces (Provincias). Vamos a seguir el mismo procedimiento que con el objeto Community, utilizando como etiqueta “Province” y “Provinces” como etiqueta en plural. El resto de opciones se dejan igual que con el objeto Community, incluidos los campos.

 

El último objeto que vamos a utilizar es el objeto Point (Punto). Este objeto también seguirá la estructura de los dos objetos anteriores, usando Point como etiqueta, Points como etiqueta en plural y dos campos adicionales además de Name: Latitude (Latitud) y Longitude (Longitud). Estos dos campos serán del tipo Precision Decimal y también obligatorios. Añadidos estos dos campos publicamos el objeto como se hizo con los demás.

 

A continuación vamos a relacionar estos objetos. Como comentaba anteriormente, cada provincia pertenece a una comunidad, por lo que vamos a representarlo. Accedemos al objeto Community y vamos a la pestaña Relationships (Relaciones). Desde aquí vamos a crear una nueva relación “Community Province” de tipo “One to many” (Uno a muchos) (cada provincia pertenece a una comunidad y una comunidad tiene varias provincias) con el objeto Province.

Ahora cuando accedemos al objeto Province veremos que tiene un nuevo campo: “Community Province”. Este campo es el ID del objeto Community al que pertenece.

 

El siguiente paso es añadir la relación entre Point y Province, ya que cada punto aparecerá en una provincia. Los pasos a seguir son los mismos que con las comunidades y las provincias: accedemos al objeto Province, vamos a la pestaña Relationships y crear una nueva relación One to many llamada “Province Point” con Point. Con esto podemos saber a qué provincia pertenece cada punto y a qué comunidad pertenece cada provincia.

 

Antes de continuar vamos a añadir otra relación One to many entre Community y Point. ¿Por qué? Esto hará nuestro código más sencillo. Sin embargo, técnicamente, esto no se considera una buena práctica y no debería realizarse de esta forma para todos los casos. Proporcionaré una explicación más detallada cuando lleguemos a la sección de este código.

 

El último paso es añadir algunos objetos de ejemplo. Para hacerlo únicamente debemos acceder al objeto que queramos (desde Applications > Content, como indicamos en Panel category key). Los objetos que voy a crear son los siguientes:

  • Community

  1. Name: Comunidad de Madrid

  2. Name: Castilla - La Mancha

  • Province

  1. Name: Madrid           Community Province: Comunidad de Madrid

  2. Name: Toledo            Community Province: Castilla - La Mancha

  3. Name: Albacete         Community Province: Castilla - La Mancha

  4. Name: Cuenca           Community Province: Castilla - La Mancha

  5. Name: Ciudad Real    Community Province: Castilla - La Mancha

  6. Name: Guadalajara    Community Province: Castilla - La Mancha

  • Point

  1. Name: Point in Madrid Latitude: 40.47628 Longitude: -3.68592

Community Point: Comunidad de Madrid Province Point: Madrid

  1. Name: Point in Toledo Latitude: 39.8602 Longitude: -4.0251

Community Point: Castilla - La Mancha Province Point: Toledo

  1. Name: Point in Albacete Latitude: 38.9957 Longitude: -1.8564

Community Point:  Castilla - La Mancha Province Point: Albacete
 

Creando la Remote App

Ahora que tenemos datos podemos continuar con nuestra Remote App. Como hemos visto anteriormente, esta Remote App va a ser creada con React, por lo que empezaremos con un esqueleto de proyecto básico. Para crearlo ejecutamos el siguiente comando:

npx create-react-app show-points-app

 

Esto creará una aplicación React básica desde donde podremos comenzar.

Primero, actualizaremos las dependencias que vamos a necesitar en el archivo package.json. Eliminaremos las dependencias @testing y también las de web-vitals y añadiremos sass, leaflet y react-leaflet. Vamos a utilizar sass para darle estilo a nuestra aplicación y leaflet para mostrar el mapa. El archivo final package.json está disponible aquí. Con las dependencias actualizadas solo hace falta ejecutar

npm install

en el directorio del proyecto para que los paquetes de las nuevas dependencias se descarguen y los de las dependencias eliminadas sean también eliminados de nuestra carpeta node_modules.

 

El siguiente paso es crear una carpeta de nombre “components” dentro de la carpeta “src”. Aquí es donde tendremos nuestros componentes React (para más información sobre componentes React consulta la documentación oficial. Además, siempre puedes encontrar componentes ya hechos en internet, muchos de ellos totalmente gratis). A continuación crearemos una carpeta “common” dentro de “src”. Esta carpeta tendrá a su vez dos carpetas más: “styles” y “services”. Esta última tendrá una carpeta llamada “liferay”. En la carpeta “styles” tendremos nuestros archivos css/scss para estilar la aplicación y en “services” los servicios que podamos necesitar. 

 
 
Estructura final del directorio
 

En nuestro caso vamos a necesitar alguna forma de conectar nuestra aplicación con la API de Objects de Liferay, por lo que crearemos un archivo “api.js” dentro de la carpeta “services/liferay”. En este archivo crearemos una función para trabajar con las peticiones HTTP que haremos a las APIs de Liferay. El código a desarrollar está disponible aquí.

 

El siguiente paso es crear nuestros componentes. En esta aplicación tendremos dos: SearchMap y OpenMap.

 

Vamos a comenzar con OpenMap. Crearemos un archivo llamado “OpenMap.js” dentro de la carpeta “components” y añadiremos el código necesario para mostrar el mapa y los marcadores necesarios. El mapa será generado usando Leaflet, una librería js de código abierto que utiliza OpenStreetMap. Además, como estamos creando una aplicación React, usaremos también React Leaflet para abstraer las capas de Leaflet como componentes React. El código a implementar se encuentra aquí. Para que el mapa se muestre correctamente es necesario el CSS de Leaflet. Por ello, vamos a importarlo en un archivo llamado “OpenMap.scss” en la carpeta “common/styles”. El código para ello se halla aquí.

 

El siguiente paso es crear el componente SearchMap. En este componente tendremos dos desplegables para filtrar los puntos a mostrar en el mapa, además de añadir el mapa directamente en el componente. Además, haremos las llamadas a las APIs desde aquí. El código necesario está disponible aquí. En las funciones getCommunities y getProvinces podemos ver que estamos llamando a la API proporcionando la URL de la API del objeto como argumento al servicio LiferayApi que creamos anteriormente. Para ver las URLs de las APIs de los objetos creados solo es necesario acceder a “nombreDeHost/o/api” y desde aquí hacer click en el desplegable en la esquina superior derecha “REST Applications”. En esta interfaz se pueden consultar las APIs que ofrece Liferay, incluidas las generadas cuando creamos objetos.

Como estas APIs son APIs REST podemos no solo recuperar datos, sino también crearlos, actualizarlos o eliminarlos. Además, también podemos filtrar las peticiones para que los datos devueltos reúnan los requisitos que indiquemos. Esto es lo que se hace en la función updatePointsInMap, donde únicamente los puntos que reúnen los requisitos de filtrado son devueltos por la API. Para evitar tener que hacer dos llamadas a la API se hace uso de las relaciones con Province y Community, así solo se pueden recuperar los datos con una única llamada en caso de que solo se use el filtro por comunidad.

 

Por último, es la hora de mostrar nuestros componentes. Como SearchMap ya tiene OpenMap en su valor de retorno únicamente debemos llamar al componente SearchMap en el archivo index.js. Además, debemos definir nuestro elemento personalizado (custom element) con un identificador (id). Todo el código necesario se encuentra aquí. Cabe destacar que el identificador del elemento personalizado se encuentra definido en la línea 19, identificador que usaremos para registrar la Remote App en nuestra instancia de Liferay.
 

Añadir la Remote App a nuestra instancia de Liferay

Con todo el código listo estamos preparados para añadirlo a nuestra instancia de Liferay. ¿Cómo? Primero debemos construir la aplicación utilizando el siguiente comando:

yarn build

 

en la raíz del directorio de la aplicación (el porqué estoy usando yarn en vez de npm viene explicado en este blog). Este comando creará una carpeta “build” que tendrá a su vez una carpeta “static”. En esta carpeta estarán los archivos js y css transpilados que necesitaremos añadir a nuestra instancia de Liferay. En concreto, necesitaremos los archivos cuyo formato de nombre es del tipo main.abc123.js y main.cde456.css. Vamos a añadir estos dos archivos y subirlos a la biblioteca de documentos de Liferay.

Con nuestro código en Liferay podemos registrar nuestra Remote App. Para ello, accederemos al menú de Remote Apps a través de la pestaña de aplicaciones (haciendo click en el cuadrado de puntitos en la esquina superior derecha). Desde aquí, crearemos una Remote App llamada “Search map”. Elegimos “custom element” como tipo, el mismo ID que le asignamos a la aplicación en su código (search-map-app) como nombre del elemento HTML y añadimos las URLs de nuestros archivos JS y CSS en los campos URL y CSS URL respectivamente. Como tenemos estos archivos en la biblioteca de documentos podemos obtener sus URLs haciendo click en el archivo y a continuación en el icono de información de la esquina superior derecha, mostrándose la URL de la última versión del archivo.

No obstante, en un entorno productivo real estos archivos deberían estar en un servidor optimizado para almacenar recursos estáticos (en este caso será necesario, a su vez, habilitar CORS. Para ello se puede consultar la documentación de Liferay).

 

La configuración final será así:

Realizados estos pasos podemos añadir nuestra aplicación a cualquier página de contenido que queramos. Esto se consigue de la misma manera que con cualquier otro fragmento o widget: arrastrando y soltando.

¡Ya tenemos nuestra Remote App React en Liferay! Ahora puedes trastear con ella, probando los filtros, añadiendo más puntos o incluso puedes subir el nivel y probar a darle estilo a la aplicación para hacerla ver mejor.
 

Ventajas de este nuevo paradigma de desarrollo y conclusiones finales

Como hemos podido ir viendo en este blog, utilizando Objects puedes evitar tener que desarrollar módulos enteros de tipo Service Builder para modelar tus datos. Además, Objects está integrado con frameworks de Liferay como el Asset Framework y el Permissions Framework, por lo que permite que el modelado de datos sea mucho más rápido y sencillo sin tener que escribir nada de código. Así mismo, como Objects está gestionado por el producto no tendrás código que actualizar cuando actualices tu versión de Liferay Portal/DXP.

 

Con el enfoque de Remote Apps puedes desarrollar aplicaciones utilizables en Liferay con tecnologías modernas, como React (nuestro caso), Angular, Vue… Igualmente, puedes acceder a servicios y datos de Liferay gracias a sus APIs REST, disponibles también con nuestros Objects.

 

Puedes encontrar todo el código utilizado en este blog en este repositorio, además de los esquemas de los objetos diseñados en esta carpeta.

 

Espero que encontréis este blog útil para vuestros futuros proyectos con Liferay Portal/DXP 7.4. Si tenéis cualquier consejo, duda o sugerencia por favor dejadlas como comentario aquí abajo. ¡Muchas gracias por leer este blog!


 
Blogs

Excelente explicación Francisco, sin lugar a dudas esto expone un enorme potencial de lo que podemos lograr con Liferay Objects y Remote Apps.