Skip to content

Commit 8d04dcf

Browse files
fix(metadata): fix POST on subresource (#5734)
1 parent e3e6a0d commit 8d04dcf

File tree

4 files changed

+167
-6
lines changed

4 files changed

+167
-6
lines changed

features/main/sub_resource.feature

+19-5
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ Feature: Sub-resource support
564564

565565
@!mongodb
566566
@createSchema
567-
Scenario: The generated crud should allow us to interact with the SubresourceEmployee
567+
Scenario Outline: The generated crud should allow us to interact with the subresources
568568
Given I add "Content-Type" header equal to "application/ld+json"
569569
And I send a "POST" request to "/subresource_organizations" with body:
570570
"""
@@ -574,22 +574,36 @@ Feature: Sub-resource support
574574
"""
575575
Then the response status code should be 201
576576
Given I add "Content-Type" header equal to "application/ld+json"
577-
And I send a "POST" request to "/subresource_organizations/1/subresource_employees" with body:
577+
And I send a "POST" request to "<invalid_uri>" with body:
578+
"""
579+
{
580+
"name": "soyuka"
581+
}
582+
"""
583+
Then the response status code should be 404
584+
Given I add "Content-Type" header equal to "application/ld+json"
585+
And I send a "POST" request to "<collection_uri>" with body:
578586
"""
579587
{
580588
"name": "soyuka"
581589
}
582590
"""
583591
Then the response status code should be 201
584-
And I send a "GET" request to "/subresource_organizations/1/subresource_employees/1"
592+
And I send a "GET" request to "<item_uri>"
585593
Then the response status code should be 200
586-
And I send a "GET" request to "/subresource_organizations/1/subresource_employees"
594+
And I send a "GET" request to "<collection_uri>"
587595
Then the response status code should be 200
588596
Given I add "Content-Type" header equal to "application/ld+json"
589-
And I send a "PUT" request to "/subresource_organizations/1/subresource_employees/1" with body:
597+
And I send a "PUT" request to "<item_uri>" with body:
590598
"""
591599
{
592600
"name": "ok"
593601
}
594602
"""
595603
Then the response status code should be 200
604+
Given I send a "DELETE" request to "<item_uri>"
605+
Then the response status code should be 204
606+
Examples:
607+
| invalid_uri | collection_uri | item_uri |
608+
| /subresource_organizations/invalid/subresource_employees | /subresource_organizations/1/subresource_employees | /subresource_organizations/1/subresource_employees/1 |
609+
| /subresource_organizations/invalid/subresource_factories | /subresource_organizations/1/subresource_factories | /subresource_organizations/1/subresource_factories/1 |

src/State/CreateProvider.php

+7-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use ApiPlatform\Metadata\Link;
2020
use ApiPlatform\Metadata\Operation;
2121
use ApiPlatform\Metadata\Post;
22+
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
2223
use Symfony\Component\PropertyAccess\PropertyAccess;
2324
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
2425

@@ -59,12 +60,17 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
5960
}
6061

6162
$relation = $this->decorated->provide(new Get(uriVariables: $relationUriVariables, class: $relationClass), $uriVariables);
63+
if (!$relation) {
64+
throw new NotFoundHttpException('Not Found');
65+
}
66+
6267
try {
6368
$resource = new ($operation->getClass());
6469
} catch (\Throwable $e) {
6570
throw new RuntimeException(sprintf('An error occurred while trying to create an instance of the "%s" resource. Consider writing your own "%s" implementation and setting it as `provider` on your operation instead.', $operation->getClass(), ProviderInterface::class), 0, $e);
6671
}
67-
$this->propertyAccessor->setValue($resource, $key, $relation);
72+
$property = $operationUriVariables[$key]->getToProperty() ?? $key;
73+
$this->propertyAccessor->setValue($resource, $property, $relation);
6874

6975
return $resource;
7076
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\Entity;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
use ApiPlatform\Metadata\Delete;
18+
use ApiPlatform\Metadata\Get;
19+
use ApiPlatform\Metadata\GetCollection;
20+
use ApiPlatform\Metadata\Link;
21+
use ApiPlatform\Metadata\Post;
22+
use ApiPlatform\Metadata\Put;
23+
use ApiPlatform\State\CreateProvider;
24+
use Doctrine\ORM\Mapping as ORM;
25+
26+
#[ApiResource(
27+
types: ['https://schema.org/LocalBusiness'],
28+
operations: [
29+
new GetCollection(
30+
uriTemplate: '/subresource_organizations/{subresourceOrganizationId}/subresource_factories',
31+
uriVariables: [
32+
'subresourceOrganizationId' => new Link(toProperty: 'subresourceOrganization', fromClass: SubresourceOrganization::class),
33+
]
34+
),
35+
new Post(
36+
uriTemplate: '/subresource_organizations/{subresourceOrganizationId}/subresource_factories',
37+
uriVariables: [
38+
'subresourceOrganizationId' => new Link(toProperty: 'subresourceOrganization', fromClass: SubresourceOrganization::class),
39+
],
40+
provider: CreateProvider::class
41+
),
42+
new Get(
43+
uriTemplate: '/subresource_organizations/{subresourceOrganizationId}/subresource_factories/{id}',
44+
uriVariables: [
45+
'subresourceOrganizationId' => new Link(toProperty: 'subresourceOrganization', fromClass: SubresourceOrganization::class),
46+
'id' => new Link(fromClass: SubresourceFactory::class),
47+
]
48+
),
49+
new Put(
50+
uriTemplate: '/subresource_organizations/{subresourceOrganizationId}/subresource_factories/{id}',
51+
extraProperties: ['standard_put' => false],
52+
uriVariables: [
53+
'subresourceOrganizationId' => new Link(toProperty: 'subresourceOrganization', fromClass: SubresourceOrganization::class),
54+
'id' => new Link(fromClass: SubresourceFactory::class),
55+
]
56+
),
57+
new Delete(
58+
uriTemplate: '/subresource_organizations/{subresourceOrganizationId}/subresource_factories/{id}',
59+
uriVariables: [
60+
'subresourceOrganizationId' => new Link(toProperty: 'subresourceOrganization', fromClass: SubresourceOrganization::class),
61+
'id' => new Link(fromClass: SubresourceFactory::class),
62+
]
63+
),
64+
]
65+
)]
66+
#[ORM\Entity]
67+
class SubresourceFactory
68+
{
69+
#[ORM\Id]
70+
#[ORM\GeneratedValue]
71+
#[ORM\Column]
72+
private ?int $id = null;
73+
74+
#[ORM\Column(length: 255, nullable: true)]
75+
private ?string $name = null;
76+
77+
#[ORM\ManyToOne(inversedBy: 'factories')]
78+
#[ORM\JoinColumn(nullable: false)]
79+
private ?SubresourceOrganization $subresourceOrganization = null;
80+
81+
public function getId(): ?int
82+
{
83+
return $this->id;
84+
}
85+
86+
public function getName(): ?string
87+
{
88+
return $this->name;
89+
}
90+
91+
public function setName(?string $name): self
92+
{
93+
$this->name = $name;
94+
95+
return $this;
96+
}
97+
98+
public function getSubresourceOrganization(): ?SubresourceOrganization
99+
{
100+
return $this->subresourceOrganization;
101+
}
102+
103+
public function setSubresourceOrganization(?SubresourceOrganization $subresourceOrganization): self
104+
{
105+
$this->subresourceOrganization = $subresourceOrganization;
106+
107+
return $this;
108+
}
109+
}

tests/Fixtures/TestBundle/Entity/SubresourceOrganization.php

+32
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,13 @@ class SubresourceOrganization
3333
#[ORM\OneToMany(mappedBy: 'subresourceOrganization', targetEntity: SubresourceEmployee::class, orphanRemoval: true)]
3434
private Collection $employees;
3535

36+
#[ORM\OneToMany(mappedBy: 'subresourceOrganization', targetEntity: SubresourceFactory::class, orphanRemoval: true)]
37+
private Collection $factories;
38+
3639
public function __construct()
3740
{
3841
$this->employees = new ArrayCollection();
42+
$this->factories = new ArrayCollection();
3943
}
4044

4145
public function getId(): ?int
@@ -82,4 +86,32 @@ public function removeSubresourceEmployee(SubresourceEmployee $employee): self
8286

8387
return $this;
8488
}
89+
90+
/**
91+
* @return Collection<int, SubresourceFactory>
92+
*/
93+
public function getSubresourceFactories(): Collection
94+
{
95+
return $this->factories;
96+
}
97+
98+
public function addSubresourceFactory(SubresourceFactory $factory): self
99+
{
100+
if (!$this->factories->contains($factory)) {
101+
$this->factories->add($factory);
102+
$factory->setSubresourceOrganization($this);
103+
}
104+
105+
return $this;
106+
}
107+
108+
public function removeSubresourceFactory(SubresourceFactory $factory): self
109+
{
110+
// set the owning side to null (unless already changed)
111+
if ($this->factories->removeElement($factory) && $factory->getSubresourceOrganization() === $this) {
112+
$factory->setSubresourceOrganization(null);
113+
}
114+
115+
return $this;
116+
}
85117
}

0 commit comments

Comments
 (0)