Skip to content

Commit 07c9989

Browse files
fix(metadata): notexposed no urivariables inheritance (#5765)
* fix(metadata): notexposed no urivariables inheritance * use named arguments Co-authored-by: Vincent <[email protected]> * fix tests --------- Co-authored-by: Vincent <[email protected]>
1 parent e2e59b0 commit 07c9989

File tree

7 files changed

+158
-5
lines changed

7 files changed

+158
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Feature: Get a subresource from inverse side that has no item operation
2+
3+
@!mongodb
4+
@createSchema
5+
Scenario: Get a subresource from inverse side that has no item operation
6+
Given there are logs on an event
7+
When I send a "GET" request to "/events/03af3507-271e-4cca-8eee-6244fb06e95b/logs"
8+
Then the response status code should be 200

src/Api/IdentifiersExtractor.php

+5-4
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,11 @@ private function getIdentifiersFromOperation(object $item, Operation $operation,
7272
}
7373

7474
$identifiers = [];
75-
foreach ($links ?? [] as $link) {
76-
if (1 < (is_countable($link->getIdentifiers()) ? \count($link->getIdentifiers()) : 0)) {
75+
foreach ($links ?? [] as $k => $link) {
76+
$linkIdentifiers = $link->getIdentifiers() ?? [$k];
77+
if (1 < \count($linkIdentifiers)) {
7778
$compositeIdentifiers = [];
78-
foreach ($link->getIdentifiers() as $identifier) {
79+
foreach ($linkIdentifiers as $identifier) {
7980
$compositeIdentifiers[$identifier] = $this->getIdentifierValue($item, $link->getFromClass() ?? $operation->getClass(), $identifier, $link->getParameterName());
8081
}
8182

@@ -84,7 +85,7 @@ private function getIdentifiersFromOperation(object $item, Operation $operation,
8485
}
8586

8687
$parameterName = $link->getParameterName();
87-
$identifiers[$parameterName] = $this->getIdentifierValue($item, $link->getFromClass() ?? $operation->getClass(), $link->getIdentifiers()[0], $parameterName, $link->getToProperty());
88+
$identifiers[$parameterName] = $this->getIdentifierValue($item, $link->getFromClass() ?? $operation->getClass(), $linkIdentifiers[0], $parameterName, $link->getToProperty());
8889
}
8990

9091
return $identifiers;

src/Metadata/Resource/Factory/NotExposedOperationResourceMetadataCollectionFactory.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public function create(string $resourceClass): ResourceMetadataCollection
7171
// No item operation has been found on all resources for resource class: generate one on the last resource
7272
// Helpful to generate an IRI for a resource without declaring the Get operation
7373
/** @var HttpOperation $operation */
74-
[$key, $operation] = $this->getOperationWithDefaults($resource, new NotExposed(), true, ['uriTemplate']); // @phpstan-ignore-line $resource is defined if count > 0
74+
[$key, $operation] = $this->getOperationWithDefaults(resource: $resource, operation: new NotExposed(), generated: true, ignoredOptions: ['uriTemplate', 'uriVariables']); // @phpstan-ignore-line $resource is defined if count > 0
7575

7676
if (!$this->linkFactory->createLinksFromIdentifiers($operation)) {
7777
$operation = $operation->withUriTemplate(self::$skolemUriTemplate);

src/Metadata/Tests/Resource/Factory/NotExposedOperationResourceMetadataCollectionFactoryTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ class: AttributeResource::class
208208
shortName: 'AttributeResource',
209209
types: ['https://schema.org/Book'],
210210
uriTemplate: '/custom_api_resources', // uriTemplate should not be inherited on NotExposed operation
211+
uriVariables: ['slug'], // same as it is used to generate the uriTemplate of our NotExposed operation
211212
operations: [
212213
'_api_AttributeResource_get_collection' => new GetCollection(controller: 'api_platform.action.placeholder', shortName: 'AttributeResource', class: AttributeResource::class),
213214
],
@@ -227,6 +228,7 @@ class: AttributeResource::class
227228
new ApiResource(
228229
shortName: 'AttributeResource',
229230
uriTemplate: '/custom_api_resources',
231+
uriVariables: ['slug'],
230232
types: ['https://schema.org/Book'],
231233
operations: [
232234
'_api_AttributeResource_get_collection' => new GetCollection(controller: 'api_platform.action.placeholder', shortName: 'AttributeResource', class: AttributeResource::class),

tests/Behat/DoctrineContext.php

+19
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@
142142
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\InitializeInput;
143143
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\InternalUser;
144144
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\IriOnlyDummy;
145+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue5722\Event;
146+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue5722\ItemLog;
145147
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\MaxDepthDummy;
146148
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\MultiRelationsDummy;
147149
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\MultiRelationsRelatedDummy;
@@ -2147,6 +2149,23 @@ public function thereIsAResourceUsingEntityClassAndDateTime(): void
21472149
$this->manager->flush();
21482150
}
21492151

2152+
/**
2153+
* @Given there are logs on an event
2154+
*/
2155+
public function thereAreLogsOnAnEvent(): void
2156+
{
2157+
$entity = new Event();
2158+
$entity->logs = new ArrayCollection([new ItemLog(), new ItemLog()]);
2159+
$entity->uuid = Uuid::fromString('03af3507-271e-4cca-8eee-6244fb06e95b');
2160+
$this->manager->persist($entity);
2161+
foreach ($entity->logs as $log) {
2162+
$log->item = $entity;
2163+
$this->manager->persist($log);
2164+
}
2165+
2166+
$this->manager->flush();
2167+
}
2168+
21502169
private function isOrm(): bool
21512170
{
21522171
return null !== $this->schemaTool;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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\Issue5722;
15+
16+
use ApiPlatform\Metadata\ApiProperty;
17+
use ApiPlatform\Metadata\ApiResource;
18+
use ApiPlatform\Metadata\Get;
19+
use ApiPlatform\Metadata\GetCollection;
20+
use Doctrine\Common\Collections\ArrayCollection;
21+
use Doctrine\Common\Collections\Collection;
22+
use Doctrine\ORM\Mapping as ORM;
23+
use Ramsey\Uuid\Uuid;
24+
25+
#[ApiResource(
26+
shortName: 'EventIssue5722',
27+
operations: [
28+
new GetCollection(),
29+
new Get(),
30+
],
31+
)]
32+
#[ORM\Entity]
33+
class Event
34+
{
35+
#[ORM\Id]
36+
#[ORM\Column(type: 'integer')]
37+
#[ORM\GeneratedValue(strategy: 'AUTO')]
38+
#[ApiProperty(readable: false, writable: false, identifier: false)]
39+
public $id;
40+
41+
#[ApiProperty(writable: false, identifier: true)]
42+
#[ORM\Column(type: 'uuid', unique: true)]
43+
public $uuid;
44+
45+
#[ORM\OneToMany(targetEntity: ItemLog::class, cascade: ['persist'], orphanRemoval: false, mappedBy: 'item')]
46+
public Collection $logs;
47+
48+
public function __construct()
49+
{
50+
$this->logs = new ArrayCollection();
51+
$this->uuid = Uuid::uuid4();
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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\Issue5722;
15+
16+
use ApiPlatform\Metadata\ApiProperty;
17+
use ApiPlatform\Metadata\ApiResource;
18+
use ApiPlatform\Metadata\GetCollection;
19+
use ApiPlatform\Metadata\Link;
20+
use Doctrine\ORM\Mapping as ORM;
21+
use Ramsey\Uuid\Uuid;
22+
23+
#[ApiResource(
24+
shortName: 'ItemLogIssue5722',
25+
operations: [
26+
new GetCollection(),
27+
],
28+
)]
29+
#[ApiResource(
30+
uriTemplate: '/events/{uuid}/logs{._format}',
31+
operations: [
32+
new GetCollection(),
33+
],
34+
uriVariables: [
35+
'uuid' => new Link(fromProperty: 'logs', fromClass: Event::class),
36+
],
37+
)]
38+
#[ORM\Entity]
39+
class ItemLog
40+
{
41+
#[ORM\Id]
42+
#[ORM\Column(type: 'integer')]
43+
#[ORM\GeneratedValue(strategy: 'AUTO')]
44+
#[ApiProperty(readable: false, writable: false, identifier: false)] // identifier was false before 3.1.14, changing this to true fixed some errors
45+
private ?int $id = null;
46+
47+
#[ApiProperty(writable: false, identifier: true)]
48+
#[ORM\Column(type: 'uuid', unique: true)]
49+
public $uuid;
50+
51+
#[ORM\ManyToOne(targetEntity: Event::class, inversedBy: 'logs')]
52+
public ?Event $item = null;
53+
54+
#[ApiProperty(required: true)]
55+
#[ORM\Column]
56+
public string $action = 'insert';
57+
58+
private \DateTimeInterface $createdAt;
59+
60+
public function __construct()
61+
{
62+
$this->createdAt = new \DateTime();
63+
$this->uuid = Uuid::uuid4();
64+
}
65+
66+
public function getId(): ?int
67+
{
68+
return $this->id;
69+
}
70+
}

0 commit comments

Comments
 (0)