22
33namespace Drupal\ {{ machine_name }}\Plugin\rest\ resource ;
44
5- use Drupal\Component\Plugin\DependentPluginInterface ;
6- use Drupal\Core\Database\Connection ;
7- use Drupal\Core\Routing\BcRoute ;
5+ use Drupal\Core\KeyValueStore\KeyValueFactoryInterface ;
86use Drupal\rest\ModifiedResourceResponse ;
97use Drupal\rest\Plugin\ResourceBase ;
108use Drupal\rest\ResourceResponse ;
119use Psr\Log\LoggerInterface ;
1210use Symfony\Component\DependencyInjection\ContainerInterface ;
13- use Symfony\Component\HttpKernel\Exception\BadRequestHttpException ;
1411use Symfony\Component\HttpKernel\Exception\NotFoundHttpException ;
1512
1613/**
@@ -26,43 +23,49 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
2623 * )
2724 *
2825 * @DCG
29- * This plugin exposes database records as REST resources. In order to enable it
30- * import the resource configuration into active configuration storage. You may
31- * find an example of such configuration in the following file:
26+ * The plugin exposes key-value records as REST resources. In order to enable it
27+ * import the resource configuration into active configuration storage. An
28+ * example of such configuration can be located in the following file:
3229 * core/modules/rest/config/optional/rest.resource.entity.node.yml.
33- * Alternatively you can make use of REST UI module.
30+ * Alternatively you can enable it through admin interface provider by REST UI
31+ * module.
3432 * @see https://www.drupal.org/project/restui
35- * For accessing Drupal entities through REST interface use
36- * \Drupal\rest\Plugin\rest\resource\EntityResource plugin.
33+ *
34+ * @DCG
35+ * Notice that this plugin does not provide any validation for the data.
36+ * Consider creating custom normalizer to validate and normalize the incoming
37+ * data. It can be enabled in the plugin definition as follows.
38+ * @code
39+ * serialization_class = "Drupal\foo\MyDataStructure",
40+ * @endcode
41+ *
42+ * @DCG
43+ * For entities, it is recommended to use REST resource plugin provided by
44+ * Drupal core.
45+ * @see \Drupal\rest\Plugin\rest\resource\EntityResource
3746 */
38- class {{ class }} extends ResourceBase implements DependentPluginInterface {
47+ class {{ class }} extends ResourceBase {
3948
4049 /**
41- * The database connection .
50+ * The key-value storage .
4251 *
43- * @var \Drupal\Core\Database\Connection
52+ * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
4453 */
45- protected $dbConnection ;
54+ protected $storage ;
4655
4756 /**
48- * Constructs a Drupal\rest\Plugin\rest\resource\EntityResource object.
49- *
50- * @param array $configuration
51- * A configuration array containing information about the plugin instance.
52- * @param string $plugin_id
53- * The plugin_id for the plugin instance.
54- * @param mixed $plugin_definition
55- * The plugin implementation definition.
56- * @param array $serializer_formats
57- * The available serialization formats.
58- * @param \Psr\Log\LoggerInterface $logger
59- * A logger instance.
60- * @param \Drupal\Core\Database\Connection $db_connection
61- * The database connection.
57+ * {@inheritdoc}
6258 */
63- public function __construct (array $configuration , $plugin_id , $plugin_definition , array $serializer_formats , LoggerInterface $logger , Connection $db_connection ) {
64- parent :: __construct($configuration , $plugin_id , $plugin_definition , $serializer_formats , $logger );
65- $this -> dbConnection = $db_connection ;
59+ public function __construct (
60+ array $configuration ,
61+ $plugin_id ,
62+ $plugin_definition ,
63+ array $serializer_formats ,
64+ LoggerInterface $logger ,
65+ KeyValueFactoryInterface $keyValueFactory ,
66+ ) {
67+ parent :: __construct($configuration , $plugin_id , $plugin_definition , $serializer_formats , $logger , $keyValueFactory );
68+ $this -> storage = $keyValueFactory -> get(' {{ plugin_id }}' );
6669 }
6770
6871 /**
@@ -75,88 +78,81 @@ class {{ class }} extends ResourceBase implements DependentPluginInterface {
7578 $plugin_definition ,
7679 $container -> getParameter(' serializer.formats' ),
7780 $container -> get(' logger.factory' )-> get(' rest' ),
78- $container -> get(' database ' )
81+ $container -> get(' keyvalue ' )
7982 );
8083 }
8184
8285 /**
83- * Responds to GET requests.
84- *
85- * @param int $id
86- * The ID of the record.
86+ * Responds to POST requests and saves the new record.
8787 *
88- * @return \Drupal\rest\ResourceResponse
89- * The response containing the record .
88+ * @param array $data
89+ * Data to write into the database .
9090 *
91- * @throws \Symfony\Component\HttpKernel\Exception\HttpException
91+ * @return \Drupal\rest\ModifiedResourceResponse
92+ * The HTTP response object.
9293 */
93- public function get ($id ) {
94- return new ResourceResponse ($this -> loadRecord($id ));
94+ public function post (array $data ) {
95+ $data [' id' ] = $this -> getNextId();
96+ $this -> storage -> set($data [' id' ], $data );
97+ $this -> logger -> notice(' Create new {{ plugin_label|lower }} record @id.' );
98+ // Return the newly created record in the response body.
99+ return new ModifiedResourceResponse ($data , 201 );
95100 }
96101
97102 /**
98- * Responds to POST requests and saves the new record .
103+ * Responds to GET requests.
99104 *
100- * @param mixed $data
101- * Data to write into the database .
105+ * @param int $id
106+ * The ID of the record .
102107 *
103- * @return \Drupal\rest\ModifiedResourceResponse
104- * The HTTP response object .
108+ * @return \Drupal\rest\ResourceResponse
109+ * The response containing the record .
105110 */
106- public function post ($data ) {
107-
108- $this -> validate($data );
109-
110- $id = $this -> dbConnection -> insert(' {{ plugin_id }}' )
111- -> fields($data )
112- -> execute();
113-
114- $this -> logger -> notice(' New {{ plugin_label|lower }} record has been created.' );
115-
116- $created_record = $this -> loadRecord($id );
117-
118- // Return the newly created record in the response body.
119- return new ModifiedResourceResponse ($created_record , 201 );
111+ public function get ($id ) {
112+ if (! $this -> storage -> has($id )) {
113+ throw new NotFoundHttpException ();
114+ }
115+ $resource = $this -> storage -> get($id );
116+ return new ResourceResponse ($resource );
120117 }
121118
122119 /**
123- * Responds to entity PATCH requests.
120+ * Responds to PATCH requests.
124121 *
125122 * @param int $id
126123 * The ID of the record.
127- * @param mixed $data
128- * Data to write into the database .
124+ * @param array $data
125+ * Data to write into the storage .
129126 *
130127 * @return \Drupal\rest\ModifiedResourceResponse
131128 * The HTTP response object.
132129 */
133- public function patch ($id , $data ) {
134- $this -> validate($data );
135- return $this -> updateRecord($id , $data );
130+ public function patch ($id , array $data ) {
131+ if (! $this -> storage -> has($id )) {
132+ throw new NotFoundHttpException ();
133+ }
134+ $stored_data = $this -> storage -> get($id );
135+ $data + = $stored_data ;
136+ $this -> storage -> set($id , $data );
137+ $this -> logger -> notice(' The {{ plugin_label|lower }} record @id has been updated.' );
138+ return new ModifiedResourceResponse ($data , 200 );
136139 }
137140
138141 /**
139- * Responds to entity DELETE requests.
142+ * Responds to DELETE requests.
140143 *
141144 * @param int $id
142145 * The ID of the record.
143146 *
144147 * @return \Drupal\rest\ModifiedResourceResponse
145148 * The HTTP response object.
146- *
147- * @throws \Symfony\Component\HttpKernel\Exception\HttpException
148149 */
149150 public function delete ($id ) {
150-
151- // Make sure the record still exists.
152- $this -> loadRecord($id );
153-
154- $this -> dbConnection -> delete(' {{ plugin_id }}' )
155- -> condition(' id' , $id )
156- -> execute();
157-
158- $this -> logger -> notice(' {{ plugin_label }} record @id has been deleted.' , [' @id' => $id ]);
159-
151+ if (! $this -> storage -> has($id )) {
152+ throw new NotFoundHttpException ();
153+ }
154+ $this -> storage -> delete($id );
155+ $this -> logger -> notice(' The {{ plugin_label|lower }} record @id has been deleted.' , [' @id' => $id ]);
160156 // Deleted responses have an empty body.
161157 return new ModifiedResourceResponse (NULL , 204 );
162158 }
@@ -166,119 +162,19 @@ class {{ class }} extends ResourceBase implements DependentPluginInterface {
166162 */
167163 protected function getBaseRoute ($canonical_path , $method ) {
168164 $route = parent :: getBaseRoute($canonical_path , $method );
169-
170- // Change ID validation pattern.
165+ // Set ID validation pattern.
171166 if ($method ! = ' POST' ) {
172167 $route -> setRequirement(' id' , ' \d+' );
173168 }
174-
175169 return $route ;
176170 }
177171
178172 /**
179- * {@inheritdoc}
180- */
181- public function calculateDependencies () {
182- return [];
183- }
184-
185- /**
186- * {@inheritdoc}
187- */
188- public function routes () {
189- $collection = parent :: routes();
190-
191- // Take out BC routes added in base class.
192- // @see https://www.drupal.org/node/2865645
193- // @todo Remove this in Drupal 9.
194- foreach ($collection as $route_name => $route ) {
195- if ($route instanceof BcRoute ) {
196- $collection -> remove($route_name );
197- }
198- }
199-
200- return $collection ;
201- }
202-
203- /**
204- * Validates incoming record.
205- *
206- * @param mixed $record
207- * Data to validate.
208- *
209- * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
210- */
211- protected function validate ($record ) {
212- if (! is_array ($record ) || count ($record ) == 0 ) {
213- throw new BadRequestHttpException (' No record content received.' );
214- }
215-
216- $allowed_fields = [
217- ' title' ,
218- ' description' ,
219- ' price' ,
220- ];
221-
222- if (count (array_diff (array_keys ($record ), $allowed_fields )) > 0 ) {
223- throw new BadRequestHttpException (' Record structure is not correct.' );
224- }
225-
226- if (empty ($record [' title' ])) {
227- throw new BadRequestHttpException (' Title is required.' );
228- }
229- elseif (isset ($record [' title' ]) && strlen ($record [' title' ]) > 255 ) {
230- throw new BadRequestHttpException (' Title is too big.' );
231- }
232- // @DCG Add more validation rules here.
233- }
234-
235- /**
236- * Loads record from database.
237- *
238- * @param int $id
239- * The ID of the record.
240- *
241- * @return array
242- * The database record.
243- *
244- * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
173+ * Returns next available ID.
245174 */
246- protected function loadRecord ($id ) {
247- $record = $this -> dbConnection -> query(' SELECT * FROM {{ ' {' }}{{ plugin_id }}{{ ' }' }} WHERE id = :id' , [' :id' => $id ])-> fetchAssoc();
248- if (! $record ) {
249- throw new NotFoundHttpException (' The record was not found.' );
250- }
251- return $record ;
252- }
253-
254- /**
255- * Updates record.
256- *
257- * @param int $id
258- * The ID of the record.
259- * @param array $record
260- * The record to validate.
261- *
262- * @return \Drupal\rest\ModifiedResourceResponse
263- * The HTTP response object.
264- */
265- protected function updateRecord ($id , array $record ) {
266-
267- // Make sure the record already exists.
268- $this -> loadRecord($id );
269-
270- $this -> validate($record );
271-
272- $this -> dbConnection -> update(' {{ plugin_id }}' )
273- -> fields($record )
274- -> condition(' id' , $id )
275- -> execute();
276-
277- $this -> logger -> notice(' {{ plugin_label }} record @id has been updated.' , [' @id' => $id ]);
278-
279- // Return the updated record in the response body.
280- $updated_record = $this -> loadRecord($id );
281- return new ModifiedResourceResponse ($updated_record , 200 );
175+ private function getNextId () {
176+ $ids = \array_keys ($this -> storage -> getAll());
177+ return count ($ids ) > 0 ? max ($ids ) + 1 : 1 ;
282178 }
283179
284180}
0 commit comments