Diciembre 19, 2014
chef
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.
- 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
- 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
- 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.
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.
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.