You are here

protected function DocumentExtractor::extractResourceObjectFromRequest in JSON:API Resources 8

Decodes and builds a resource object from a request body.

Parameters

\Symfony\Component\HttpFoundation\Request $request: The request.

Return value

\Drupal\jsonapi_resources\Unstable\Value\NewResourceObject A new resource object.

1 call to DocumentExtractor::extractResourceObjectFromRequest()
DocumentExtractor::extractDocument in src/Unstable/DocumentExtractor.php
Extracts the JSON:API document from a request.

File

src/Unstable/DocumentExtractor.php, line 114

Class

DocumentExtractor
Document extractor for requests.

Namespace

Drupal\jsonapi_resources\Unstable

Code

protected function extractResourceObjectFromRequest(Request $request) {
  $decoded = $this
    ->decodeRequestPayload($request);
  $primary_data = $decoded['data'] ?? [];
  if (isset($decoded['data'][0])) {

    // The `data` member of a request document can only be an array if it is
    // updating a relationship. If the objects in a data array are resource
    // object, not resource identifiers, then it's because of a developer
    // error.
    if (!empty(array_intersect_key($decoded['data'][0], array_flip([
      'attributes',
      'relationships',
    ])))) {
      throw new UnprocessableEntityHttpException("To add or update a resource object, the request document's primary data must not be an array.");
    }
    else {
      throw new HttpException(501, 'The JSON:API Resources module does not yet support updating relationships.');
    }
  }
  if (!isset($primary_data['type'])) {
    throw new UnprocessableEntityHttpException("The document's primary data must have a `type` member.");
  }

  // Ensure that the client provided ID is a valid UUID.
  if (isset($primary_data['id']) && !Uuid::isValid($primary_data['id'])) {
    throw new UnprocessableEntityHttpException('IDs should be properly generated and formatted UUIDs as described in RFC 4122. See https://jsonapi.org/format/#crud-creating-client-ids.');
  }

  /** @var \Drupal\jsonapi\ResourceType\ResourceType[] $route_resource_types */
  $route_resource_types = $request->attributes
    ->get('resource_types');
  foreach ($route_resource_types as $route_resource_type) {
    if ($primary_data['type'] === $route_resource_type
      ->getTypeName()) {
      $resource_type = $route_resource_type;
    }
  }
  if (!isset($resource_type)) {
    $format = "The document's primary data contains a resource object with a type that cannot be created via this URL. Allowed resource types: `%s`";
    $supported_resource_types = array_map(static function (ResourceType $resource_type) {
      return $resource_type
        ->getTypeName();
    }, $route_resource_types);
    $message = sprintf($format, implode('`, `', $supported_resource_types));
    throw new AccessDeniedHttpException($message);
  }

  // Ensure that no relationship fields are being set via the attributes
  // resource object member.
  if (isset($primary_data['attributes'])) {
    $received_attribute_field_names = array_keys($primary_data['attributes']);
    $relationship_field_names = array_keys($resource_type
      ->getRelatableResourceTypes());
    if ($relationship_fields_sent_as_attributes = array_intersect($received_attribute_field_names, $relationship_field_names)) {
      throw new UnprocessableEntityHttpException(sprintf('The following relationship fields were provided as attributes: [ %s ]', implode(', ', $relationship_fields_sent_as_attributes)));
    }
  }
  if (isset($decoded['data']['relationships'])) {
    $primary_data['relationships'] = $this
      ->handleRelationships($decoded['data']);
  }
  return NewResourceObject::createFromPrimaryData($resource_type, $primary_data);
}