One of the most significant changes in Drupal 8 is his integration with RESTful.
Today I want to share with you how to create Rest Resources in a custom module to publish your website information using a RESTful API.
I will create a new REST Resource with the objective to get the list of bundle types available for a specific entity.
Create a Module
I will skip the explanation about how to create a Module in Drupal 8 because could be generated using the project Drupal Console executing the following command.
$ php console.phar generate:module
Create a new REST Resource
Assuming we create a new module named entity_rest_extra is required to create a class file EntityBundlesResource.php inside the module in a folder path src/Plugin/rest/resource.
##Namespace
The namespace for this new Rest Resource will be
namespace Drupal\entityrestextra\Plugin\rest\resource;
line-numbers">">
Libraries
We must use some dependencies to create the REST Resource, below the full list.
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;
Annotations
To enable the Discover for REST Resources found our new Rest Resource we must to implement the proper information in Annotation. Check the following example.
/**
- Provides a resource to get bundles by entity.
- *
- @RestResource(
- id = "entity_bundles",
- label = @Translation("Bundles by entities"),
- uri_paths = {
- "canonical" = "/bundles/{entity}"
- }
- ) */
line-numbers">">
As you can see we provide a Resource id with a label, also we define the canonical URL for our REST Resource with a parameter named entity
Using the annotation the Discover for Rest Resources will declare the routing dynamically, so we don’t need to include a routing.yml file in our module.
Implement Class
Now we have to create a class extending from ResourceBase as you can see in the following snippet
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;
}
Our implementation require two properties for Current User and EntityManager.
Class Setup
As you can imagine each class requires a constructor and Drupal 8 is not the exception, but Drupal also implement The Factory Pattern to prepare the values to send to the constructor.
But is better to explain to you with the following example where we need to send the services: Resource Format, Logger, Entity manager and Current User to the constructor.
/**
- {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $pluginid, $plugindefinition) { return new static( $configuration, $pluginid, $plugindefinition, $container->getParameter('serializer.formats'), $container->get('logger.factory')->get('rest'), $container->get('entity.manager'), $container->get('current_user') ); }
line-numbers">">
Now we need to define the constructor where we are receiving the values from the create method, as you can see below.
/**
- 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, $pluginid, $plugindefinition, array $serializerformats, LoggerInterface $logger, EntityManagerInterface $entitymanager, AccountProxyInterface $current_user) { parent::construct($configuration, $pluginid, $plugindefinition, $serializer_formats, $logger);
$this->entityManager = $entity_manager;
$this->currentUser = $current_user;
}
line-numbers">">
Implement REST method
Last but not least we have to define the RESTful state we want to implement, in my example, I want to apply a GET method to response according to the parameters.
Inside the class with have to create a method matching the RESTful state name, so for GET, we have to implement a method named get as you can see in the following snippet.
/*
* 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'));
}
Also using the Account Proxy Interface, we can determine if Current User has enough rights to get the bundle list.
The Entity Manager Interface allows us to we get the list of bundles for Entity requested.
After that we prepare an array with the results, these results will be transformed to the proper format requested by the user using the class ResourceResponse, in our case, we will request a JSON response.
If you want to implement RESTful state POST add a method post
You can see a full and functional custom REST Resources at https://github.com/enzolutions/entity_rest_extra
Using our new Resource
Utilizing the contrib module Rest UI (I recommend to use the git version until Drupal 8 get the first release), you can enable your custom Rest Resource.
This module enables a UI to set the Authentication and format for each RESTful method implemented as you can see in the following image.
Using this setting the access to Resource will be granted or denied.
Using the Chrome Application Postman - REST Client you can execute an authenticated request to URL http://example.com/bundles/node as you can see in the following image.
If all is working as expected, you will get a similar result to following JSON output.
{ "article": "Article", "page": "Basic page" }
line-numbers">">
I hope you find this blog entry useful.