Construyendo un RestFull API con Symfony y AngularJS parte 2

June 18, 2021

Tags: IT Staff ES 2024
Share

Table of contents

Quick Access

api

 

En esta sección, vamos a continuar con la creación de una API RESTful usando Symfony. Para ello, instalaremos el resto de las dependencias necesarias con Composer, generaremos nuestra entidad, y completaremos un CRUD utilizando varios componentes de Symfony.

 

Instalación de dependencias

Primero, instalaremos las dependencias necesarias:

 
composer require nelmio/api-doc-bundle 

 

Este comando instala NelmioApiDocBundle, un bundle que facilita la creación de documentación legible y eficiente para nuestra API, y permite probar los servicios directamente desde la interfaz generada.

 

Configuración de NelmioApiDocBundle

Una vez instalado el bundle, el siguiente paso es cargar sus rutas. Para hacerlo, editamos el archivo routing.yml ubicado en app/config:

 
NelmioApiDocBundle:  resource: "@NelmioApiDocBundle/Resources/config/routing.yml"  prefix: /api/doc 

 

Esto genera una página accesible en /api/doc, donde se mostrará la documentación de la API.

 

Especificaciones de las rutas

Las especificaciones de las rutas se definen mediante anotaciones en los controladores. Aquí un ejemplo de cómo documentar una acción:

 
/** * @ApiDoc( *   description="Descripción de lo que la ruta va a hacer", *   section="Sección para agrupar funcionalidad", *   statusCodes={ *     200="Estado exitoso", *     500="Error no esperado" *   }, *   parameters={ *     {"name"="nombre del parámetro", "dataType"="tipo de dato", "required"=true, "description"="Descripción del parámetro"} *   } * ) */ public function testAction() {} 

 

JMSSerializerBundle

El siguiente componente es JMSSerializerBundle, que amplía la funcionalidad del serializador de Symfony, permitiendo, por ejemplo, ocultar ciertos campos de un objeto al momento de serializarlo. Este bundle también es utilizado por FOSRestBundle para adaptar la respuesta según el formato solicitado por la API (JSON o XML).

 

Generación de la entidad

Para generar nuestra entidad, utilizamos el siguiente comando de Doctrine:

 
php bin/console doctrine:generate:entity 

 

Esto genera una clase Task con un campo content de tipo texto y un campo created_at de tipo datetime. Posteriormente, configuramos la serialización con JMSSerializerBundle, asegurando que los campos sean requeridos y que created_at tenga un valor por defecto:

 
setCreatedAt(new \DateTime()); 

 

Además, utilizamos las clases ExclusionPolicy, Exclude y Assert para controlar cómo se serializan los campos y para agregar validaciones.

 

Creación del formulario

Para crear el formulario de la clase Task, usamos el siguiente comando:

 
php bin/console doctrine:generate:form TaskBundle:Task 

 

En el formulario generado, eliminamos el campo created_at y deshabilitamos la protección CSRF para evitar problemas con nuestra API RESTful:

 
add('content'); 

 

Y configuramos las opciones del formulario:

 
public function configureOptions(OptionsResolver $resolver) {    $resolver->setDefaults(array(        'data_class' => 'TaskBundle\Entity\Task',        'csrf_protection' => false    )); } 

 

Generación de rutas

Ahora, configuramos las rutas en Symfony. Primero, modificamos routing.yml para indicar que nuestro controlador es de tipo REST:

 
task:  resource: "@TaskBundle/Controller/"  type: rest  prefix: / 

 

Luego, definimos las rutas en el controlador, importando las clases necesarias y extendiendo de FOSRestController:

 
use FOS\RestBundle\Controller\FOSRestController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use TaskBundle\Entity\Task; use TaskBundle\Form\TaskType; use FOS\RestBundle\Controller\Annotations as Rest; use Nelmio\ApiDocBundle\Annotation\ApiDoc; class DefaultController extends FOSRestController {    // Acciones del controlador

 

Acciones CRUD en el controlador

Ahora, vamos a crear las acciones para manejar las operaciones CRUD:

  1. Listar tareas (GET)
 
/** * @Rest\Get("task") * @ApiDoc( *   description="Retorna la lista de tareas", *   section="Task", *   statusCodes={ *     200="Éxito", *     500="Error inesperado" *   } * ) */ public function getListAction() {    $tasks = $this->getDoctrine()->getRepository('TaskBundle:Task')->findAll();    return $this->handleView($this->view(array('list' => $tasks))); } 
  1. Obtener tarea por ID (GET)
 
/** * @Rest\Get("task/{id}") * @ApiDoc( *   description="Retorna una tarea por su ID", *   section="Task", *   statusCodes={ *     200="Encontrado", *     404="No encontrado" *   }, *   parameters={ *     {"name"="id", "dataType"="integer", "required"=true, "description"="ID de la tarea"} *   } * ) */ public function getAction(Task $task) {    return $this->handleView($this->view(array('task' => $task))); } 
  1. Eliminar tarea (DELETE)
 
/** * @Rest\Delete("task/{id}") * @ApiDoc( *   description="Elimina una tarea por su ID", *   section="Task", *   statusCodes={ *     204="Eliminado", *     404="No encontrado" *   }, *   parameters={ *     {"name"="id", "dataType"="integer", "required"=true, "description"="ID de la tarea"} *   } * ) */ public function deleteAction(Task $task) {    $em = $this->getDoctrine()->getManager();    $em->remove($task);    $em->flush();    return $this->handleView($this->view(null, 204)); } 
  1. Crear tarea (POST)
 
/** * @Rest\Post("task") * @ApiDoc( *   description="Crea una nueva tarea", *   section="Task", *   statusCodes={ *     201="Creada", *     400="Datos inválidos" *   }, *   parameters={ *     {"name"="content", "dataType"="string", "required"=true, "description"="Contenido de la tarea"} *   } * ) */ public function postAction(Request $request) {    return $this->processForm(new Task(), $request, true); } 
  1. Actualizar tarea (PUT)
 
/** * @Rest\Put("task/{id}") * @ApiDoc( *   description="Actualiza una tarea existente", *   section="Task", *   statusCodes={ *     204="Actualizada", *     400="Datos inválidos" *   } * ) */ public function putAction(Task $task, Request $request) {    return $this->processForm($task, $request); } 

 

Método processForm

El método processForm es crucial, ya que maneja la validación y persistencia de los datos:

 
private function processForm(Task $task, Request $request, $new = false) {    $statusCode = $new ? 201 : 204;    $form = $this->createForm(TaskType::class, $task);    $data = $request->request->all();    $children = $form->all();    $toBind = array_intersect_key($data, $children);    $form->submit($toBind);    if ($form->isValid()) {        $em = $this->getDoctrine()->getManager();        $em->persist($task);        $em->flush();        return $this->handleView($this->view($new ? $task : null, $statusCode));    }    return $this->handleView($this->view($form, 400)); } 

 

Con esto, hemos completado la creación de una API RESTful utilizando Symfony, FOSRestBundle, NelmioApiDocBundle, JMSSerializerBundle y otros componentes importantes para manejar las rutas, la serialización de datos, la validación de formularios, y la documentación de la API.

 

Te recomendamos este video