annotate core/modules/jsonapi/src/Routing/Routes.php @ 5:12f9dff5fda9 tip

Update to Drupal core 8.7.1
author Chris Cannam
date Thu, 09 May 2019 15:34:47 +0100
parents
children
rev   line source
Chris@5 1 <?php
Chris@5 2
Chris@5 3 namespace Drupal\jsonapi\Routing;
Chris@5 4
Chris@5 5 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
Chris@5 6 use Drupal\jsonapi\Access\RelationshipFieldAccess;
Chris@5 7 use Drupal\jsonapi\Controller\EntryPoint;
Chris@5 8 use Drupal\jsonapi\ParamConverter\ResourceTypeConverter;
Chris@5 9 use Drupal\jsonapi\ResourceType\ResourceType;
Chris@5 10 use Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface;
Chris@5 11 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
Chris@5 12 use Symfony\Component\DependencyInjection\ContainerInterface;
Chris@5 13 use Symfony\Component\Routing\Route;
Chris@5 14 use Symfony\Component\Routing\RouteCollection;
Chris@5 15
Chris@5 16 /**
Chris@5 17 * Defines dynamic routes.
Chris@5 18 *
Chris@5 19 * @internal JSON:API maintains no PHP API since its API is the HTTP API. This
Chris@5 20 * class may change at any time and this will break any dependencies on it.
Chris@5 21 *
Chris@5 22 * @see https://www.drupal.org/project/jsonapi/issues/3032787
Chris@5 23 * @see jsonapi.api.php
Chris@5 24 */
Chris@5 25 class Routes implements ContainerInjectionInterface {
Chris@5 26
Chris@5 27 /**
Chris@5 28 * The service name for the primary JSON:API controller.
Chris@5 29 *
Chris@5 30 * All resources except the entrypoint are served by this controller.
Chris@5 31 *
Chris@5 32 * @var string
Chris@5 33 */
Chris@5 34 const CONTROLLER_SERVICE_NAME = 'jsonapi.entity_resource';
Chris@5 35
Chris@5 36 /**
Chris@5 37 * A key with which to flag a route as belonging to the JSON:API module.
Chris@5 38 *
Chris@5 39 * @var string
Chris@5 40 */
Chris@5 41 const JSON_API_ROUTE_FLAG_KEY = '_is_jsonapi';
Chris@5 42
Chris@5 43 /**
Chris@5 44 * The route default key for the route's resource type information.
Chris@5 45 *
Chris@5 46 * @var string
Chris@5 47 */
Chris@5 48 const RESOURCE_TYPE_KEY = 'resource_type';
Chris@5 49
Chris@5 50 /**
Chris@5 51 * The JSON:API resource type repository.
Chris@5 52 *
Chris@5 53 * @var \Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface
Chris@5 54 */
Chris@5 55 protected $resourceTypeRepository;
Chris@5 56
Chris@5 57 /**
Chris@5 58 * List of providers.
Chris@5 59 *
Chris@5 60 * @var string[]
Chris@5 61 */
Chris@5 62 protected $providerIds;
Chris@5 63
Chris@5 64 /**
Chris@5 65 * The JSON:API base path.
Chris@5 66 *
Chris@5 67 * @var string
Chris@5 68 */
Chris@5 69 protected $jsonApiBasePath;
Chris@5 70
Chris@5 71 /**
Chris@5 72 * Instantiates a Routes object.
Chris@5 73 *
Chris@5 74 * @param \Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface $resource_type_repository
Chris@5 75 * The JSON:API resource type repository.
Chris@5 76 * @param string[] $authentication_providers
Chris@5 77 * The authentication providers, keyed by ID.
Chris@5 78 * @param string $jsonapi_base_path
Chris@5 79 * The JSON:API base path.
Chris@5 80 */
Chris@5 81 public function __construct(ResourceTypeRepositoryInterface $resource_type_repository, array $authentication_providers, $jsonapi_base_path) {
Chris@5 82 $this->resourceTypeRepository = $resource_type_repository;
Chris@5 83 $this->providerIds = array_keys($authentication_providers);
Chris@5 84 assert(is_string($jsonapi_base_path));
Chris@5 85 assert(
Chris@5 86 $jsonapi_base_path[0] === '/',
Chris@5 87 sprintf('The provided base path should contain a leading slash "/". Given: "%s".', $jsonapi_base_path)
Chris@5 88 );
Chris@5 89 assert(
Chris@5 90 substr($jsonapi_base_path, -1) !== '/',
Chris@5 91 sprintf('The provided base path should not contain a trailing slash "/". Given: "%s".', $jsonapi_base_path)
Chris@5 92 );
Chris@5 93 $this->jsonApiBasePath = $jsonapi_base_path;
Chris@5 94 }
Chris@5 95
Chris@5 96 /**
Chris@5 97 * {@inheritdoc}
Chris@5 98 */
Chris@5 99 public static function create(ContainerInterface $container) {
Chris@5 100 return new static(
Chris@5 101 $container->get('jsonapi.resource_type.repository'),
Chris@5 102 $container->getParameter('authentication_providers'),
Chris@5 103 $container->getParameter('jsonapi.base_path')
Chris@5 104 );
Chris@5 105 }
Chris@5 106
Chris@5 107 /**
Chris@5 108 * {@inheritdoc}
Chris@5 109 */
Chris@5 110 public function routes() {
Chris@5 111 $routes = new RouteCollection();
Chris@5 112 $upload_routes = new RouteCollection();
Chris@5 113
Chris@5 114 // JSON:API's routes: entry point + routes for every resource type.
Chris@5 115 foreach ($this->resourceTypeRepository->all() as $resource_type) {
Chris@5 116 $routes->addCollection(static::getRoutesForResourceType($resource_type, $this->jsonApiBasePath));
Chris@5 117 $upload_routes->addCollection(static::getFileUploadRoutesForResourceType($resource_type, $this->jsonApiBasePath));
Chris@5 118 }
Chris@5 119 $routes->add('jsonapi.resource_list', static::getEntryPointRoute($this->jsonApiBasePath));
Chris@5 120
Chris@5 121 // Require the JSON:API media type header on every route, except on file
Chris@5 122 // upload routes, where we require `application/octet-stream`.
Chris@5 123 $routes->addRequirements(['_content_type_format' => 'api_json']);
Chris@5 124 $upload_routes->addRequirements(['_content_type_format' => 'bin']);
Chris@5 125
Chris@5 126 $routes->addCollection($upload_routes);
Chris@5 127
Chris@5 128 // Enable all available authentication providers.
Chris@5 129 $routes->addOptions(['_auth' => $this->providerIds]);
Chris@5 130
Chris@5 131 // Flag every route as belonging to the JSON:API module.
Chris@5 132 $routes->addDefaults([static::JSON_API_ROUTE_FLAG_KEY => TRUE]);
Chris@5 133
Chris@5 134 // All routes serve only the JSON:API media type.
Chris@5 135 $routes->addRequirements(['_format' => 'api_json']);
Chris@5 136
Chris@5 137 return $routes;
Chris@5 138 }
Chris@5 139
Chris@5 140 /**
Chris@5 141 * Gets applicable resource routes for a JSON:API resource type.
Chris@5 142 *
Chris@5 143 * @param \Drupal\jsonapi\ResourceType\ResourceType $resource_type
Chris@5 144 * The JSON:API resource type for which to get the routes.
Chris@5 145 * @param string $path_prefix
Chris@5 146 * The root path prefix.
Chris@5 147 *
Chris@5 148 * @return \Symfony\Component\Routing\RouteCollection
Chris@5 149 * A collection of routes for the given resource type.
Chris@5 150 */
Chris@5 151 protected static function getRoutesForResourceType(ResourceType $resource_type, $path_prefix) {
Chris@5 152 // Internal resources have no routes.
Chris@5 153 if ($resource_type->isInternal()) {
Chris@5 154 return new RouteCollection();
Chris@5 155 }
Chris@5 156
Chris@5 157 $routes = new RouteCollection();
Chris@5 158
Chris@5 159 // Collection route like `/jsonapi/node/article`.
Chris@5 160 if ($resource_type->isLocatable()) {
Chris@5 161 $collection_route = new Route("/{$resource_type->getPath()}");
Chris@5 162 $collection_route->addDefaults([RouteObjectInterface::CONTROLLER_NAME => static::CONTROLLER_SERVICE_NAME . ':getCollection']);
Chris@5 163 $collection_route->setMethods(['GET']);
Chris@5 164 // Allow anybody access because "view" and "view label" access are checked
Chris@5 165 // in the controller.
Chris@5 166 $collection_route->setRequirement('_access', 'TRUE');
Chris@5 167 $routes->add(static::getRouteName($resource_type, 'collection'), $collection_route);
Chris@5 168 }
Chris@5 169
Chris@5 170 // Creation route.
Chris@5 171 if ($resource_type->isMutable()) {
Chris@5 172 $collection_create_route = new Route("/{$resource_type->getPath()}");
Chris@5 173 $collection_create_route->addDefaults([RouteObjectInterface::CONTROLLER_NAME => static::CONTROLLER_SERVICE_NAME . ':createIndividual']);
Chris@5 174 $collection_create_route->setMethods(['POST']);
Chris@5 175 $create_requirement = sprintf("%s:%s", $resource_type->getEntityTypeId(), $resource_type->getBundle());
Chris@5 176 $collection_create_route->setRequirement('_entity_create_access', $create_requirement);
Chris@5 177 $collection_create_route->setRequirement('_csrf_request_header_token', 'TRUE');
Chris@5 178 $routes->add(static::getRouteName($resource_type, 'collection.post'), $collection_create_route);
Chris@5 179 }
Chris@5 180
Chris@5 181 // Individual routes like `/jsonapi/node/article/{uuid}` or
Chris@5 182 // `/jsonapi/node/article/{uuid}/relationships/uid`.
Chris@5 183 $routes->addCollection(static::getIndividualRoutesForResourceType($resource_type));
Chris@5 184
Chris@5 185 // Add the resource type as a parameter to every resource route.
Chris@5 186 foreach ($routes as $route) {
Chris@5 187 static::addRouteParameter($route, static::RESOURCE_TYPE_KEY, ['type' => ResourceTypeConverter::PARAM_TYPE_ID]);
Chris@5 188 $route->addDefaults([static::RESOURCE_TYPE_KEY => $resource_type->getTypeName()]);
Chris@5 189 }
Chris@5 190
Chris@5 191 // Resource routes all have the same base path.
Chris@5 192 $routes->addPrefix($path_prefix);
Chris@5 193
Chris@5 194 return $routes;
Chris@5 195 }
Chris@5 196
Chris@5 197 /**
Chris@5 198 * Gets the file upload route collection for the given resource type.
Chris@5 199 *
Chris@5 200 * @param \Drupal\jsonapi\ResourceType\ResourceType $resource_type
Chris@5 201 * The resource type for which the route collection should be created.
Chris@5 202 * @param string $path_prefix
Chris@5 203 * The root path prefix.
Chris@5 204 *
Chris@5 205 * @return \Symfony\Component\Routing\RouteCollection
Chris@5 206 * The route collection.
Chris@5 207 */
Chris@5 208 protected static function getFileUploadRoutesForResourceType(ResourceType $resource_type, $path_prefix) {
Chris@5 209 $routes = new RouteCollection();
Chris@5 210
Chris@5 211 // Internal resources have no routes; individual routes require locations.
Chris@5 212 if ($resource_type->isInternal() || !$resource_type->isLocatable()) {
Chris@5 213 return $routes;
Chris@5 214 }
Chris@5 215
Chris@5 216 // File upload routes are only necessary for resource types that have file
Chris@5 217 // fields.
Chris@5 218 $has_file_field = array_reduce($resource_type->getRelatableResourceTypes(), function ($carry, array $target_resource_types) {
Chris@5 219 return $carry || static::hasNonInternalFileTargetResourceTypes($target_resource_types);
Chris@5 220 }, FALSE);
Chris@5 221 if (!$has_file_field) {
Chris@5 222 return $routes;
Chris@5 223 }
Chris@5 224
Chris@5 225 if ($resource_type->isMutable()) {
Chris@5 226 $path = $resource_type->getPath();
Chris@5 227 $entity_type_id = $resource_type->getEntityTypeId();
Chris@5 228
Chris@5 229 $new_resource_file_upload_route = new Route("/{$path}/{file_field_name}");
Chris@5 230 $new_resource_file_upload_route->addDefaults([RouteObjectInterface::CONTROLLER_NAME => 'jsonapi.file_upload:handleFileUploadForNewResource']);
Chris@5 231 $new_resource_file_upload_route->setMethods(['POST']);
Chris@5 232 $new_resource_file_upload_route->setRequirement('_csrf_request_header_token', 'TRUE');
Chris@5 233 $routes->add(static::getFileUploadRouteName($resource_type, 'new_resource'), $new_resource_file_upload_route);
Chris@5 234
Chris@5 235 $existing_resource_file_upload_route = new Route("/{$path}/{entity}/{file_field_name}");
Chris@5 236 $existing_resource_file_upload_route->addDefaults([RouteObjectInterface::CONTROLLER_NAME => 'jsonapi.file_upload:handleFileUploadForExistingResource']);
Chris@5 237 $existing_resource_file_upload_route->setMethods(['POST']);
Chris@5 238 $existing_resource_file_upload_route->setRequirement('_csrf_request_header_token', 'TRUE');
Chris@5 239 $routes->add(static::getFileUploadRouteName($resource_type, 'existing_resource'), $existing_resource_file_upload_route);
Chris@5 240
Chris@5 241 // Add entity parameter conversion to every route.
Chris@5 242 $routes->addOptions(['parameters' => ['entity' => ['type' => 'entity:' . $entity_type_id]]]);
Chris@5 243
Chris@5 244 // Add the resource type as a parameter to every resource route.
Chris@5 245 foreach ($routes as $route) {
Chris@5 246 static::addRouteParameter($route, static::RESOURCE_TYPE_KEY, ['type' => ResourceTypeConverter::PARAM_TYPE_ID]);
Chris@5 247 $route->addDefaults([static::RESOURCE_TYPE_KEY => $resource_type->getTypeName()]);
Chris@5 248 }
Chris@5 249 }
Chris@5 250
Chris@5 251 // File upload routes all have the same base path.
Chris@5 252 $routes->addPrefix($path_prefix);
Chris@5 253
Chris@5 254 return $routes;
Chris@5 255 }
Chris@5 256
Chris@5 257 /**
Chris@5 258 * Determines if the given request is for a JSON:API generated route.
Chris@5 259 *
Chris@5 260 * @param array $defaults
Chris@5 261 * The request's route defaults.
Chris@5 262 *
Chris@5 263 * @return bool
Chris@5 264 * Whether the request targets a generated route.
Chris@5 265 */
Chris@5 266 public static function isJsonApiRequest(array $defaults) {
Chris@5 267 return isset($defaults[RouteObjectInterface::CONTROLLER_NAME])
Chris@5 268 && strpos($defaults[RouteObjectInterface::CONTROLLER_NAME], static::CONTROLLER_SERVICE_NAME) === 0;
Chris@5 269 }
Chris@5 270
Chris@5 271 /**
Chris@5 272 * Gets a route collection for the given resource type.
Chris@5 273 *
Chris@5 274 * @param \Drupal\jsonapi\ResourceType\ResourceType $resource_type
Chris@5 275 * The resource type for which the route collection should be created.
Chris@5 276 *
Chris@5 277 * @return \Symfony\Component\Routing\RouteCollection
Chris@5 278 * The route collection.
Chris@5 279 */
Chris@5 280 protected static function getIndividualRoutesForResourceType(ResourceType $resource_type) {
Chris@5 281 if (!$resource_type->isLocatable()) {
Chris@5 282 return new RouteCollection();
Chris@5 283 }
Chris@5 284
Chris@5 285 $routes = new RouteCollection();
Chris@5 286
Chris@5 287 $path = $resource_type->getPath();
Chris@5 288 $entity_type_id = $resource_type->getEntityTypeId();
Chris@5 289
Chris@5 290 // Individual read, update and remove.
Chris@5 291 $individual_route = new Route("/{$path}/{entity}");
Chris@5 292 $individual_route->addDefaults([RouteObjectInterface::CONTROLLER_NAME => static::CONTROLLER_SERVICE_NAME . ':getIndividual']);
Chris@5 293 $individual_route->setMethods(['GET']);
Chris@5 294 // No _entity_access requirement because "view" and "view label" access are
Chris@5 295 // checked in the controller. So it's safe to allow anybody access.
Chris@5 296 $individual_route->setRequirement('_access', 'TRUE');
Chris@5 297 $routes->add(static::getRouteName($resource_type, 'individual'), $individual_route);
Chris@5 298 if ($resource_type->isMutable()) {
Chris@5 299 $individual_update_route = new Route($individual_route->getPath());
Chris@5 300 $individual_update_route->addDefaults([RouteObjectInterface::CONTROLLER_NAME => static::CONTROLLER_SERVICE_NAME . ':patchIndividual']);
Chris@5 301 $individual_update_route->setMethods(['PATCH']);
Chris@5 302 $individual_update_route->setRequirement('_entity_access', "entity.update");
Chris@5 303 $individual_update_route->setRequirement('_csrf_request_header_token', 'TRUE');
Chris@5 304 $routes->add(static::getRouteName($resource_type, 'individual.patch'), $individual_update_route);
Chris@5 305 $individual_remove_route = new Route($individual_route->getPath());
Chris@5 306 $individual_remove_route->addDefaults([RouteObjectInterface::CONTROLLER_NAME => static::CONTROLLER_SERVICE_NAME . ':deleteIndividual']);
Chris@5 307 $individual_remove_route->setMethods(['DELETE']);
Chris@5 308 $individual_remove_route->setRequirement('_entity_access', "entity.delete");
Chris@5 309 $individual_remove_route->setRequirement('_csrf_request_header_token', 'TRUE');
Chris@5 310 $routes->add(static::getRouteName($resource_type, 'individual.delete'), $individual_remove_route);
Chris@5 311 }
Chris@5 312
Chris@5 313 foreach ($resource_type->getRelatableResourceTypes() as $relationship_field_name => $target_resource_types) {
Chris@5 314 // Read, update, add, or remove an individual resources relationships to
Chris@5 315 // other resources.
Chris@5 316 $relationship_route = new Route("/{$path}/{entity}/relationships/{$relationship_field_name}");
Chris@5 317 $relationship_route->addDefaults(['_on_relationship' => TRUE]);
Chris@5 318 $relationship_route->addDefaults(['related' => $relationship_field_name]);
Chris@5 319 $relationship_route->setRequirement(RelationshipFieldAccess::ROUTE_REQUIREMENT_KEY, $relationship_field_name);
Chris@5 320 $relationship_route->setRequirement('_csrf_request_header_token', 'TRUE');
Chris@5 321 $relationship_route_methods = $resource_type->isMutable()
Chris@5 322 ? ['GET', 'POST', 'PATCH', 'DELETE']
Chris@5 323 : ['GET'];
Chris@5 324 $relationship_controller_methods = [
Chris@5 325 'GET' => 'getRelationship',
Chris@5 326 'POST' => 'addToRelationshipData',
Chris@5 327 'PATCH' => 'replaceRelationshipData',
Chris@5 328 'DELETE' => 'removeFromRelationshipData',
Chris@5 329 ];
Chris@5 330 foreach ($relationship_route_methods as $method) {
Chris@5 331 $method_specific_relationship_route = clone $relationship_route;
Chris@5 332 $method_specific_relationship_route->addDefaults([RouteObjectInterface::CONTROLLER_NAME => static::CONTROLLER_SERVICE_NAME . ":{$relationship_controller_methods[$method]}"]);
Chris@5 333 $method_specific_relationship_route->setMethods($method);
Chris@5 334 $routes->add(static::getRouteName($resource_type, sprintf("%s.relationship.%s", $relationship_field_name, strtolower($method))), $method_specific_relationship_route);
Chris@5 335 }
Chris@5 336
Chris@5 337 // Only create routes for related routes that target at least one
Chris@5 338 // non-internal resource type.
Chris@5 339 if (static::hasNonInternalTargetResourceTypes($target_resource_types)) {
Chris@5 340 // Get an individual resource's related resources.
Chris@5 341 $related_route = new Route("/{$path}/{entity}/{$relationship_field_name}");
Chris@5 342 $related_route->setMethods(['GET']);
Chris@5 343 $related_route->addDefaults([RouteObjectInterface::CONTROLLER_NAME => static::CONTROLLER_SERVICE_NAME . ':getRelated']);
Chris@5 344 $related_route->addDefaults(['related' => $relationship_field_name]);
Chris@5 345 $related_route->setRequirement(RelationshipFieldAccess::ROUTE_REQUIREMENT_KEY, $relationship_field_name);
Chris@5 346 $routes->add(static::getRouteName($resource_type, "$relationship_field_name.related"), $related_route);
Chris@5 347 }
Chris@5 348 }
Chris@5 349
Chris@5 350 // Add entity parameter conversion to every route.
Chris@5 351 $routes->addOptions(['parameters' => ['entity' => ['type' => 'entity:' . $entity_type_id]]]);
Chris@5 352
Chris@5 353 return $routes;
Chris@5 354 }
Chris@5 355
Chris@5 356 /**
Chris@5 357 * Provides the entry point route.
Chris@5 358 *
Chris@5 359 * @param string $path_prefix
Chris@5 360 * The root path prefix.
Chris@5 361 *
Chris@5 362 * @return \Symfony\Component\Routing\Route
Chris@5 363 * The entry point route.
Chris@5 364 */
Chris@5 365 protected function getEntryPointRoute($path_prefix) {
Chris@5 366 $entry_point = new Route("/{$path_prefix}");
Chris@5 367 $entry_point->addDefaults([RouteObjectInterface::CONTROLLER_NAME => EntryPoint::class . '::index']);
Chris@5 368 $entry_point->setRequirement('_access', 'TRUE');
Chris@5 369 $entry_point->setMethods(['GET']);
Chris@5 370 return $entry_point;
Chris@5 371 }
Chris@5 372
Chris@5 373 /**
Chris@5 374 * Adds a parameter option to a route, overrides options of the same name.
Chris@5 375 *
Chris@5 376 * The Symfony Route class only has a method for adding options which
Chris@5 377 * overrides any previous values. Therefore, it is tedious to add a single
Chris@5 378 * parameter while keeping those that are already set.
Chris@5 379 *
Chris@5 380 * @param \Symfony\Component\Routing\Route $route
Chris@5 381 * The route to which the parameter is to be added.
Chris@5 382 * @param string $name
Chris@5 383 * The name of the parameter.
Chris@5 384 * @param mixed $parameter
Chris@5 385 * The parameter's options.
Chris@5 386 */
Chris@5 387 protected static function addRouteParameter(Route $route, $name, $parameter) {
Chris@5 388 $parameters = $route->getOption('parameters') ?: [];
Chris@5 389 $parameters[$name] = $parameter;
Chris@5 390 $route->setOption('parameters', $parameters);
Chris@5 391 }
Chris@5 392
Chris@5 393 /**
Chris@5 394 * Get a unique route name for the JSON:API resource type and route type.
Chris@5 395 *
Chris@5 396 * @param \Drupal\jsonapi\ResourceType\ResourceType $resource_type
Chris@5 397 * The resource type for which the route collection should be created.
Chris@5 398 * @param string $route_type
Chris@5 399 * The route type. E.g. 'individual' or 'collection'.
Chris@5 400 *
Chris@5 401 * @return string
Chris@5 402 * The generated route name.
Chris@5 403 */
Chris@5 404 public static function getRouteName(ResourceType $resource_type, $route_type) {
Chris@5 405 return sprintf('jsonapi.%s.%s', $resource_type->getTypeName(), $route_type);
Chris@5 406 }
Chris@5 407
Chris@5 408 /**
Chris@5 409 * Get a unique route name for the file upload resource type and route type.
Chris@5 410 *
Chris@5 411 * @param \Drupal\jsonapi\ResourceType\ResourceType $resource_type
Chris@5 412 * The resource type for which the route collection should be created.
Chris@5 413 * @param string $route_type
Chris@5 414 * The route type. E.g. 'individual' or 'collection'.
Chris@5 415 *
Chris@5 416 * @return string
Chris@5 417 * The generated route name.
Chris@5 418 */
Chris@5 419 protected static function getFileUploadRouteName(ResourceType $resource_type, $route_type) {
Chris@5 420 return sprintf('jsonapi.%s.%s.%s', $resource_type->getTypeName(), 'file_upload', $route_type);
Chris@5 421 }
Chris@5 422
Chris@5 423 /**
Chris@5 424 * Determines if an array of resource types has any non-internal ones.
Chris@5 425 *
Chris@5 426 * @param \Drupal\jsonapi\ResourceType\ResourceType[] $resource_types
Chris@5 427 * The resource types to check.
Chris@5 428 *
Chris@5 429 * @return bool
Chris@5 430 * TRUE if there is at least one non-internal resource type in the given
Chris@5 431 * array; FALSE otherwise.
Chris@5 432 */
Chris@5 433 protected static function hasNonInternalTargetResourceTypes(array $resource_types) {
Chris@5 434 return array_reduce($resource_types, function ($carry, ResourceType $target) {
Chris@5 435 return $carry || !$target->isInternal();
Chris@5 436 }, FALSE);
Chris@5 437 }
Chris@5 438
Chris@5 439 /**
Chris@5 440 * Determines if an array of resource types lists non-internal "file" ones.
Chris@5 441 *
Chris@5 442 * @param \Drupal\jsonapi\ResourceType\ResourceType[] $resource_types
Chris@5 443 * The resource types to check.
Chris@5 444 *
Chris@5 445 * @return bool
Chris@5 446 * TRUE if there is at least one non-internal "file" resource type in the
Chris@5 447 * given array; FALSE otherwise.
Chris@5 448 */
Chris@5 449 protected static function hasNonInternalFileTargetResourceTypes(array $resource_types) {
Chris@5 450 return array_reduce($resource_types, function ($carry, ResourceType $target) {
Chris@5 451 return $carry || (!$target->isInternal() && $target->getEntityTypeId() === 'file');
Chris@5 452 }, FALSE);
Chris@5 453 }
Chris@5 454
Chris@5 455 /**
Chris@5 456 * Gets the resource type from a route or request's parameters.
Chris@5 457 *
Chris@5 458 * @param array $parameters
Chris@5 459 * An array of parameters. These may be obtained from a route's
Chris@5 460 * parameter defaults or from a request object.
Chris@5 461 *
Chris@5 462 * @return \Drupal\jsonapi\ResourceType\ResourceType|null
Chris@5 463 * The resource type, NULL if one cannot be found from the given parameters.
Chris@5 464 */
Chris@5 465 public static function getResourceTypeNameFromParameters(array $parameters) {
Chris@5 466 if (isset($parameters[static::JSON_API_ROUTE_FLAG_KEY]) && $parameters[static::JSON_API_ROUTE_FLAG_KEY]) {
Chris@5 467 return isset($parameters[static::RESOURCE_TYPE_KEY]) ? $parameters[static::RESOURCE_TYPE_KEY] : NULL;
Chris@5 468 }
Chris@5 469 return NULL;
Chris@5 470 }
Chris@5 471
Chris@5 472 /**
Chris@5 473 * Invalidates any JSON:API resource type dependent responses and routes.
Chris@5 474 */
Chris@5 475 public static function rebuild() {
Chris@5 476 \Drupal::service('cache_tags.invalidator')->invalidateTags(['jsonapi_resource_types']);
Chris@5 477 \Drupal::service('router.builder')->setRebuildNeeded();
Chris@5 478 }
Chris@5 479
Chris@5 480 }