API Platform, Symfony: añadiendo funcionalidades fácilmente

2021-09-12 - Categorías: General / PHP / Symfony

Jugueteando con esta joya del software, la API Platform, podemos construir una API Restful en muy poco tiempo. Lo siguiente que podemos necesitar es disparar acciones cuando ocurren cosas con los elementos de la API. Pero no vamos a editar las acciones de GET, POST, PUT, PATCH o DELETE, porque esto viene hecho, y es estándar su comportamiento. Si no que vamos a engancharnos a los eventos de dichas acciones, a post-procesar la información o a implementar nuevos endpoints en este post.

Es decir, una API Restful puede ser una cosa bastante simple, en la que sólo almacenamos información que podemos editar remotamente. Se pueden añadir puntos de entrada con lógica nueva y controlable desde el exterior. Pero es más interesante, que después de recibir peticiones, hagamos tareas aparte de las básicas CRUD: crear, listar, actualizar y borrar.

Creo que sólo tenemos tres formas de lanzar estas acciones nuevas:

  • Añadiendo nuevos puntos de entrada para procesar, no recomendable.
  • Programando tareas que se ejecuten cada cierto tiempo.
  • Enganchándonos a los eventos de la API en las peticiones.

La primera opción, añadiendo a la API nuevos puntos de entrada

Esta opción es la más simple que nos puede venir a la cabeza. Consiste en añadir nuevos puntos de entrada al proyecto, sobre los que lanzar peticiones para las nuevas funcionalidades.

No es muy recomendable como primera opción, pero puede ser necesario. Por ejemplo, si queremos lanzar healthChecks, recabar cierta información combinada, o realizar procesos a petición dependiendo de sistemas externos sin almacenar registro. Digo sin almacenar registro, porque si queremos registrar el uso podemos pasar a la opción 2 y 3.. después de recibir dichos mensajes, almacenamos y procesamos.

Para este caso, como tenemos un proyecto Symfony estándar porque usamos la API Platform, bastaría con lanzar el comando de consola siguiente:

php bin/console make:controller

..y seguir las instrucciones. Por ejemplo si queremos añadir un nuevo endpoint en la URL /api/new/endpoint, podríamos hacer lo siguiente:

El maker de Symfony me ha añadido el Twig además del controlador porque tengo el vendor de Twig instalado en el proyecto. Podemos eliminarlo y editar entonces el controlador a algo así por ejemplo:

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class ApiNewEndpointController extends AbstractController
{
    /**
     * @Route("/api/new/endpoint", name="api_new_endpoint")
     */
    public function index(): Response
    {
        /* Aquí el código que retorna la información que queramos */
    }
}

Si listamos los puntos de entrada al proyecto podremos ver el nuevo que hemos creado, y a programar!

La segunda opción, programando tareas en background

Aquí la idea es simple, consiste en tener un programador de tareas como el que podemos encontrar en sistemas como Magento o WordPress. Simplemente la idea es almacenar algún campo de fecha, un flag de procesado, algo que marque los registros que haya que procesar.. para luego recorrerlos y hacer lo que corresponda.

Como la API Platform es un proyecto en Symfony, me remito a cómo crear comandos de consola para luego poder lanzarlos a nivel de sistema operativo, con comandos programados como por ejemplo podrían ser:

php bin/console app:process-background-tasks
php bin/console app:process-entity-name
etc..

..luego falta programar en el cronjob de GNU/Linux estos comandos.

Y la tercera opción, lanzar de forma síncrona, enganchándonos a eventos, cuando se trabaja la información servida en la API

Para esto, al tratarse de un proyecto en Symfony, podemos simplemente escuchar ciertos eventos del framework. Es decir, podemos escuchar eventos de Doctrine, con los que nos ponemos a la espera de cambios en las entidades.

Los eventos de Doctrine se identifican como ‘doctrine.orm.entity_listener’. Aquí hay más documentación oficial sobre los listeners y sus configuraciones disponibles de Doctrine:
https://symfony.com/bundles/DoctrineBundle/current/entity-listeners.html

Unas configuraciones de ejemplo para engancharse con listeners en la creación de una entidad, podría ser esto en el fichero services.yml:

services:
    App\EventListener\NewUser:
        tags:
            - { name: 'doctrine.orm.entity_listener', event: 'postPersist', entity: 'App\Entity\User', lazy: true }

Si lo que queremos es engancharnos en la actualización de una entidad, entonces podríamos hacer algo así:

services:
    App\EventListener\UpdateUser:
        tags:
            - { name: 'doctrine.orm.entity_listener', event: 'postUpdate', entity: 'App\Entity\User', lazy: true }

Por ejemplo, el primer listener podría quedar con algo como lo siguiente en el fichero src/EventListener/NewUser:

<?php

namespace App\EventListener;

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\Event\LifecycleEventArgs;

class NewUser
{
    private $entityManager;
    private $service1;
    private $service2;

    // Aquí la inyección de dependencias..
    public function __construct(
        EntityManagerInterface $entityManager,
        Service1 $service1,
        Service2 $service2)
    {
        $this->entityManager = $entityManager;
        $this->service1 = $service1;
        $this->service2 = $service2;
    }

    public function postPersist(User $user, LifecycleEventArgs $event)
    {
        /* Some code */
    }
}

No lo he probado, pero otra opción sería usar los subscribers, es parecido a los listeners. Me remito a la documentación oficial para más información que está muy bien:
https://symfony.com/doc/current/doctrine/events.html#doctrine-lifecycle-subscribers

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

 

© 2021 JnjSite.com - MIT license

Sitio hecho con WordPress, diseño y programación del tema por Jnj.