Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / modules / rest / src / Routing / ResourceRoutes.php
1 <?php
2
3 namespace Drupal\rest\Routing;
4
5 use Drupal\Core\Entity\EntityTypeManagerInterface;
6 use Drupal\Core\Routing\RouteBuildEvent;
7 use Drupal\Core\Routing\RoutingEvents;
8 use Drupal\rest\Plugin\Type\ResourcePluginManager;
9 use Drupal\rest\RestResourceConfigInterface;
10 use Psr\Log\LoggerInterface;
11 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
12 use Symfony\Component\Routing\RouteCollection;
13
14 /**
15  * Subscriber for REST-style routes.
16  */
17 class ResourceRoutes implements EventSubscriberInterface {
18
19   /**
20    * The plugin manager for REST plugins.
21    *
22    * @var \Drupal\rest\Plugin\Type\ResourcePluginManager
23    */
24   protected $manager;
25
26   /**
27    * The REST resource config storage.
28    *
29    * @var \Drupal\Core\Entity\EntityManagerInterface
30    */
31   protected $resourceConfigStorage;
32
33   /**
34    * A logger instance.
35    *
36    * @var \Psr\Log\LoggerInterface
37    */
38   protected $logger;
39
40   /**
41    * Constructs a RouteSubscriber object.
42    *
43    * @param \Drupal\rest\Plugin\Type\ResourcePluginManager $manager
44    *   The resource plugin manager.
45    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
46    *   The entity type manager
47    * @param \Psr\Log\LoggerInterface $logger
48    *   A logger instance.
49    */
50   public function __construct(ResourcePluginManager $manager, EntityTypeManagerInterface $entity_type_manager, LoggerInterface $logger) {
51     $this->manager = $manager;
52     $this->resourceConfigStorage = $entity_type_manager->getStorage('rest_resource_config');
53     $this->logger = $logger;
54   }
55
56   /**
57    * Alters existing routes for a specific collection.
58    *
59    * @param \Drupal\Core\Routing\RouteBuildEvent $event
60    *   The route build event.
61    * @return array
62    */
63   public function onDynamicRouteEvent(RouteBuildEvent $event) {
64     // Iterate over all enabled REST resource config entities.
65     /** @var \Drupal\rest\RestResourceConfigInterface[] $resource_configs */
66     $resource_configs = $this->resourceConfigStorage->loadMultiple();
67     foreach ($resource_configs as $resource_config) {
68       if ($resource_config->status()) {
69         $resource_routes = $this->getRoutesForResourceConfig($resource_config);
70         $event->getRouteCollection()->addCollection($resource_routes);
71       }
72     }
73   }
74
75   /**
76    * Provides all routes for a given REST resource config.
77    *
78    * This method determines where a resource is reachable, what path
79    * replacements are used, the required HTTP method for the operation etc.
80    *
81    * @param \Drupal\rest\RestResourceConfigInterface $rest_resource_config
82    *   The rest resource config.
83    *
84    * @return \Symfony\Component\Routing\RouteCollection
85    *   The route collection.
86    */
87   protected function getRoutesForResourceConfig(RestResourceConfigInterface $rest_resource_config) {
88     $plugin = $rest_resource_config->getResourcePlugin();
89     $collection = new RouteCollection();
90
91     foreach ($plugin->routes() as $name => $route) {
92       /** @var \Symfony\Component\Routing\Route $route */
93       // @todo: Are multiple methods possible here?
94       $methods = $route->getMethods();
95       // Only expose routes
96       // - that have an explicit method and allow >=1 format for that method
97       // - that exist for BC
98       // @see \Drupal\rest\RouteProcessor\RestResourceGetRouteProcessorBC
99       if (($methods && ($method = $methods[0]) && $supported_formats = $rest_resource_config->getFormats($method)) || $route->hasOption('bc_route')) {
100         $route->setRequirement('_csrf_request_header_token', 'TRUE');
101
102         // Check that authentication providers are defined.
103         if (empty($rest_resource_config->getAuthenticationProviders($method))) {
104           $this->logger->error('At least one authentication provider must be defined for resource @id', [':id' => $rest_resource_config->id()]);
105           continue;
106         }
107
108         // Check that formats are defined.
109         if (empty($rest_resource_config->getFormats($method))) {
110           $this->logger->error('At least one format must be defined for resource @id', [':id' => $rest_resource_config->id()]);
111           continue;
112         }
113
114         // Remove BC routes for unsupported formats.
115         if ($route->getOption('bc_route') === TRUE) {
116           $format_requirement = $route->getRequirement('_format');
117           if ($format_requirement && !in_array($format_requirement, $rest_resource_config->getFormats($method))) {
118             continue;
119           }
120         }
121
122         // The configuration has been validated, so we update the route to:
123         // - set the allowed response body content types/formats for methods
124         //   that may send response bodies (unless hardcoded by the plugin)
125         // - set the allowed request body content types/formats for methods that
126         //   allow request bodies to be sent (unless hardcoded by the plugin)
127         // - set the allowed authentication providers
128         if (in_array($method, ['GET', 'HEAD', 'POST', 'PUT', 'PATCH'], TRUE) && !$route->hasRequirement('_format')) {
129           $route->addRequirements(['_format' => implode('|', $rest_resource_config->getFormats($method))]);
130         }
131         if (in_array($method, ['POST', 'PATCH', 'PUT'], TRUE) && !$route->hasRequirement('_content_type_format')) {
132           $route->addRequirements(['_content_type_format' => implode('|', $rest_resource_config->getFormats($method))]);
133         }
134         $route->setOption('_auth', $rest_resource_config->getAuthenticationProviders($method));
135         $route->setDefault('_rest_resource_config', $rest_resource_config->id());
136         $parameters = $route->getOption('parameters') ?: [];
137         $route->setOption('parameters', $parameters + [
138           '_rest_resource_config' => [
139             'type' => 'entity:' . $rest_resource_config->getEntityTypeId(),
140           ],
141         ]);
142         $collection->add("rest.$name", $route);
143       }
144
145     }
146     return $collection;
147   }
148
149   /**
150    * {@inheritdoc}
151    */
152   public static function getSubscribedEvents() {
153     $events[RoutingEvents::DYNAMIC] = 'onDynamicRouteEvent';
154     return $events;
155   }
156
157 }