3 namespace Drupal\rest\Plugin;
5 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
6 use Drupal\Core\Plugin\PluginBase;
7 use Psr\Log\LoggerInterface;
8 use Symfony\Component\DependencyInjection\ContainerInterface;
9 use Symfony\Component\Routing\Route;
10 use Symfony\Component\Routing\RouteCollection;
13 * Common base class for resource plugins.
15 * Note that this base class' implementation of the permissions() method
16 * generates a permission for every method for a resource. If your resource
17 * already has its own access control mechanism, you should opt out from this
18 * default permissions() method by overriding it.
20 * @see \Drupal\rest\Annotation\RestResource
21 * @see \Drupal\rest\Plugin\Type\ResourcePluginManager
22 * @see \Drupal\rest\Plugin\ResourceInterface
25 * @ingroup third_party
27 abstract class ResourceBase extends PluginBase implements ContainerFactoryPluginInterface, ResourceInterface {
30 * The available serialization formats.
34 protected $serializerFormats = [];
39 * @var \Psr\Log\LoggerInterface
44 * Constructs a Drupal\rest\Plugin\ResourceBase object.
46 * @param array $configuration
47 * A configuration array containing information about the plugin instance.
48 * @param string $plugin_id
49 * The plugin_id for the plugin instance.
50 * @param mixed $plugin_definition
51 * The plugin implementation definition.
52 * @param array $serializer_formats
53 * The available serialization formats.
54 * @param \Psr\Log\LoggerInterface $logger
57 public function __construct(array $configuration, $plugin_id, $plugin_definition, array $serializer_formats, LoggerInterface $logger) {
58 parent::__construct($configuration, $plugin_id, $plugin_definition);
59 $this->serializerFormats = $serializer_formats;
60 $this->logger = $logger;
66 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
71 $container->getParameter('serializer.formats'),
72 $container->get('logger.factory')->get('rest')
77 * Implements ResourceInterface::permissions().
79 * Every plugin operation method gets its own user permission. Example:
80 * "restful delete entity:node" with the title "Access DELETE on Node
83 public function permissions() {
85 $definition = $this->getPluginDefinition();
86 foreach ($this->availableMethods() as $method) {
87 $lowered_method = strtolower($method);
88 $permissions["restful $lowered_method $this->pluginId"] = [
89 'title' => $this->t('Access @method on %label resource', ['@method' => $method, '%label' => $definition['label']]),
98 public function routes() {
99 $collection = new RouteCollection();
101 $definition = $this->getPluginDefinition();
102 $canonical_path = isset($definition['uri_paths']['canonical']) ? $definition['uri_paths']['canonical'] : '/' . strtr($this->pluginId, ':', '/') . '/{id}';
103 $create_path = isset($definition['uri_paths']['https://www.drupal.org/link-relations/create']) ? $definition['uri_paths']['https://www.drupal.org/link-relations/create'] : '/' . strtr($this->pluginId, ':', '/');
105 $route_name = strtr($this->pluginId, ':', '.');
107 $methods = $this->availableMethods();
108 foreach ($methods as $method) {
109 $route = $this->getBaseRoute($canonical_path, $method);
113 $route->setPath($create_path);
114 $collection->add("$route_name.$method", $route);
119 // Restrict GET and HEAD requests to the media type specified in the
120 // HTTP Accept headers.
121 foreach ($this->serializerFormats as $format_name) {
122 // Expose one route per available format.
123 $format_route = clone $route;
124 $format_route->addRequirements(['_format' => $format_name]);
125 $collection->add("$route_name.$method.$format_name", $format_route);
130 $collection->add("$route_name.$method", $route);
139 * Provides predefined HTTP request methods.
141 * Plugins can override this method to provide additional custom request
145 * The list of allowed HTTP request method strings.
147 protected function requestMethods() {
164 public function availableMethods() {
165 $methods = $this->requestMethods();
167 foreach ($methods as $method) {
168 // Only expose methods where the HTTP request method exists on the plugin.
169 if (method_exists($this, strtolower($method))) {
170 $available[] = $method;
177 * Gets the base route for a particular method.
179 * @param string $canonical_path
180 * The canonical path for the resource.
181 * @param string $method
182 * The HTTP method to be used for the route.
184 * @return \Symfony\Component\Routing\Route
185 * The created base route.
187 protected function getBaseRoute($canonical_path, $method) {
188 return new Route($canonical_path, [
189 '_controller' => 'Drupal\rest\RequestHandler::handle',
191 $this->getBaseRouteRequirements($method),
195 // The HTTP method is a requirement for this route.
201 * Gets the base route requirements for a particular method.
204 * The HTTP method to be used for the route.
207 * An array of requirements for parameters.
209 protected function getBaseRouteRequirements($method) {
210 $lower_method = strtolower($method);
211 // Every route MUST have requirements that result in the access manager
212 // having access checks to check. If it does not, the route is made
213 // inaccessible. So, we default to granting access to everyone. If a
214 // permission exists, then we add that below. The access manager requires
215 // that ALL access checks must grant access, so this still results in
221 // Only specify route requirements if the default permission exists. For any
222 // more advanced route definition, resource plugins extending this base
223 // class must override this method.
224 $permission = "restful $lower_method $this->pluginId";
225 if (isset($this->permissions()[$permission])) {
226 $requirements['_permission'] = $permission;
229 return $requirements;