3 namespace Drupal\{{ machine_name }}\Plugin\rest\resource;
5 use Drupal\Component\Plugin\DependentPluginInterface;
6 use Drupal\Core\Database\Connection;
7 use Drupal\rest\ModifiedResourceResponse;
8 use Drupal\rest\Plugin\ResourceBase;
9 use Drupal\rest\ResourceResponse;
10 use Psr\Log\LoggerInterface;
11 use Symfony\Component\DependencyInjection\ContainerInterface;
12 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
13 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
16 * Represents {{ plugin_label }} records as resources.
19 * id = "{{ plugin_id }}",
20 * label = @Translation("{{ plugin_label }}"),
22 * "canonical" = "/api/{{ plugin_id|u2h }}/{id}",
23 * "https://www.drupal.org/link-relations/create" = "/api/{{ plugin_id|u2h }}"
28 * This plugin exposes database records as REST resources. In order to enable it
29 * import the resource configuration into active configuration storage. You may
30 * find an example of such configuration in the following file:
31 * core/modules/rest/config/optional/rest.resource.entity.node.yml.
32 * Alternatively you can make use of REST UI module.
33 * @see https://www.drupal.org/project/restui
34 * For accessing Drupal entities through REST interface use
35 * \Drupal\rest\Plugin\rest\resource\EntityResource plugin.
37 class {{ class }} extends ResourceBase implements DependentPluginInterface {
40 * The database connection.
42 * @var \Drupal\Core\Database\Connection
44 protected $dbConnection;
47 * Constructs a Drupal\rest\Plugin\rest\resource\EntityResource object.
49 * @param array $configuration
50 * A configuration array containing information about the plugin instance.
51 * @param string $plugin_id
52 * The plugin_id for the plugin instance.
53 * @param mixed $plugin_definition
54 * The plugin implementation definition.
55 * @param array $serializer_formats
56 * The available serialization formats.
57 * @param \Psr\Log\LoggerInterface $logger
59 * @param \Drupal\Core\Database\Connection $db_connection
60 * The database connection.
62 public function __construct(array $configuration, $plugin_id, $plugin_definition, array $serializer_formats, LoggerInterface $logger, Connection $db_connection) {
63 parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);
64 $this->dbConnection = $db_connection;
70 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
75 $container->getParameter('serializer.formats'),
76 $container->get('logger.factory')->get('rest'),
77 $container->get('database')
82 * Responds to GET requests.
85 * The ID of the record.
87 * @return \Drupal\rest\ResourceResponse
88 * The response containing the record.
90 * @throws \Symfony\Component\HttpKernel\Exception\HttpException
92 public function get($id) {
93 return new ResourceResponse($this->loadRecord($id));
97 * Responds to POST requests and saves the new record.
99 * @param mixed $record
100 * Data to write into the database.
102 * @return \Drupal\rest\ModifiedResourceResponse
103 * The HTTP response object.
105 public function post($record) {
107 $this->validate($record);
109 $id = $this->dbConnection->insert('{{ plugin_id }}')
113 $this->logger->notice('New {{ plugin_label|lower }} record has been created.');
115 $created_record = $this->loadRecord($id);
117 // Return the newly created record in the response body.
118 return new ModifiedResourceResponse($created_record, 201);
122 * Responds to entity PATCH requests.
125 * The ID of the record.
126 * @param mixed $record
127 * Data to write into the database.
129 * @return \Drupal\rest\ModifiedResourceResponse
130 * The HTTP response object.
132 public function patch($id, $record) {
133 $this->validate($record);
134 return $this->updateRecord($id, $record);
138 * Responds to entity PUT requests.
141 * The ID of the record.
142 * @param mixed $record
143 * Data to write into the database.
145 * @return \Drupal\rest\ModifiedResourceResponse
146 * The HTTP response object.
148 public function put($id, $record) {
150 $this->validate($record);
152 // Provide default values to make sure the record is completely replaced.
159 return $this->updateRecord($id, $record);
163 * Responds to entity DELETE requests.
166 * The ID of the record.
168 * @return \Drupal\rest\ModifiedResourceResponse
169 * The HTTP response object.
171 * @throws \Symfony\Component\HttpKernel\Exception\HttpException
173 public function delete($id) {
175 // Make sure the record still exists.
176 $this->loadRecord($id);
178 $this->dbConnection->delete('{{ plugin_id }}')
179 ->condition('id', $id)
182 $this->logger->notice('{{ plugin_label }} record @id has been deleted.', ['@id' => $id]);
184 // Deleted responses have an empty body.
185 return new ModifiedResourceResponse(NULL, 204);
191 protected function getBaseRoute($canonical_path, $method) {
192 $route = parent::getBaseRoute($canonical_path, $method);
194 // Change ID validation pattern.
195 if ($method != 'POST') {
196 $route->setRequirement('id', '\d+');
205 public function calculateDependencies() {
212 public function routes() {
213 $collection = parent::routes();
215 // ResourceBase class does not support PUT method by some reason.
216 $definition = $this->getPluginDefinition();
217 $canonical_path = $definition['uri_paths']['canonical'];
218 $route = $this->getBaseRoute($canonical_path, 'PUT');
219 $route->addRequirements(['_content_type_format' => implode('|', $this->serializerFormats)]);
220 $collection->add('{{ plugin_id }}.PUT', $route);
226 * Validates incoming record.
228 * @param mixed $record
231 * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
233 protected function validate($record) {
234 if (!is_array($record) || count($record) == 0) {
235 throw new BadRequestHttpException('No record content received.');
244 if (count(array_diff(array_keys($record), $allowed_fields)) > 0) {
245 throw new BadRequestHttpException('Record structure is not correct.');
248 if (empty($record['title'])) {
249 throw new BadRequestHttpException('Title is required.');
251 elseif (isset($record['title']) && strlen($record['title']) > 255) {
252 throw new BadRequestHttpException('Title is too big.');
254 // @DCG Add more validation rules here.
258 * Loads record from database.
261 * The ID of the record.
264 * The database record.
266 * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
268 protected function loadRecord($id) {
269 $record = $this->dbConnection->query('SELECT * FROM {{ '{' }}{{ plugin_id }}{{ '}' }} WHERE id = :id', [':id' => $id])->fetchAssoc();
271 throw new NotFoundHttpException('The record was not found.');
280 * The ID of the record.
281 * @param array $record
282 * The record to validate.
284 * @return \Drupal\rest\ModifiedResourceResponse
285 * The HTTP response object.
287 protected function updateRecord($id, array $record) {
289 // Make sure the record already exists.
290 $this->loadRecord($id);
292 $this->validate($record);
294 $this->dbConnection->update('{{ plugin_id }}')
296 ->condition('id', $id)
299 $this->logger->notice('{{ plugin_label }} record @id has been updated.', ['@id' => $id]);
301 // Return the updated record in the response body.
302 $updated_record = $this->loadRecord($id);
303 return new ModifiedResourceResponse($updated_record, 200);