Skip to content

Commit

Permalink
feat: handle uriTemplate on property for HAL format
Browse files Browse the repository at this point in the history
  • Loading branch information
GregoireHebert committed Aug 21, 2023
1 parent d1fc54c commit 86ee2d0
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 3 deletions.
52 changes: 52 additions & 0 deletions features/hal/collection_uri_template.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
@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

Scenario: Retrieve Resource with uriTemplate collection Property
Given there are propertyCollectionIriOnly with relations
When I add "Accept" header equal to "application/hal+json"
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/hal+json; charset=utf-8"
And the JSON should be equal to:
"""
{
"_links": {
"self": {
"href": "/property_collection_iri_onlies/1"
},
"propertyCollectionIriOnlyRelation": {
"href": "/property-collection-relations"
},
"iterableIri": {
"href": "/parent/1/another-collection-operations"
}
},
"_embedded": {
"propertyCollectionIriOnlyRelation": [
{
"_links": {
"self": {
"href": "/property_collection_iri_only_relations/1"
}
},
"name": "relation"
}
],
"iterableIri": [
{
"_links": {
"self": {
"href": "/property_collection_iri_only_relations/9999"
}
},
"name": "Michel"
}
]
}
}
"""
30 changes: 29 additions & 1 deletion src/Hal/Serializer/ItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
namespace ApiPlatform\Hal\Serializer;

use ApiPlatform\Api\UrlGeneratorInterface;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Util\ClassInfoTrait;
use ApiPlatform\Serializer\AbstractItemNormalizer;
use ApiPlatform\Serializer\CacheKeyTrait;
Expand Down Expand Up @@ -166,7 +167,28 @@ private function getComponents(object $object, ?string $format, array $context):
continue;
}

$relation = ['name' => $attribute, 'cardinality' => $isOne ? 'one' : 'many'];
$relation = ['name' => $attribute, 'cardinality' => $isOne ? 'one' : 'many', 'uriTemplate' => null];

// 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) {
$relation['uriTemplate'] = $this->iriConverter->getIriFromResource($object, UrlGeneratorInterface::ABS_PATH, $operation, $childContext);
}
}


if ($propertyMetadata->isReadableLink()) {
$components['embedded'][] = $relation;
}
Expand Down Expand Up @@ -215,6 +237,12 @@ private function populateRelation(array $data, object $object, ?string $format,
$relationName = $this->nameConverter->normalize($relationName, $class, $format, $context);
}

// if we specify the uriTemplate, then the link takes the uriTemplate defined.
if ('links' === $type && $itemUriTemplate = $relation['uriTemplate']) {
$data[$key][$relationName]['href'] = $itemUriTemplate;
continue;
}

if ('one' === $relation['cardinality']) {
if ('links' === $type) {
$data[$key][$relationName]['href'] = $this->getRelationIri($attributeValue);
Expand Down
3 changes: 2 additions & 1 deletion src/Serializer/AbstractItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,8 @@ protected function getAttributeValue(object $object, string $attribute, string $
$resourceClass = $this->resourceClassResolver->getResourceClass($attributeValue, $className);
$childContext = $this->createChildContext($this->createOperationContext($context, $resourceClass), $attribute, $format);

if ($itemUriTemplate = $propertyMetadata->getUriTemplate()) {
// @see ApiPlatform\Hal\Serializer\ItemNormalizer:getComponents logic for intentional duplicate content
if ($format === 'jsonld' && $itemUriTemplate = $propertyMetadata->getUriTemplate()) {
$operation = $this->resourceMetadataCollectionFactory->create($resourceClass)->getOperation(
operationName: $itemUriTemplate,
forceCollection: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public function getIterableIri(): array
$propertyCollectionIriOnlyRelation = new PropertyCollectionIriOnlyRelation();
$propertyCollectionIriOnlyRelation->name = 'Michel';

$this->iterableIri[] = $propertyCollectionIriOnlyRelation;
$this->iterableIri = [$propertyCollectionIriOnlyRelation];

return $this->iterableIri;
}
Expand Down

0 comments on commit 86ee2d0

Please sign in to comment.