Skip to content

Commit 2f0db8e

Browse files
committed
Add and fix tests about relation inheritance
Previous commits fix the behavior with non api resources inherited from resource but relation management was still not ok since the update was not done on the abstract item normalizer. The resource class resolver is now used as it should in every normalizers. Most of this commit (tests) comes from @vincentchalamon and its PR #2760
1 parent a75b639 commit 2f0db8e

File tree

11 files changed

+441
-21
lines changed

11 files changed

+441
-21
lines changed

features/bootstrap/DoctrineContext.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,4 +1561,44 @@ public function testEagerLoadingNotDuplicateRelation()
15611561
$this->manager->flush();
15621562
$this->manager->clear();
15631563
}
1564+
1565+
/**
1566+
* @Given there are :nb sites with internal owner
1567+
*/
1568+
public function thereAreSitesWithInternalOwner(int $nb)
1569+
{
1570+
for ($i = 1; $i <= $nb; ++$i) {
1571+
$internalUser = new \ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\InternalUser();
1572+
$internalUser->setFirstname('Internal');
1573+
$internalUser->setLastname('User');
1574+
$internalUser->setEmail('john.doe@example.com');
1575+
$internalUser->setInternalId('INT');
1576+
$site = new \ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Site();
1577+
$site->setTitle('title');
1578+
$site->setDescription('description');
1579+
$site->setOwner($internalUser);
1580+
$this->manager->persist($site);
1581+
}
1582+
$this->manager->flush();
1583+
}
1584+
1585+
/**
1586+
* @Given there are :nb sites with external owner
1587+
*/
1588+
public function thereAreSitesWithExternalOwner(int $nb)
1589+
{
1590+
for ($i = 1; $i <= $nb; ++$i) {
1591+
$externalUser = new \ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ExternalUser();
1592+
$externalUser->setFirstname('External');
1593+
$externalUser->setLastname('User');
1594+
$externalUser->setEmail('john.doe@example.com');
1595+
$externalUser->setExternalId('EXT');
1596+
$site = new \ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Site();
1597+
$site->setTitle('title');
1598+
$site->setDescription('description');
1599+
$site->setOwner($externalUser);
1600+
$this->manager->persist($site);
1601+
}
1602+
$this->manager->flush();
1603+
}
15641604
}

features/main/table_inheritance.feature

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,3 +446,114 @@ Feature: Table inheritance
446446
}
447447
}
448448
"""
449+
450+
@!mongodb
451+
Scenario: Generate iri from parent resource
452+
Given there are 3 sites with internal owner
453+
When I add "Content-Type" header equal to "application/ld+json"
454+
And I send a "GET" request to "/sites"
455+
Then the response status code should be 200
456+
And the response should be in JSON
457+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
458+
And the JSON should be valid according to this schema:
459+
"""
460+
{
461+
"type": "object",
462+
"properties": {
463+
"hydra:member": {
464+
"type": "array",
465+
"items": {
466+
"type": "object",
467+
"properties": {
468+
"@type": {
469+
"type": "string",
470+
"pattern": "^Site$",
471+
"required": "true"
472+
},
473+
"@id": {
474+
"type": "string",
475+
"pattern": "^/sites/\\d+$",
476+
"required": "true"
477+
},
478+
"id": {
479+
"type": "integer",
480+
"required": "true"
481+
},
482+
"title": {
483+
"type": "string",
484+
"required": "true"
485+
},
486+
"description": {
487+
"type": "string",
488+
"required": "true"
489+
},
490+
"owner": {
491+
"type": "string",
492+
"pattern": "^/custom_users/\\d+$",
493+
"required": "true"
494+
}
495+
}
496+
},
497+
"minItems": 3,
498+
"maxItems": 3,
499+
"required": "true"
500+
}
501+
}
502+
}
503+
"""
504+
505+
@!mongodb
506+
@createSchema
507+
Scenario: Generate iri from current resource even if parent class is a resource
508+
Given there are 3 sites with external owner
509+
When I add "Content-Type" header equal to "application/ld+json"
510+
And I send a "GET" request to "/sites"
511+
Then the response status code should be 200
512+
And the response should be in JSON
513+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
514+
And the JSON should be valid according to this schema:
515+
"""
516+
{
517+
"type": "object",
518+
"properties": {
519+
"hydra:member": {
520+
"type": "array",
521+
"items": {
522+
"type": "object",
523+
"properties": {
524+
"@type": {
525+
"type": "string",
526+
"pattern": "^Site$",
527+
"required": "true"
528+
},
529+
"@id": {
530+
"type": "string",
531+
"pattern": "^/sites/\\d+$",
532+
"required": "true"
533+
},
534+
"id": {
535+
"type": "integer",
536+
"required": "true"
537+
},
538+
"title": {
539+
"type": "string",
540+
"required": "true"
541+
},
542+
"description": {
543+
"type": "string",
544+
"required": "true"
545+
},
546+
"owner": {
547+
"type": "string",
548+
"pattern": "^/external_users/\\d+$",
549+
"required": "true"
550+
}
551+
}
552+
},
553+
"minItems": 3,
554+
"maxItems": 3,
555+
"required": "true"
556+
}
557+
}
558+
}
559+
"""

src/Serializer/AbstractItemNormalizer.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public function normalize($object, $format = null, array $context = [])
129129
// Use resolved resource class instead of given resource class to support multiple inheritance child types
130130
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, true);
131131
$context = $this->initContext($resourceClass, $context);
132-
$iri = $context['iri'] ?? $this->iriConverter->getIriFromItem($object);
132+
$iri = $context['iri'] ?? $this->iriConverter->getIriFromItemWithResource($object, $resourceClass);
133133
$context['iri'] = $iri;
134134
$context['api_normalize'] = true;
135135

@@ -546,15 +546,17 @@ protected function getAttributeValue($object, $attribute, $format = null, array
546546
$type->isCollection() &&
547547
($collectionValueType = $type->getCollectionValueType()) &&
548548
($className = $collectionValueType->getClassName()) &&
549-
$this->resourceClassResolver->isResourceClass($className)
549+
$this->resourceClassResolver->isResourceClass($className) &&
550+
($className = $this->resourceClassResolver->getResourceClass($attributeValue, $className, true))
550551
) {
551552
return $this->normalizeCollectionOfRelations($propertyMetadata, $attributeValue, $className, $format, $this->createChildContext($context, $attribute));
552553
}
553554

554555
if (
555556
$type &&
556557
($className = $type->getClassName()) &&
557-
$this->resourceClassResolver->isResourceClass($className)
558+
$this->resourceClassResolver->isResourceClass($className) &&
559+
($className = $this->resourceClassResolver->getResourceClass($attributeValue, $className, true))
558560
) {
559561
return $this->normalizeRelation($propertyMetadata, $attributeValue, $className, $format, $this->createChildContext($context, $attribute));
560562
}
@@ -606,7 +608,8 @@ protected function normalizeRelation(PropertyMetadata $propertyMetadata, $relate
606608
return $this->serializer->normalize($relatedObject, $format, $context);
607609
}
608610

609-
$iri = $this->iriConverter->getIriFromItem($relatedObject);
611+
$iri = $this->iriConverter->getIriFromItemWithResource($relatedObject, $resourceClass);
612+
610613
if (isset($context['resources'])) {
611614
$context['resources'][$iri] = $iri;
612615
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
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\Core\Tests\Fixtures\TestBundle\Entity;
15+
16+
use ApiPlatform\Core\Annotation\ApiResource;
17+
use Doctrine\ORM\Mapping as ORM;
18+
19+
/**
20+
* @ORM\Entity
21+
* @ORM\InheritanceType("JOINED")
22+
* @ApiResource(
23+
* collectionOperations={
24+
* "get"={"path"="/custom_users"}
25+
* },
26+
* itemOperations={
27+
* "get"={"path"="/custom_users/{id}"}
28+
* }
29+
* )
30+
*/
31+
abstract class AbstractUser
32+
{
33+
/**
34+
* @ORM\Column(type="integer")
35+
* @ORM\Id
36+
* @ORM\GeneratedValue(strategy="AUTO")
37+
*/
38+
private $id;
39+
/**
40+
* @ORM\Column
41+
*/
42+
private $firstname;
43+
/**
44+
* @ORM\Column
45+
*/
46+
private $lastname;
47+
/**
48+
* @ORM\Column
49+
*/
50+
private $email;
51+
52+
public function getId(): ?int
53+
{
54+
return $this->id;
55+
}
56+
57+
public function getFirstname(): ?string
58+
{
59+
return $this->firstname;
60+
}
61+
62+
public function setFirstname(string $firstname): self
63+
{
64+
$this->firstname = $firstname;
65+
66+
return $this;
67+
}
68+
69+
public function getLastname(): ?string
70+
{
71+
return $this->lastname;
72+
}
73+
74+
public function setLastname(string $lastname): self
75+
{
76+
$this->lastname = $lastname;
77+
78+
return $this;
79+
}
80+
81+
public function getEmail(): ?string
82+
{
83+
return $this->email;
84+
}
85+
86+
public function setEmail(string $email): self
87+
{
88+
$this->email = $email;
89+
90+
return $this;
91+
}
92+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
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\Core\Tests\Fixtures\TestBundle\Entity;
15+
16+
use ApiPlatform\Core\Annotation\ApiResource;
17+
use Doctrine\ORM\Mapping as ORM;
18+
19+
/**
20+
* @ORM\Entity
21+
* @ApiResource
22+
*/
23+
class ExternalUser extends AbstractUser
24+
{
25+
/**
26+
* @ORM\Column
27+
*/
28+
private $externalId;
29+
30+
public function getExternalId(): ?string
31+
{
32+
return $this->externalId;
33+
}
34+
35+
public function setExternalId(string $externalId): self
36+
{
37+
$this->externalId = $externalId;
38+
39+
return $this;
40+
}
41+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
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\Core\Tests\Fixtures\TestBundle\Entity;
15+
16+
use Doctrine\ORM\Mapping as ORM;
17+
18+
/**
19+
* @ORM\Entity
20+
*/
21+
class InternalUser extends AbstractUser
22+
{
23+
/**
24+
* @ORM\Column
25+
*/
26+
private $internalId;
27+
28+
public function getInternalId(): ?string
29+
{
30+
return $this->internalId;
31+
}
32+
33+
public function setInternalId(string $internalId): self
34+
{
35+
$this->internalId = $internalId;
36+
37+
return $this;
38+
}
39+
}

0 commit comments

Comments
 (0)