diff --git a/src/Controller/EntityResource.php b/src/Controller/EntityResource.php index aed2498..740c64e 100644 --- a/src/Controller/EntityResource.php +++ b/src/Controller/EntityResource.php @@ -38,7 +38,6 @@ use Drupal\jsonapi\ResourceResponse; use Drupal\jsonapi\ResourceType\ResourceType; use Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface; use Drupal\jsonapi\Revisions\ResourceVersionRouteEnhancer; -use Drupal\jsonapi\Routing\Routes; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Drupal\Core\Http\Exception\CacheableBadRequestHttpException; @@ -155,11 +154,11 @@ class EntityResource { * Thrown when access to the entity is not allowed. */ public function getIndividual(EntityInterface $entity, Request $request) { - $entity = $this->entityAccessChecker->getAccessCheckedResourceObject($entity); - if ($entity instanceof EntityAccessDeniedHttpException) { - throw $entity; + $resource_object = $this->entityAccessChecker->getAccessCheckedResourceObject($entity); + if ($resource_object instanceof EntityAccessDeniedHttpException) { + throw $resource_object; } - $response = $this->buildWrappedResponse($entity, $request, $this->getIncludes($request, $entity)); + $response = $this->buildWrappedResponse($resource_object, $request, $this->getIncludes($request, $resource_object)); return $response; } @@ -216,7 +215,8 @@ class EntityResource { $parsed_entity->save(); // Build response object. - $response = $this->buildWrappedResponse(new ResourceObject($resource_type, $parsed_entity), $request, $this->getIncludes($request, $parsed_entity), 201); + $resource_object = new ResourceObject($resource_type, $parsed_entity); + $response = $this->buildWrappedResponse($resource_object, $request, $this->getIncludes($request, $resource_object), 201); // According to JSON:API specification, when a new entity was created // we should send "Location" header to the frontend. @@ -274,7 +274,8 @@ class EntityResource { static::validate($entity, $field_names); $entity->save(); - return $this->buildWrappedResponse(new ResourceObject($resource_type, $entity), $request, $this->getIncludes($request, $entity)); + $resource_object = new ResourceObject($resource_type, $entity); + return $this->buildWrappedResponse($resource_object, $request, $this->getIncludes($request, $resource_object)); } /** @@ -449,7 +450,7 @@ class EntityResource { $collection_data[] = $this->entityAccessChecker->getAccessCheckedResourceObject($referenced_entity); } $entity_collection = new EntityCollection($collection_data, $field_list->getFieldDefinition()->getFieldStorageDefinition()->getCardinality()); - $response = $this->buildWrappedResponse($entity_collection, $request, $this->getIncludes($request, $entity_collection, $related)); + $response = $this->buildWrappedResponse($entity_collection, $request, $this->getIncludes($request, $entity_collection)); // $response does not contain the entity list cache tag. We add the // cacheable metadata for the finite list of entities in the relationship. @@ -937,9 +938,6 @@ class EntityResource { * The request object. * @param \Drupal\Core\Entity\EntityInterface|\Drupal\jsonapi\JsonApiResource\EntityCollection $data * The response data from which to resolve includes. - * @param string $related - * (optional) The relationship field name to be given for getting includes - * on a related route. * * @return \Drupal\jsonapi\JsonApiResource\EntityCollection * An EntityCollection to be included or a NullEntityCollection if the @@ -948,9 +946,9 @@ class EntityResource { * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ - public function getIncludes(Request $request, $data, $related = NULL) { + public function getIncludes(Request $request, $data) { return $request->query->has('include') && ($include_parameter = $request->query->get('include')) && !empty($include_parameter) - ? $this->includeResolver->resolve($request->get(Routes::RESOURCE_TYPE_KEY), $data, $include_parameter, $related) + ? $this->includeResolver->resolve($data, $include_parameter) : new NullEntityCollection(); } diff --git a/src/IncludeResolver.php b/src/IncludeResolver.php index 065295a..3104026 100644 --- a/src/IncludeResolver.php +++ b/src/IncludeResolver.php @@ -47,8 +47,6 @@ class IncludeResolver { /** * Resolves included resources. * - * @param \Drupal\jsonapi\ResourceType\ResourceType $base_resource_type - * The base resource type for which includes are to be resolved. * @param \Drupal\jsonapi\JsonApiResource\ResourceIdentifierInterface|\Drupal\jsonapi\JsonApiResource\EntityCollection $data * The resource(s) for which to resolve includes. * @param string $include_parameter @@ -64,11 +62,11 @@ class IncludeResolver { * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * Thrown if a storage handler couldn't be loaded. */ - public function resolve(ResourceType $base_resource_type, $data, $include_parameter, $related_field = NULL) { + public function resolve($data, $include_parameter, $related_field = NULL) { assert($data instanceof ResourceIdentifierInterface || $data instanceof EntityCollection); // Map a single entity into an EntityCollection. $entity_collection = $data instanceof ResourceIdentifierInterface ? new EntityCollection([$data], 1) : $data; - $include_tree = static::toIncludeTree($base_resource_type, $include_parameter, $related_field); + $include_tree = static::toIncludeTree($entity_collection, $include_parameter); return EntityCollection::deduplicate($this->resolveIncludeTree($include_tree, $entity_collection)); } @@ -161,26 +159,27 @@ class IncludeResolver { /** * Returns a tree of field names to include from an include parameter. * - * @param \Drupal\jsonapi\ResourceType\ResourceType $base_resource_type - * The base resource type from which to resolve an internal include path. + * @param \Drupal\jsonapi\JsonApiResource\EntityCollection $entity_collection + * The base resources for which includes should be resolved. * @param string $include_parameter * The raw include parameter value. - * @param string|null $related_field - * A relationship field name if the includes are being resolved on a - * relationship route. * * @return array * An multi-dimensional array representing a tree of field names to be * included. Array keys are the field names. Leaves are empty arrays. */ - protected static function toIncludeTree(ResourceType $base_resource_type, $include_parameter, $related_field) { + protected static function toIncludeTree(EntityCollection $entity_collection, $include_parameter) { // $include_parameter: 'one.two.three, one.two.four'. $include_paths = array_map('trim', explode(',', $include_parameter)); // $exploded_paths: [['one', 'two', 'three'], ['one', 'two', 'four']]. $exploded_paths = array_map(function ($include_path) { return array_map('trim', explode('.', $include_path)); }, $include_paths); - $resolved_paths = static::resolveInternalIncludePaths($base_resource_type, $exploded_paths, $related_field); + $resolved_paths = []; + /* @var \Drupal\jsonapi\JsonApiResource\ResourceIdentifierInterface $item */ + foreach ($entity_collection as $item) { + $resolved_paths = array_merge($resolved_paths, static::resolveInternalIncludePaths($item->getResourceType(), $exploded_paths)); + } return static::buildTree($resolved_paths); } @@ -191,9 +190,6 @@ class IncludeResolver { * The base resource type from which to resolve an internal include path. * @param array $paths * An array of exploded include paths. - * @param string|null $related_field - * A relationship field name if the includes are being resolved on a - * relationship route. * * @return array * An array of all possible internal include paths derived from the given @@ -201,17 +197,12 @@ class IncludeResolver { * * @see self::buildTree */ - protected static function resolveInternalIncludePaths(ResourceType $base_resource_type, array $paths, $related_field) { - $internal_paths = array_map(function ($exploded_path) use ($base_resource_type, $related_field) { + protected static function resolveInternalIncludePaths(ResourceType $base_resource_type, array $paths) { + $internal_paths = array_map(function ($exploded_path) use ($base_resource_type) { if (empty($exploded_path)) { return []; } - $resolved_paths = FieldResolver::resolveInternalIncludePath($base_resource_type, $related_field ? array_merge([$related_field], $exploded_path) : $exploded_path); - return $related_field - ? array_map(function ($resolved_path) { - return array_slice($resolved_path, 1); - }, $resolved_paths) - : $resolved_paths; + return FieldResolver::resolveInternalIncludePath($base_resource_type, $exploded_path); }, $paths); $flattened_paths = array_reduce($internal_paths, 'array_merge', []); return $flattened_paths; diff --git a/tests/src/Kernel/Normalizer/JsonApiDocumentTopLevelNormalizerTest.php b/tests/src/Kernel/Normalizer/JsonApiDocumentTopLevelNormalizerTest.php index dec157e..ec86b67 100644 --- a/tests/src/Kernel/Normalizer/JsonApiDocumentTopLevelNormalizerTest.php +++ b/tests/src/Kernel/Normalizer/JsonApiDocumentTopLevelNormalizerTest.php @@ -218,7 +218,7 @@ class JsonApiDocumentTopLevelNormalizerTest extends JsonapiKernelTestBase { list($request, $resource_type) = $this->generateProphecies('node', 'article'); $resource_object = new ResourceObject($resource_type, $this->node); - $includes = $this->includeResolver->resolve($resource_type, $resource_object, 'uid,field_tags,field_image'); + $includes = $this->includeResolver->resolve($resource_object, 'uid,field_tags,field_image'); $jsonapi_doc_object = $this ->getNormalizer() @@ -349,7 +349,7 @@ class JsonApiDocumentTopLevelNormalizerTest extends JsonapiKernelTestBase { list($request, $resource_type) = $this->generateProphecies('node', 'article', 'uuid'); $resource_object = new ResourceObject($resource_type, $this->node); $include_param = 'uid,field_tags'; - $includes = $this->includeResolver->resolve($resource_type, $resource_object, $include_param); + $includes = $this->includeResolver->resolve($resource_object, $include_param); $document_wrapper = new JsonApiDocumentTopLevel($resource_object, $includes, new LinkCollection([])); $request->query = new ParameterBag([