Hero

Cómo crear un recurso REST en Drupal 8

Diciembre 19, 2014

chef
Drupal

Uno de los mayores cambios en la Drupal 8 es su integración con RESTfull.

Hoy quiero compartir con ustedes cómo crear recursos REST propios en un módulo personalizado para publicar datos de su web mediante una API REST.

Voy a crear un nuevo recurso REST con el objetivo de obtener la lista de bundles disponibles para una entidad específica.

  1. Crear modulo

Voy a omitir la explicación acerca de cómo crear un módulo en Drupal 8 porque se podría generar con el proyecto Drupal Console ejecutando el siguiente comando.

$ php console.phar generate:module
  1. Crear un nuevo recurso REST

Suponiendo que creamos un nuevo módulo llamado entity_rest_extra debemos crear un archivo de clase EntityBundlesResource.php dentro del módulo en una carpeta src/Plugin/rest/resource.

2.1 Namespace

El namespace para este nuevo recurso Resto sería

namespace Drupal\entity_rest_extra\Plugin\rest\resource;

2.2 Librerías

Debemos resolver algunas dependencias para crear el recurso REST, a continuación la lista completa de estas dependencias

use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\rest\Plugin\ResourceBase;
use Drupal\rest\ResourceResponse;
use Drupal\Core\Session\AccountProxyInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Psr\Log\LoggerInterface;

2.3 Anotaciones

En Drupal 8 Existe un Discover que detecta los REST Resources, Para que el Discover encuentre nuestro nuevo Recurso REST tenemos para aplicar la información adecuada en la anotación. Como se muestra el siguiente ejemplo.

/**
 * Provides a resource to get bundles by entity.
 *
 * @RestResource(
 *   id = "entity_bundles",
 *   label = @Translation("Bundles by entities"),
 *   uri_paths = {
 *     "canonical" = "/bundles/{entity}"
 *   }
 * )
 */

Como se puede ver, se proporciona un Resource id con una label, también definimos la URL canónica para nuestro Recurso REST con un nombre de entidad como parámetro.

El uso de la anotación para Discover para REST declarará el enrutamiento de forma dinámica por lo que no es necesario incluir un archivo routing.yml en nuestro módulo.

2.4 Implementar Clase

Ahora tenemos que crear una clase que extienda la clase ResourceBase como se puede ver en el siguiente fragmento

class EntityBundlesResource extends ResourceBase {
  /**
   *  A curent user instance.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;
  /**
   *  A instance of entity manager.
   *
   * @var \Drupal\Core\Entity\EntityManagerInterface
   */
  protected $entityManager;
}

Nuestra implementación requieren dos propiedades para el CurrentUser y el EntityManager.

2.5 Configurar clase

Como se puede imaginar cada clase requiere un constructor y Drupal 8 no es la excepción, pero Drupal también implementa el patrón de diseño Factory para preparar los valores a enviar al constructor.

Pero es mejor para explicar esto con el siguiente ejemplo en el que tenemos que enviar los servicios de Resource Format, Logger, Entity Manager y Current User al constructor.

/**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->getParameter('serializer.formats'),
      $container->get('logger.factory')->get('rest'),
      $container->get('entity.manager'),
      $container->get('current_user')
    );
  }

Ahora tenemos que definir el constructor donde estamos recibiendo los valores del método de create, como se puede ver a continuación.

/**
  * Constructs a Drupal\rest\Plugin\ResourceBase object.
  *
  * @param array $configuration
  *   A configuration array containing information about the plugin instance.
  * @param string $plugin_id
  *   The plugin_id for the plugin instance.
  * @param mixed $plugin_definition
  *   The plugin implementation definition.
  * @param array $serializer_formats
  *   The available serialization formats.
  * @param \Psr\Log\LoggerInterface $logger
  *   A logger instance.
  */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    array $serializer_formats,
    LoggerInterface $logger,
    EntityManagerInterface $entity_manager,
    AccountProxyInterface $current_user) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);

    $this->entityManager = $entity_manager;
    $this->currentUser = $current_user;
  }

2.6 Implementar metodo REST

Por último, pero no menos importante, tenemos que definir el método REST que deseamos implementar, en mi ejemplo yo quiero poner en práctica un método GET dependiendo de un nombre de entidad enviado como parámetro.

Dentro de la clase se debe crear un método que coincida con el nombre del REST State, por lo que para GET tenemos que poner en práctica un método llamado get como se puede ver en el siguiente fragmento.

/*
   * Responds to GET requests.
   *
   * Returns a list of bundles for specified entity.
   *
   * @return \Drupal\rest\ResourceResponse
   *   The response containing a list of bundle names.
   *
   * @throws \Symfony\Component\HttpKernel\Exception\HttpException
   */
  public function get($entity = NULL) {
    if ($entity) {
      $permission = 'Administer content types';
      if(!$this->currentUser->hasPermission($permission)) {
        throw new AccessDeniedHttpException();
      }
      $bundles_entities = \Drupal::entityManager()->getStorage($entity .'_type')->loadMultiple();
      $bundles = array();
      foreach ($bundles_entities as $entity) {
        $bundles[$entity->id()] = $entity->label();
      }
      if (!empty($bundles)) {
        return new ResourceResponse($bundles);
      }
      throw new NotFoundHttpException(t('Bundles for entity @entity were not found', array('@entity' => $entity)));
    }

    throw new HttpException(t('Entity wasn\'t provided'));
  }

Como se puede ver el método recibe como parámetro el último valor de pasar a la URL.

También usando el objeto de tipo Account Proxy Interface podemos determinar si el usuario actual tiene suficientes derechos para obtener la lista de bundles.

El Entity Manager nos permite obtener la lista de bundles asociados a la de Entidad solicitada.

Después de eso se debe preparar los resultados, estos resultados serán transformadas al formato adecuado solicitado por el usuario mediante el ResourceResponse clase, en nuestro caso vamos a solicitar una respuesta tipo JSON.

Si desea implementar REST State Post sólo debe añadir un método de nombre post.

Usted puede ver una implementación completa y funcional Recursos REST en https://github.com/enzolutions/entity_rest_extra

  1. Usando el nuevo Recurso Rest

Con el módulo contribuido Rest UI (recomiendo usar la versión git hasta que Drupal 8 tenga un release estable), puede permitir a su costumbre de Recursos Rest.

Este módulo permite mediante una interfaz de usuario configurar la autenticación y el formato para cada método REST implementado como se puede ver en la siguiente imagen.

restui bundle entities settings

El uso de esta configuración permite o deniega el acceso a los recursos REST.

Utilizando la aplicación Chrome Postman - REST Client se puede ejecutar una solicitud autenticada a URL http://example.com/bundles/node como se puede ver en la siguiente imagen.

postman rest request

Si todo funciona como se esperaba obtendrá un resultado similar al siguiente salida JSON.

{
  "article": "Article",
  "page": "Basic page"
}

Espero que esta entrada de blog haya sido de su agrado.

Recibe consejos y oportunidades de trabajo 100% remotas y en dólares de weKnow Inc.