Skip to content

Commit

Permalink
feat: handle uriTemplate on property for JSON:API format
Browse files Browse the repository at this point in the history
  • Loading branch information
GregoireHebert committed Aug 21, 2023
1 parent 86ee2d0 commit e3bf432
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 2 deletions.
48 changes: 48 additions & 0 deletions features/jsonapi/collection_uri_template.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
@php8
@v3
Feature: Exposing a property being a collection of resources
can return an IRI instead of an array
when the uriTemplate is set on the ApiProperty attribute

Background:
Given I add "Accept" header equal to "application/vnd.api+json"
And I add "Content-Type" header equal to "application/vnd.api+json"

Scenario: Retrieve Resource with uriTemplate collection Property
Given there are propertyCollectionIriOnly with relations
And I send a "GET" request to "/property_collection_iri_onlies/1"
Then the response status code should be 200
And the response should be in JSON
And the JSON should be valid according to the JSON HAL schema
And the header "Content-Type" should be equal to "application/vnd.api+json; charset=utf-8"
And the JSON should be equal to:
"""
{
"links": {
"propertyCollectionIriOnlyRelation": "/property-collection-relations",
"iterableIri": "/parent/1/another-collection-operations"
},
"data": {
"id": "/property_collection_iri_onlies/1",
"type": "PropertyCollectionIriOnly",
"relationships": {
"propertyCollectionIriOnlyRelation": {
"data": [
{
"type": "PropertyCollectionIriOnlyRelation",
"id": "/property_collection_iri_only_relations/1"
}
]
},
"iterableIri": {
"data": [
{
"type": "PropertyCollectionIriOnlyRelation",
"id": "/property_collection_iri_only_relations/9999"
}
]
}
}
}
}
"""
30 changes: 28 additions & 2 deletions src/JsonApi/Serializer/ItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use ApiPlatform\Api\UrlGeneratorInterface;
use ApiPlatform\Exception\ItemNotFoundException;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
Expand Down Expand Up @@ -104,7 +105,7 @@ public function normalize(mixed $object, string $format = null, array $context =
}

// Get and populate relations
$allRelationshipsData = $this->getComponents($object, $format, $context)['relationships'];
['relationships' => $allRelationshipsData, 'links' => $links] = $this->getComponents($object, $format, $context);
$populatedRelationContext = $context;
$relationshipsData = $this->getPopulatedRelations($object, $format, $populatedRelationContext, $allRelationshipsData);

Expand All @@ -126,7 +127,13 @@ public function normalize(mixed $object, string $format = null, array $context =
$resourceData['relationships'] = $relationshipsData;
}

$document = ['data' => $resourceData];
$document = [];

if ($links) {
$document['links'] = $links;
}

$document['data'] = $resourceData;

if ($includedResourcesData) {
$document['included'] = $includedResourcesData;
Expand Down Expand Up @@ -311,6 +318,25 @@ private function getComponents(object $object, ?string $format, array $context):
'cardinality' => $isOne ? 'one' : 'many',
];

// if we specify the uriTemplate, generates its value for link definition
// @see ApiPlatform\Serializer\AbstractItemNormalizer:getAttributeValue logic for intentional duplicate content
if ($isMany && $itemUriTemplate = $propertyMetadata->getUriTemplate()) {
$attributeValue = $this->propertyAccessor->getValue($object, $attribute);
$resourceClass = $this->resourceClassResolver->getResourceClass($attributeValue, $className);
$childContext = $this->createChildContext($context, $attribute, $format);
unset($childContext['iri'], $childContext['uri_variables'], $childContext['resource_class'], $childContext['operation']);

$operation = $this->resourceMetadataCollectionFactory->create($resourceClass)->getOperation(
operationName: $itemUriTemplate,
forceCollection: true,
httpOperation: true
);

if ($operation instanceof GetCollection) {
$components['links'][$attribute] = $this->iriConverter->getIriFromResource($object, UrlGeneratorInterface::ABS_PATH, $operation, $childContext);
}
}

$components['relationships'][] = $relation;
$isRelationship = true;
}
Expand Down
1 change: 1 addition & 0 deletions src/Serializer/AbstractItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,7 @@ protected function getAttributeValue(object $object, string $attribute, string $
$childContext = $this->createChildContext($this->createOperationContext($context, $resourceClass), $attribute, $format);

// @see ApiPlatform\Hal\Serializer\ItemNormalizer:getComponents logic for intentional duplicate content
// @see ApiPlatform\JsonApi\Serializer\ItemNormalizer:getComponents logic for intentional duplicate content
if ($format === 'jsonld' && $itemUriTemplate = $propertyMetadata->getUriTemplate()) {
$operation = $this->resourceMetadataCollectionFactory->create($resourceClass)->getOperation(
operationName: $itemUriTemplate,
Expand Down

0 comments on commit e3bf432

Please sign in to comment.