manager = $manager; $this->resourceConfigStorage = $entity_type_manager->getStorage('rest_resource_config'); $this->logger = $logger; } /** * Alters existing routes for a specific collection. * * @param \Drupal\Core\Routing\RouteBuildEvent $event * The route build event. */ public function onDynamicRouteEvent(RouteBuildEvent $event) { // Iterate over all enabled REST resource config entities. /** @var \Drupal\rest\RestResourceConfigInterface[] $resource_configs */ $resource_configs = $this->resourceConfigStorage->loadMultiple(); foreach ($resource_configs as $resource_config) { if ($resource_config->status()) { $resource_routes = $this->getRoutesForResourceConfig($resource_config); $event->getRouteCollection()->addCollection($resource_routes); } } } /** * Provides all routes for a given REST resource config. * * This method determines where a resource is reachable, what path * replacements are used, the required HTTP method for the operation etc. * * @param \Drupal\rest\RestResourceConfigInterface $rest_resource_config * The rest resource config. * * @return \Symfony\Component\Routing\RouteCollection * The route collection. */ protected function getRoutesForResourceConfig(RestResourceConfigInterface $rest_resource_config) { $plugin = $rest_resource_config->getResourcePlugin(); $collection = new RouteCollection(); foreach ($plugin->routes() as $name => $route) { /** @var \Symfony\Component\Routing\Route $route */ // @todo: Are multiple methods possible here? $methods = $route->getMethods(); // Only expose routes that have an explicit method and allow >=1 format // for that method. if (($methods && ($method = $methods[0]) && $rest_resource_config->getFormats($method))) { $route->setRequirement('_csrf_request_header_token', 'TRUE'); // Check that authentication providers are defined. if (empty($rest_resource_config->getAuthenticationProviders($method))) { $this->logger->error('At least one authentication provider must be defined for resource @id', ['@id' => $rest_resource_config->id()]); continue; } // Check that formats are defined. if (empty($rest_resource_config->getFormats($method))) { $this->logger->error('At least one format must be defined for resource @id', ['@id' => $rest_resource_config->id()]); continue; } // The configuration has been validated, so we update the route to: // - set the allowed response body content types/formats for methods // that may send response bodies (unless hardcoded by the plugin) // - set the allowed request body content types/formats for methods that // allow request bodies to be sent (unless hardcoded by the plugin) // - set the allowed authentication providers if (in_array($method, ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'], TRUE) && !$route->hasRequirement('_format')) { $route->addRequirements(['_format' => implode('|', $rest_resource_config->getFormats($method))]); } if (in_array($method, ['POST', 'PATCH', 'PUT'], TRUE) && !$route->hasRequirement('_content_type_format')) { $route->addRequirements(['_content_type_format' => implode('|', $rest_resource_config->getFormats($method))]); } $route->setOption('_auth', $rest_resource_config->getAuthenticationProviders($method)); $route->setDefault('_rest_resource_config', $rest_resource_config->id()); $parameters = $route->getOption('parameters') ?: []; $route->setOption('parameters', $parameters + [ '_rest_resource_config' => [ 'type' => 'entity:' . $rest_resource_config->getEntityTypeId(), ], ]); $collection->add("rest.$name", $route); } } return $collection; } /** * {@inheritdoc} */ public static function getSubscribedEvents(): array { $events[RoutingEvents::DYNAMIC] = 'onDynamicRouteEvent'; return $events; } }