Skip to content

Commit

Permalink
fix: identifier value ignore to property field (#5352)
Browse files Browse the repository at this point in the history
  • Loading branch information
Byidi authored Jan 23, 2023
1 parent ab1f015 commit 937786e
Show file tree
Hide file tree
Showing 7 changed files with 311 additions and 2 deletions.
61 changes: 61 additions & 0 deletions features/sub_resources/multiple_relation.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
Feature: JSON-LD multi relation
In order to use non-resource types
As a developer
I should be able to serialize types not mapped to an API resource.

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

@createSchema
@!mongodb
Scenario: Get a multiple relation between to object
Given there is a relationMultiple object
When I send a "GET" request to "/dummy/1/relations/2"
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON should be equal to:
"""
{
"@context": "/contexts/RelationMultiple",
"@id": "/dummy/1/relations/2",
"@type": "RelationMultiple",
"id": 1,
"first": "/dummies/1",
"second": "/dummies/2"
}
"""

@!mongodb
Scenario: Get all multiple relation of an object
Given there is a dummy object with many multiple relation
When I send a "GET" request to "/dummy/1/relations"
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON should be equal to:
"""
{
"@context": "/contexts/RelationMultiple",
"@id": "/dummy/1/relations",
"@type": "hydra:Collection",
"hydra:member": [
{
"@id": "/dummy/1/relations/2",
"@type": "RelationMultiple",
"id": 1,
"first": "/dummies/1",
"second": "/dummies/2"
},
{
"@id": "/dummy/1/relations/3",
"@type": "RelationMultiple",
"id": 2,
"first": "/dummies/1",
"second": "/dummies/3"
}
],
"hydra:totalItems": 2
}
"""
8 changes: 6 additions & 2 deletions src/Api/IdentifiersExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ private function getIdentifiersFromOperation(object $item, Operation $operation,
}

$parameterName = $link->getParameterName();
$identifiers[$parameterName] = $this->getIdentifierValue($item, $link->getFromClass(), $link->getIdentifiers()[0], $parameterName);
$identifiers[$parameterName] = $this->getIdentifierValue($item, $link->getFromClass(), $link->getIdentifiers()[0], $parameterName, $link->getToProperty());
}

return $identifiers;
Expand All @@ -93,7 +93,7 @@ private function getIdentifiersFromOperation(object $item, Operation $operation,
/**
* Gets the value of the given class property.
*/
private function getIdentifierValue(object $item, string $class, string $property, string $parameterName): float|bool|int|string
private function getIdentifierValue(object $item, string $class, string $property, string $parameterName, ?string $toProperty = null): float|bool|int|string
{
if ($item instanceof $class) {
try {
Expand All @@ -103,6 +103,10 @@ private function getIdentifierValue(object $item, string $class, string $propert
}
}

if ($toProperty) {
return $this->resolveIdentifierValue($this->propertyAccessor->getValue($item, "$toProperty.$property"), $parameterName);
}

$resourceClass = $this->getResourceClass($item, true);
foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $propertyName) {
$propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName);
Expand Down
59 changes: 59 additions & 0 deletions tests/Api/IdentifiersExtractorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Dummy;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\RelationMultiple;
use ApiPlatform\Tests\Fixtures\TestBundle\State\RelationMultipleProvider;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
Expand Down Expand Up @@ -115,4 +117,61 @@ public function testGetIdentifiersFromItemWithId(): void

$this->assertEquals(['id' => 1], $identifiersExtractor->getIdentifiersFromItem($item, $operation));
}

public function testGetIdentifiersFromItemWithToProperty(): void
{
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);

$resourceClassResolverProphecy->isResourceClass(Argument::any())->willReturn(true);
$resourceClassResolver = $resourceClassResolverProphecy->reveal();

$identifiersExtractor = new IdentifiersExtractor(
$resourceMetadataFactoryProphecy->reveal(),
$resourceClassResolver,
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal()
);

$operation = (new Get())
->withUriTemplate('/resources/{firstId}/relations/{secondId}')
->withUriVariables([
'firstId' => (new Link())
->withFromClass(Dummy::class)
->withToProperty('first')
->withIdentifiers(['id'])
->withParameterName('firstId'),
'secondId' => (new Link())
->withFromClass(Dummy::class)
->withToProperty('second')
->withIdentifiers(['id'])
->withParameterName('secondId'),
])
->withProvider(RelationMultipleProvider::class)
->withClass(RelationMultiple::class);

$first = new Dummy();
$first->setId(1);
$second = new Dummy();
$second->setId(2);

$item = new RelationMultiple();
$item->id = 1;
$item->first = $first;
$item->second = $second;

$resourceClass = RelationMultiple::class;

$resourceClassResolverProphecy->getResourceClass($item)->willReturn($resourceClass);

$this->assertEquals(
[
'firstId' => 1,
'secondId' => 2,
],
$identifiersExtractor->getIdentifiersFromItem($item, $operation)
);
}
}
54 changes: 54 additions & 0 deletions tests/Behat/DoctrineContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\RelatedSecuredDummy;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\RelatedToDummyFriend;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\RelationEmbedder;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\RelationMultiple;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\SecuredDummy;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Site;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\SoMany;
Expand Down Expand Up @@ -2033,6 +2034,59 @@ public function thereIsAPayment(): void
$this->manager->flush();
}

/**
* @Given there is a relationMultiple object
*/
public function thereIsARelationMultipleObject(): void
{
$first = $this->buildDummy();
$first->setId(1);
$first->setName('foo');
$second = $this->buildDummy();
$second->setId(2);
$second->setName('bar');

$relationMultiple = (new RelationMultiple());
$relationMultiple->first = $first;
$relationMultiple->second = $second;

$this->manager->persist($first);
$this->manager->persist($second);
$this->manager->persist($relationMultiple);
$this->manager->flush();
}

/**
* @Given there is a dummy object with many multiple relation
*/
public function thereIsADummyObjectWithManyMultipleRelation(): void
{
$first = $this->buildDummy();
$first->setId(1);
$first->setName('foo');
$second = $this->buildDummy();
$second->setId(2);
$second->setName('bar');
$third = $this->buildDummy();
$third->setId(3);
$third->setName('foobar');

$relationMultiple1 = (new RelationMultiple());
$relationMultiple1->first = $first;
$relationMultiple1->second = $second;

$relationMultiple2 = (new RelationMultiple());
$relationMultiple2->first = $first;
$relationMultiple2->second = $third;

$this->manager->persist($first);
$this->manager->persist($second);
$this->manager->persist($third);
$this->manager->persist($relationMultiple1);
$this->manager->persist($relationMultiple2);
$this->manager->flush();
}

private function isOrm(): bool
{
return null !== $this->schemaTool;
Expand Down
73 changes: 73 additions & 0 deletions tests/Fixtures/TestBundle/Entity/RelationMultiple.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?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\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Tests\Fixtures\TestBundle\State\RelationMultipleProvider;
use Doctrine\ORM\Mapping as ORM;

#[ApiResource(
mercure: true,
operations: [
new Post(),
new Get(
uriTemplate: '/dummy/{firstId}/relations/{secondId}',
uriVariables: [
'firstId' => new Link(
fromClass: Dummy::class,
toProperty: 'first',
identifiers: ['id'],
),
'secondId' => new Link(
fromClass: Dummy::class,
toProperty: 'second',
identifiers: ['id'],
),
],
provider: RelationMultipleProvider::class,
),
new GetCollection(
uriTemplate : '/dummy/{firstId}/relations',
uriVariables: [
'firstId' => new Link(
fromClass: Dummy::class,
toProperty: 'first',
identifiers: ['id'],
),
],
provider: RelationMultipleProvider::class,
),
]
)]

#[ORM\Entity]
class RelationMultiple
{
#[ORM\Id]
#[ORM\Column(type: 'integer')]
#[ORM\GeneratedValue]
#[ApiProperty(identifier: true)]
public ?int $id = null;

#[ORM\ManyToOne(targetEntity: Dummy::class)]
public ?Dummy $first = null;

#[ORM\ManyToOne(targetEntity: Dummy::class)]
public ?Dummy $second = null;
}
53 changes: 53 additions & 0 deletions tests/Fixtures/TestBundle/State/RelationMultipleProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?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\State;

use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Dummy;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\RelationMultiple;

class RelationMultipleProvider implements ProviderInterface
{
/**
* {@inheritDoc}
*/
public function provide(Operation $operation, array $uriVariables = [], array $context = []): array|object|null
{
$firstDummy = new Dummy();
$firstDummy->setId($uriVariables['firstId']);
$secondDummy = new Dummy();
$relationMultiple = new RelationMultiple();
$relationMultiple->id = 1;
$relationMultiple->first = $firstDummy;
$relationMultiple->second = $secondDummy;

if ($operation instanceof GetCollection) {
$secondDummy->setId(2);
$thirdDummy = new Dummy();
$thirdDummy->setId(3);
$relationMultiple2 = new RelationMultiple();
$relationMultiple2->id = 2;
$relationMultiple2->first = $firstDummy;
$relationMultiple2->second = $thirdDummy;

return [$relationMultiple, $relationMultiple2];
}

$relationMultiple->second->setId($uriVariables['secondId']);

return $relationMultiple;
}
}
5 changes: 5 additions & 0 deletions tests/Fixtures/app/config/config_common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ services:
tags:
- { name: 'api_platform.state_provider' }

ApiPlatform\Tests\Fixtures\TestBundle\State\RelationMultipleProvider:
class: 'ApiPlatform\Tests\Fixtures\TestBundle\State\RelationMultipleProvider'
tags:
- { name: 'api_platform.state_provider' }

ApiPlatform\Tests\Fixtures\TestBundle\State\CarProcessor:
class: 'ApiPlatform\Tests\Fixtures\TestBundle\State\CarProcessor'
tags:
Expand Down

0 comments on commit 937786e

Please sign in to comment.