annotate core/modules/rest/src/Routing/ResourceRoutes.php @ 19:fa3358dc1485 tip

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