Skip to content

Commit

Permalink
fix(serializer): read groups off the root operation (#5196)
Browse files Browse the repository at this point in the history
* fix(serializer): read groups off the root operation
  • Loading branch information
soyuka committed Nov 18, 2022
1 parent abed742 commit ebaad51
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 1 deletion.
18 changes: 18 additions & 0 deletions features/serializer/groups_related.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@php8
@!mongodb
Feature: Groups to embed relations
In order to show embed relations on a Resource
As a client software developer
I need to set up groups on the Resource embed properties

Scenario: Get a single resource
When I send a "GET" request to "/relation_group_impact_on_collections/1"
Then the response status code should be 200
And the response should be in JSON
And the JSON node "related.title" should be equal to "foo"

Scenario: Get a collection resource not impacted by groups
When I send a "GET" request to "/relation_group_impact_on_collections"
Then the response status code should be 200
And the response should be in JSON
And the JSON node "hydra:member[0].related" should be equal to "/relation_group_impact_on_collection_relations/1"
10 changes: 10 additions & 0 deletions src/Hydra/Serializer/CollectionNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,21 @@ public function normalize($object, $format = null, array $context = []): array
$data['hydra:member'] = [];
$iriOnly = $context[self::IRI_ONLY] ?? $this->defaultContext[self::IRI_ONLY];

// We need to keep this operation for serialization groups for later
if (isset($context['operation'])) {
$context['root_operation'] = $context['operation'];
}

if (isset($context['operation_name'])) {
$context['root_operation_name'] = $context['operation_name'];
}

if ($this->resourceMetadataCollectionFactory && ($operation = $context['operation'] ?? null) instanceof CollectionOperationInterface && ($itemUriTemplate = $operation->getItemUriTemplate())) {
$context['operation'] = $this->resourceMetadataCollectionFactory->create($resourceClass)->getOperation($operation->getItemUriTemplate());
} else {
unset($context['operation']);
}

unset($context['operation_name'], $context['uri_variables']);

foreach ($object as $obj) {
Expand Down
10 changes: 10 additions & 0 deletions src/Serializer/AbstractCollectionNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,23 @@ public function normalize($object, $format = null, array $context = [])
$data = [];
$paginationData = $this->getPaginationData($object, $context);

// We need to keep this operation for serialization groups for later
if (isset($context['operation'])) {
$context['root_operation'] = $context['operation'];
}

if (isset($context['operation_name'])) {
$context['root_operation_name'] = $context['operation_name'];
}

/** @var ResourceMetadata|ResourceMetadataCollection */
$metadata = $this->resourceMetadataFactory->create($context['resource_class'] ?? '');
if ($metadata instanceof ResourceMetadataCollection && ($operation = $context['operation'] ?? null) instanceof CollectionOperationInterface && ($itemUriTemplate = $operation->getItemUriTemplate())) {
$context['operation'] = $metadata->getOperation($itemUriTemplate);
} else {
unset($context['operation']);
}

unset($context['operation_type'], $context['operation_name']);
$itemsData = $this->getItemsData($object, $format, $context);

Expand Down
2 changes: 1 addition & 1 deletion src/Serializer/AbstractItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ protected function getFactoryOptions(array $context): array
if (isset($context['resource_class']) && $this->resourceClassResolver->isResourceClass($context['resource_class']) && $this->resourceMetadataFactory instanceof ResourceMetadataCollectionFactoryInterface) {
$resourceClass = $this->resourceClassResolver->getResourceClass(null, $context['resource_class']); // fix for abstract classes and interfaces
// This is a hot spot, we should avoid calling this here but in many cases we can't
$operation = $context['operation'] ?? $this->resourceMetadataFactory->create($resourceClass)->getOperation($context['operation_name'] ?? null);
$operation = $context['root_operation'] ?? $context['operation'] ?? $this->resourceMetadataFactory->create($resourceClass)->getOperation($context['root_operation_name'] ?? $context['operation_name'] ?? null);
$options['normalization_groups'] = $operation->getNormalizationContext()['groups'] ?? null;
$options['denormalization_groups'] = $operation->getDenormalizationContext()['groups'] ?? null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Tests\Fixtures\TestBundle\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\CollectionOperationInterface;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Operation;
use Symfony\Component\Serializer\Annotation\Groups;

#[ApiResource(
provider: [RelationGroupImpactOnCollection::class, 'getData']
)]
#[GetCollection]
#[Get(normalizationContext: ['groups' => 'related'])]
class RelationGroupImpactOnCollection
{
public ?int $id;
#[Groups('related')]
public ?RelationGroupImpactOnCollectionRelation $related;

public function __construct($id = null, $related = null)
{
$this->id = $id;
$this->related = $related;
}

public static function getData(Operation $operation, array $uriVariables = [], array $context = []): self|array
{
$item = new self($uriVariables['id'] ?? 1, new RelationGroupImpactOnCollectionRelation(id: $uriVariables['id'] ?? 1, title: 'foo'));
if ($operation instanceof CollectionOperationInterface) {
return [$item];
}

return $item;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Tests\Fixtures\TestBundle\Entity;

use ApiPlatform\Metadata\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;

#[ApiResource]
class RelationGroupImpactOnCollectionRelation
{
public ?int $id;

#[Groups('related')]
public ?string $title;

public function __construct($id, $title)
{
$this->id = $id;
$this->title = $title;
}
}

0 comments on commit ebaad51

Please sign in to comment.