Skip to content

Commit af70ddf

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 f1bcbe4 commit af70ddf

File tree

11 files changed

+446
-21
lines changed

11 files changed

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

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)