diff --git a/features/bootstrap/DoctrineContext.php b/features/bootstrap/DoctrineContext.php
index cd03b6024d3..bc9a83e1ec2 100644
--- a/features/bootstrap/DoctrineContext.php
+++ b/features/bootstrap/DoctrineContext.php
@@ -31,6 +31,7 @@
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyOffer as DummyOfferDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyProduct as DummyProductDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyProperty as DummyPropertyDocument;
+use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyTableInheritanceNotApiResourceChild as DummyTableInheritanceNotApiResourceChildDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\EmbeddableDummy as EmbeddableDummyDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\EmbeddedDummy as EmbeddedDummyDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\FileConfigDummy as FileConfigDummyDocument;
@@ -74,6 +75,7 @@
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyOffer;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyProduct;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyProperty;
+use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyTableInheritanceNotApiResourceChild;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\EmbeddableDummy;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\EmbeddedDummy;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\FileConfigDummy;
@@ -165,6 +167,17 @@ public function thereAreDummyObjects(int $nb)
$this->manager->flush();
}
+ /**
+ * @When some dummy table inheritance data but not api resource child are created
+ */
+ public function someDummyTableInheritanceDataButNotApiResourceChildAreCreated()
+ {
+ $dummy = $this->buildDummyTableInheritanceNotApiResourceChild();
+ $dummy->setName('Foobarbaz inheritance');
+ $this->manager->persist($dummy);
+ $this->manager->flush();
+ }
+
/**
* @Given there are :nb foo objects with fake names
*/
@@ -1272,6 +1285,14 @@ private function buildDummy()
return $this->isOrm() ? new Dummy() : new DummyDocument();
}
+ /**
+ * @return DummyTableInheritanceNotApiResourceChild|DummyTableInheritanceNotApiResourceChildDocument
+ */
+ private function buildDummyTableInheritanceNotApiResourceChild()
+ {
+ return $this->isOrm() ? new DummyTableInheritanceNotApiResourceChild() : new DummyTableInheritanceNotApiResourceChildDocument();
+ }
+
/**
* @return DummyAggregateOffer|DummyAggregateOfferDocument
*/
@@ -1540,4 +1561,44 @@ public function testEagerLoadingNotDuplicateRelation()
$this->manager->flush();
$this->manager->clear();
}
+
+ /**
+ * @Given there are :nb sites with internal owner
+ */
+ public function thereAreSitesWithInternalOwner(int $nb)
+ {
+ for ($i = 1; $i <= $nb; ++$i) {
+ $internalUser = new \ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\InternalUser();
+ $internalUser->setFirstname('Internal');
+ $internalUser->setLastname('User');
+ $internalUser->setEmail('john.doe@example.com');
+ $internalUser->setInternalId('INT');
+ $site = new \ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Site();
+ $site->setTitle('title');
+ $site->setDescription('description');
+ $site->setOwner($internalUser);
+ $this->manager->persist($site);
+ }
+ $this->manager->flush();
+ }
+
+ /**
+ * @Given there are :nb sites with external owner
+ */
+ public function thereAreSitesWithExternalOwner(int $nb)
+ {
+ for ($i = 1; $i <= $nb; ++$i) {
+ $externalUser = new \ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ExternalUser();
+ $externalUser->setFirstname('External');
+ $externalUser->setLastname('User');
+ $externalUser->setEmail('john.doe@example.com');
+ $externalUser->setExternalId('EXT');
+ $site = new \ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Site();
+ $site->setTitle('title');
+ $site->setDescription('description');
+ $site->setOwner($externalUser);
+ $this->manager->persist($site);
+ }
+ $this->manager->flush();
+ }
}
diff --git a/features/main/operation.feature b/features/main/operation.feature
index 2e2cc094b49..b81805498ea 100644
--- a/features/main/operation.feature
+++ b/features/main/operation.feature
@@ -4,7 +4,6 @@ Feature: Operation support
I need to be able to add custom operations and remove built-in ones
@createSchema
- @dropSchema
Scenario: Can not write readonly property
When I add "Content-Type" header equal to "application/ld+json"
And I send a "POST" request to "/readable_only_properties" with body:
diff --git a/features/main/relation.feature b/features/main/relation.feature
index 0ba6404f8bd..24c5b7e1efd 100644
--- a/features/main/relation.feature
+++ b/features/main/relation.feature
@@ -491,7 +491,7 @@ Feature: Relations support
Given there are people having pets
When I add "Content-Type" header equal to "application/ld+json"
And I send a "GET" request to "/people"
- And the response status code should be 200
+ Then the response status code should be 200
And the response should be in JSON
And the JSON should be equal to:
"""
@@ -621,8 +621,6 @@ Feature: Relations support
}
"""
-
- @dropSchema
Scenario: Passing an invalid IRI to a relation
When I add "Content-Type" header equal to "application/ld+json"
And I send a "POST" request to "/relation_embedders" with body:
@@ -634,7 +632,7 @@ Feature: Relations support
Then the response status code should be 400
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 node "hydra:description" should contain "Invalid value provided (invalid IRI?)."
+ And the JSON node "hydra:description" should contain 'Invalid IRI "certainly not an iri and not a plain identifier".'
Scenario: Passing an invalid type to a relation
When I add "Content-Type" header equal to "application/ld+json"
@@ -647,4 +645,32 @@ Feature: Relations support
Then the response status code should be 400
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 node "hydra:description" should contain "Invalid value provided (invalid IRI?)."
+ And the JSON should be valid according to this schema:
+ """
+ {
+ "type": "object",
+ "properties": {
+ "@context": {
+ "type": "string",
+ "pattern": "^/contexts/Error$"
+ },
+ "@type": {
+ "type": "string",
+ "pattern": "^hydra:Error$"
+ },
+ "hydra:title": {
+ "type": "string",
+ "pattern": "^An error occurred$"
+ },
+ "hydra:description": {
+ "pattern": "^Expected IRI or document for resource \"ApiPlatform\\\\Core\\\\Tests\\\\Fixtures\\\\TestBundle\\\\(Document|Entity)\\\\RelatedDummy\", \"integer\" given.$"
+ }
+ },
+ "required": [
+ "@context",
+ "@type",
+ "hydra:title",
+ "hydra:description"
+ ]
+ }
+ """
diff --git a/features/main/table_inheritance.feature b/features/main/table_inheritance.feature
index 3cf9c9e46f9..379e39f49e1 100644
--- a/features/main/table_inheritance.feature
+++ b/features/main/table_inheritance.feature
@@ -32,15 +32,20 @@ Feature: Table inheritance
},
"name": {
"type": "string",
- "pattern": "^foo$",
- "required": "true"
+ "pattern": "^foo$"
},
"nickname": {
"type": "string",
- "pattern": "^bar$",
- "required": "true"
+ "pattern": "^bar$"
}
- }
+ },
+ "required": [
+ "@type",
+ "@context",
+ "@id",
+ "name",
+ "nickname"
+ ]
}
"""
@@ -56,31 +61,114 @@ Feature: Table inheritance
"properties": {
"hydra:member": {
"type": "array",
- "items": {
- "type": "object",
- "properties": {
- "@type": {
- "type": "string",
- "pattern": "^DummyTableInheritanceChild$"
+ "items": [
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^DummyTableInheritanceChild$"
+ },
+ "@id": {
+ "type": "string",
+ "pattern": "^/dummy_table_inheritance_children/1$"
+ },
+ "name": {
+ "type": "string"
+ },
+ "nickname": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "@type",
+ "@id",
+ "name",
+ "nickname"
+ ]
+ }
+ ],
+ "additionalItems": false
+ }
+ },
+ "required": [
+ "hydra:member"
+ ]
+ }
+ """
+
+ Scenario: Some children not api resources are created in the app
+ When some dummy table inheritance data but not api resource child are created
+ And I send a "GET" request to "/dummy_table_inheritances"
+ 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 valid according to this schema:
+ """
+ {
+ "type": "object",
+ "properties": {
+ "hydra:member": {
+ "type": "array",
+ "items": [
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^DummyTableInheritanceChild$"
+ },
+ "@id": {
+ "type": "string",
+ "pattern": "^/dummy_table_inheritance_children/1$"
+ },
+ "name": {
+ "type": "string"
+ }
},
- "name": {
- "type": "string",
- "required": "true"
+ "required": [
+ "@type",
+ "@id",
+ "name"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^DummyTableInheritance$"
+ },
+ "@id": {
+ "type": "string",
+ "pattern": "^/dummy_table_inheritances/2$"
+ },
+ "name": {
+ "type": "string"
+ }
},
- "nickname": {
- "type": "string",
- "required": "true"
- }
+ "required": [
+ "@type",
+ "@id",
+ "name"
+ ]
}
- },
- "minItems": 1
+ ],
+ "additionalItems": false
+ },
+ "hydra:totalItems": {
+ "type": "integer",
+ "minimum": 2,
+ "maximum": 2
}
},
- "required": ["hydra:member"]
+ "required": [
+ "hydra:member",
+ "hydra:totalItems"
+ ]
}
"""
- @createSchema
Scenario: Create a table inherited resource
When I add "Content-Type" header equal to "application/ld+json"
And I send a "POST" request to "/dummy_table_inheritance_children" with body:
@@ -105,19 +193,24 @@ Feature: Table inheritance
},
"@id": {
"type": "string",
- "pattern": "^/dummy_table_inheritance_children/1$"
+ "pattern": "^/dummy_table_inheritance_children/3$"
},
"name": {
"type": "string",
- "pattern": "^foo$",
- "required": "true"
+ "pattern": "^foo$"
},
"nickname": {
"type": "string",
- "pattern": "^bar$",
- "required": "true"
+ "pattern": "^bar$"
}
- }
+ },
+ "required": [
+ "@type",
+ "@context",
+ "@id",
+ "name",
+ "nickname"
+ ]
}
"""
@@ -144,19 +237,24 @@ Feature: Table inheritance
},
"@id": {
"type": "string",
- "pattern": "^/dummy_table_inheritance_different_children/2$"
+ "pattern": "^/dummy_table_inheritance_different_children/4$"
},
"name": {
"type": "string",
- "pattern": "^foo$",
- "required": "true"
+ "pattern": "^foo$"
},
"email": {
"type": "string",
- "pattern": "^bar\\@localhost$",
- "required": "true"
+ "pattern": "^bar\\@localhost$"
}
- }
+ },
+ "required": [
+ "@type",
+ "@context",
+ "@id",
+ "name",
+ "email"
+ ]
}
"""
@@ -167,7 +265,7 @@ Feature: Table inheritance
{
"children": [
"/dummy_table_inheritance_children/1",
- "/dummy_table_inheritance_different_children/2"
+ "/dummy_table_inheritance_different_children/4"
]
}
"""
@@ -192,47 +290,58 @@ Feature: Table inheritance
"pattern": "^/dummy_table_inheritance_relateds/1$"
},
"children": {
- "items": {
- "type": "object",
- "anyOf": [
- {
- "properties": {
- "@type": {
- "type": "string",
- "pattern": "^DummyTableInheritanceChild$"
- },
- "name": {
- "type": "string",
- "required": "true"
- },
- "nickname": {
- "type": "string",
- "required": "true"
- }
+ "type": "array",
+ "items": [
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^DummyTableInheritanceChild$"
+ },
+ "name": {
+ "type": "string"
+ },
+ "nickname": {
+ "type": "string"
}
},
- {
- "properties": {
- "@type": {
- "type": "string",
- "pattern": "^DummyTableInheritanceDifferentChild$"
- },
- "name": {
- "type": "string",
- "required": "true"
- },
- "email": {
- "type": "string",
- "required": "true"
- }
+ "required": [
+ "@type",
+ "name",
+ "nickname"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^DummyTableInheritanceDifferentChild$"
+ },
+ "name": {
+ "type": "string"
+ },
+ "email": {
+ "type": "string"
}
- }
- ]
- },
- "minItems": 2,
- "maxItems": 2
+ },
+ "required": [
+ "@type",
+ "name",
+ "email"
+ ]
+ }
+ ],
+ "additionalItems": false
}
- }
+ },
+ "required": [
+ "@type",
+ "@context",
+ "@id",
+ "children"
+ ]
}
"""
@@ -248,81 +357,170 @@ Feature: Table inheritance
"properties": {
"hydra:member": {
"type": "array",
- "items": {
- "type": "object",
- "anyOf": [
- {
- "properties": {
- "@type": {
- "type": "string",
- "pattern": "^DummyTableInheritanceChild$"
- },
- "name": {
- "type": "string",
- "required": "true"
- },
- "nickname": {
- "type": "string",
- "required": "true"
- }
+ "items": [
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^DummyTableInheritanceChild$"
+ },
+ "@id": {
+ "type": "string",
+ "pattern": "^/dummy_table_inheritance_children/1$"
+ },
+ "name": {
+ "type": "string"
+ },
+ "nickname": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "@type",
+ "@id",
+ "name",
+ "nickname"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^DummyTableInheritance$"
+ },
+ "@id": {
+ "type": "string",
+ "pattern": "^/dummy_table_inheritances/2$"
+ },
+ "name": {
+ "type": "string"
}
},
- {
- "properties": {
- "@type": {
- "type": "string",
- "pattern": "^DummyTableInheritanceDifferentChild$"
- },
- "name": {
- "type": "string",
- "required": "true"
- },
- "email": {
- "type": "string",
- "required": "true"
- }
+ "required": [
+ "@type",
+ "@id",
+ "name"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^DummyTableInheritanceChild$"
+ },
+ "@id": {
+ "type": "string",
+ "pattern": "^/dummy_table_inheritance_children/3$"
+ },
+ "name": {
+ "type": "string"
+ },
+ "nickname": {
+ "type": "string"
}
- }
- ]
- },
- "minItems": 2
+ },
+ "required": [
+ "@type",
+ "@id",
+ "name",
+ "nickname"
+ ]
+ }
+ ],
+ "additionalItems": false
+ },
+ "hydra:totalItems": {
+ "type": "integer",
+ "minimum": 4,
+ "maximum": 4
}
},
- "required": ["hydra:member"]
+ "required": [
+ "hydra:member",
+ "hydra:totalItems"
+ ]
}
"""
Scenario: Get the parent interface collection
- When I send a "GET" request to "/resource_interfaces"
- 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 valid according to this schema:
- """
- {
- "type": "object",
- "properties": {
- "hydra:member": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "foo": {
- "type": "string",
- "required": "true"
- },
- "fooz": {
- "type": "string",
- "required": "true"
- }
- }
- },
- "minItems": 1
- }
- },
- "required": ["hydra:member"]
- }
- """
+ When I send a "GET" request to "/resource_interfaces"
+ 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 valid according to this schema:
+ """
+ {
+ "type": "object",
+ "properties": {
+ "hydra:member": {
+ "type": "array",
+ "items": [
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^ResourceInterface$"
+ },
+ "@id": {
+ "type": "string",
+ "pattern": "^/resource_interfaces/item1"
+ },
+ "foo": {
+ "type": "string",
+ "pattern": "^item1$"
+ },
+ "fooz": {
+ "type": "string",
+ "pattern": "^fooz$"
+ }
+ },
+ "required": [
+ "@type",
+ "@id",
+ "foo",
+ "fooz"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^ResourceInterface$"
+ },
+ "@id": {
+ "type": "string",
+ "pattern": "^/resource_interfaces/item2"
+ },
+ "foo": {
+ "type": "string",
+ "pattern": "^item2$"
+ },
+ "fooz": {
+ "type": "string",
+ "pattern": "^fooz$"
+ }
+ },
+ "required": [
+ "@type",
+ "@id",
+ "foo",
+ "fooz"
+ ]
+ }
+ ],
+ "additionalItems": false
+ }
+ },
+ "required": [
+ "hydra:member"
+ ]
+ }
+ """
Scenario: Get an interface resource item
When I send a "GET" request to "/resource_interfaces/some-id"
@@ -334,19 +532,267 @@ Feature: Table inheritance
{
"type": "object",
"properties": {
- "context": {
+ "@context": {
+ "type": "string",
+ "pattern": "^/contexts/ResourceInterface$"
+ },
+ "@id": {
"type": "string",
- "pattern": "ResourceInterface$"
+ "pattern": "^/resource_interfaces/single%2520item$"
+ },
+ "@type": {
+ "type": "string",
+ "pattern": "^ResourceInterface$"
},
"foo": {
"type": "string",
- "required": "true"
+ "pattern": "^single item$"
},
"fooz": {
"type": "string",
- "required": "true",
"pattern": "fooz"
}
- }
+ },
+ "required": [
+ "@context",
+ "@id",
+ "@type",
+ "foo",
+ "fooz"
+ ],
+ "additionalProperties": false
+ }
+ """
+
+ @!mongodb
+ Scenario: Generate iri from parent resource
+ Given there are 3 sites with internal owner
+ When I add "Content-Type" header equal to "application/ld+json"
+ And I send a "GET" request to "/sites"
+ 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 valid according to this schema:
+ """
+ {
+ "type": "object",
+ "properties": {
+ "hydra:member": {
+ "type": "array",
+ "items": [
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^Site$"
+ },
+ "@id": {
+ "type": "string",
+ "pattern": "^/sites/1$"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "owner": {
+ "type": "string",
+ "pattern": "^/custom_users/1$"
+ }
+ },
+ "required": [
+ "@type",
+ "@id",
+ "title",
+ "description",
+ "owner"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^Site$"
+ },
+ "@id": {
+ "type": "string",
+ "pattern": "^/sites/2$"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "owner": {
+ "type": "string",
+ "pattern": "^/custom_users/2$"
+ }
+ },
+ "required": [
+ "@type",
+ "@id",
+ "title",
+ "description",
+ "owner"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^Site$"
+ },
+ "@id": {
+ "type": "string",
+ "pattern": "^/sites/3$"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "owner": {
+ "type": "string",
+ "pattern": "^/custom_users/3$"
+ }
+ },
+ "required": [
+ "@type",
+ "@id",
+ "title",
+ "description",
+ "owner"
+ ]
+ }
+ ],
+ "additionalItems": false
+ }
+ },
+ "required": [
+ "hydra:member"
+ ]
+ }
+ """
+
+ @!mongodb
+ @createSchema
+ Scenario: Generate iri from current resource even if parent class is a resource
+ Given there are 3 sites with external owner
+ When I add "Content-Type" header equal to "application/ld+json"
+ And I send a "GET" request to "/sites"
+ 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 valid according to this schema:
+ """
+ {
+ "type": "object",
+ "properties": {
+ "hydra:member": {
+ "type": "array",
+ "items": [
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^Site$"
+ },
+ "@id": {
+ "type": "string",
+ "pattern": "^/sites/1$"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "owner": {
+ "type": "string",
+ "pattern": "^/external_users/1$"
+ }
+ },
+ "required": [
+ "@type",
+ "@id",
+ "title",
+ "description",
+ "owner"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^Site$"
+ },
+ "@id": {
+ "type": "string",
+ "pattern": "^/sites/2$"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "owner": {
+ "type": "string",
+ "pattern": "^/external_users/2$"
+ }
+ },
+ "required": [
+ "@type",
+ "@id",
+ "title",
+ "description",
+ "owner"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string",
+ "pattern": "^Site$"
+ },
+ "@id": {
+ "type": "string",
+ "pattern": "^/sites/3$"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "owner": {
+ "type": "string",
+ "pattern": "^/external_users/3$"
+ }
+ },
+ "required": [
+ "@type",
+ "@id",
+ "title",
+ "description",
+ "owner"
+ ]
+ }
+ ],
+ "additionalItems": false
+ }
+ },
+ "required": [
+ "hydra:member"
+ ]
}
"""
diff --git a/features/security/strong_typing.feature b/features/security/strong_typing.feature
index 6a3c4fb56dc..ef630a2c27a 100644
--- a/features/security/strong_typing.feature
+++ b/features/security/strong_typing.feature
@@ -73,7 +73,7 @@ Feature: Handle properly invalid data submitted to the API
And the JSON node "@context" should be equal to "/contexts/Error"
And the JSON node "@type" should be equal to "hydra:Error"
And the JSON node "hydra:title" should be equal to "An error occurred"
- And the JSON node "hydra:description" should be equal to 'Expected IRI or nested document for attribute "relatedDummy", "string" given.'
+ And the JSON node "hydra:description" should be equal to 'Invalid IRI "1".'
And the JSON node "trace" should exist
Scenario: Ignore invalid dates
diff --git a/features/serializer/vo_relations.feature b/features/serializer/vo_relations.feature
index 1f4759460d3..78deaecbe34 100644
--- a/features/serializer/vo_relations.feature
+++ b/features/serializer/vo_relations.feature
@@ -26,28 +26,28 @@ Feature: Value object as ApiResource
Then the response status code should be 201
And the JSON should be equal to:
"""
- {
- "@context": "/contexts/VoDummyCar",
- "@id": "/vo_dummy_cars/1",
- "@type": "VoDummyCar",
- "mileage": 1500,
- "bodyType": "suv",
- "inspections": [],
- "make": "CustomCar",
- "insuranceCompany": {
- "@id": "/vo_dummy_insurance_companies/1",
- "@type": "VoDummyInsuranceCompany",
- "name": "Safe Drive Company"
- },
- "drivers": [
- {
- "@id": "/vo_dummy_drivers/1",
- "@type": "VoDummyDriver",
- "firstName": "John",
- "lastName": "Doe"
- }
- ]
- }
+ {
+ "@context": "/contexts/VoDummyCar",
+ "@id": "/vo_dummy_cars/1",
+ "@type": "VoDummyCar",
+ "mileage": 1500,
+ "bodyType": "suv",
+ "inspections": [],
+ "make": "CustomCar",
+ "insuranceCompany": {
+ "@id": "/vo_dummy_insurance_companies/1",
+ "@type": "VoDummyInsuranceCompany",
+ "name": "Safe Drive Company"
+ },
+ "drivers": [
+ {
+ "@id": "/vo_dummy_drivers/1",
+ "@type": "VoDummyDriver",
+ "firstName": "John",
+ "lastName": "Doe"
+ }
+ ]
+ }
"""
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
@@ -98,8 +98,7 @@ Feature: Value object as ApiResource
"@type": "VoDummyInspection",
"accepted": true,
"car": "/vo_dummy_cars/1",
- "performed": "2018-08-24T00:00:00+00:00",
- "id": 1
+ "performed": "2018-08-24T00:00:00+00:00"
}
"""
@@ -117,27 +116,36 @@ Feature: Value object as ApiResource
}
"""
Then the response status code should be 400
+ And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON should be valid according to this schema:
"""
{
"type": "object",
"properties": {
"@context": {
- "enum": ["/contexts/Error"]
+ "type": "string",
+ "pattern": "^/contexts/Error$"
},
- "type": {
- "enum": ["hydra:Error"]
+ "@type": {
+ "type": "string",
+ "pattern": "^hydra:Error$"
},
"hydra:title": {
- "enum": ["An error occurred"]
+ "type": "string",
+ "pattern": "^An error occurred$"
},
"hydra:description": {
"pattern": "^Cannot create an instance of ApiPlatform\\\\Core\\\\Tests\\\\Fixtures\\\\TestBundle\\\\(Document|Entity)\\\\VoDummyCar from serialized data because its constructor requires parameter \"drivers\" to be present.$"
}
- }
+ },
+ "required": [
+ "@context",
+ "@type",
+ "hydra:title",
+ "hydra:description"
+ ]
}
"""
- And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
@createSchema
Scenario: Create Value object without default param
diff --git a/src/Api/CachedIdentifiersExtractor.php b/src/Api/CachedIdentifiersExtractor.php
index 7934dbec9cf..ca15e065420 100644
--- a/src/Api/CachedIdentifiersExtractor.php
+++ b/src/Api/CachedIdentifiersExtractor.php
@@ -13,7 +13,7 @@
namespace ApiPlatform\Core\Api;
-use ApiPlatform\Core\Util\ClassInfoTrait;
+use ApiPlatform\Core\Util\ResourceClassInfoTrait;
use Psr\Cache\CacheException;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\PropertyAccess\PropertyAccess;
@@ -26,14 +26,13 @@
*/
final class CachedIdentifiersExtractor implements IdentifiersExtractorInterface
{
- use ClassInfoTrait;
+ use ResourceClassInfoTrait;
public const CACHE_KEY_PREFIX = 'iri_identifiers';
private $cacheItemPool;
private $propertyAccessor;
private $decorated;
- private $resourceClassResolver;
private $localCache = [];
private $localResourceCache = [];
@@ -82,9 +81,7 @@ public function getIdentifiersFromItem($item): array
continue;
}
- $relatedResourceClass = $this->getObjectClass($identifiers[$propertyName]);
-
- if (null !== $this->resourceClassResolver && !$this->resourceClassResolver->isResourceClass($relatedResourceClass)) {
+ if (null === $relatedResourceClass = $this->getResourceClass($identifiers[$propertyName])) {
continue;
}
diff --git a/src/Api/IdentifiersExtractor.php b/src/Api/IdentifiersExtractor.php
index cd630ec5975..f7f7dc65b85 100644
--- a/src/Api/IdentifiersExtractor.php
+++ b/src/Api/IdentifiersExtractor.php
@@ -16,7 +16,7 @@
use ApiPlatform\Core\Exception\RuntimeException;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
-use ApiPlatform\Core\Util\ClassInfoTrait;
+use ApiPlatform\Core\Util\ResourceClassInfoTrait;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
@@ -27,12 +27,11 @@
*/
final class IdentifiersExtractor implements IdentifiersExtractorInterface
{
- use ClassInfoTrait;
+ use ResourceClassInfoTrait;
private $propertyNameCollectionFactory;
private $propertyMetadataFactory;
private $propertyAccessor;
- private $resourceClassResolver;
public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, PropertyAccessorInterface $propertyAccessor = null, ResourceClassResolverInterface $resourceClassResolver = null)
{
@@ -67,7 +66,8 @@ public function getIdentifiersFromResourceClass(string $resourceClass): array
public function getIdentifiersFromItem($item): array
{
$identifiers = [];
- $resourceClass = $this->getObjectClass($item);
+ $resourceClass = $this->getResourceClass($item, true);
+
foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $propertyName) {
$propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName);
$identifier = $propertyMetadata->isIdentifier();
@@ -81,9 +81,7 @@ public function getIdentifiersFromItem($item): array
continue;
}
- $relatedResourceClass = $this->getObjectClass($identifier);
-
- if (null !== $this->resourceClassResolver && !$this->resourceClassResolver->isResourceClass($relatedResourceClass)) {
+ if (null === $relatedResourceClass = $this->getResourceClass($identifier)) {
continue;
}
diff --git a/src/Api/ResourceClassResolver.php b/src/Api/ResourceClassResolver.php
index f75f58227d1..4479f4abe0e 100644
--- a/src/Api/ResourceClassResolver.php
+++ b/src/Api/ResourceClassResolver.php
@@ -40,33 +40,45 @@ public function __construct(ResourceNameCollectionFactoryInterface $resourceName
*/
public function getResourceClass($value, string $resourceClass = null, bool $strict = false): string
{
- $type = \is_object($value) && !$value instanceof \Traversable ? $this->getObjectClass($value) : $resourceClass;
- $resourceClass = $resourceClass ?? $type;
+ if ($strict && null === $resourceClass) {
+ throw new InvalidArgumentException('Strict checking is only possible when resource class is specified.');
+ }
+
+ $actualClass = \is_object($value) && !$value instanceof \Traversable ? $this->getObjectClass($value) : null;
- if (null === $resourceClass) {
- throw new InvalidArgumentException(sprintf('No resource class found.'));
+ if (null === $actualClass && null === $resourceClass) {
+ throw new InvalidArgumentException('Resource type could not be determined. Resource class must be specified.');
}
- if (
- null === $type
- || ((!$strict || $resourceClass === $type) && $isResourceClass = $this->isResourceClass($type))
- ) {
+ if (null !== $resourceClass && !$this->isResourceClass($resourceClass)) {
+ throw new InvalidArgumentException(sprintf('Specified class "%s" is not a resource class.', $resourceClass));
+ }
+
+ if (null === $actualClass) {
return $resourceClass;
}
- // The Resource is an interface
- if ($value instanceof $resourceClass && $type !== $resourceClass && interface_exists($resourceClass)) {
- throw new InvalidArgumentException(sprintf('The given object\'s resource is the interface "%s", finding a class is not possible.', $resourceClass));
+ if ($strict && !is_a($actualClass, $resourceClass, true)) {
+ throw new InvalidArgumentException(sprintf('Object of type "%s" does not match "%s" resource class.', $actualClass, $resourceClass));
+ }
+
+ $mostSpecificResourceClass = null;
+
+ foreach ($this->resourceNameCollectionFactory->create() as $resourceClassName) {
+ if (!is_a($actualClass, $resourceClassName, true)) {
+ continue;
+ }
+
+ if (null === $mostSpecificResourceClass || is_subclass_of($resourceClassName, $mostSpecificResourceClass, true)) {
+ $mostSpecificResourceClass = $resourceClassName;
+ }
}
- if (
- ($isResourceClass ?? $this->isResourceClass($type))
- || (is_subclass_of($type, $resourceClass) && $this->isResourceClass($resourceClass))
- ) {
- return $type;
+ if (null === $mostSpecificResourceClass) {
+ throw new InvalidArgumentException(sprintf('No resource class found for object of type "%s".', $actualClass));
}
- throw new InvalidArgumentException(sprintf('No resource class found for object of type "%s".', $type));
+ return $mostSpecificResourceClass;
}
/**
@@ -79,7 +91,7 @@ public function isResourceClass(string $type): bool
}
foreach ($this->resourceNameCollectionFactory->create() as $resourceClass) {
- if ($type === $resourceClass) {
+ if (is_a($type, $resourceClass, true)) {
return $this->localIsResourceClassCache[$type] = true;
}
}
diff --git a/src/Api/ResourceClassResolverInterface.php b/src/Api/ResourceClassResolverInterface.php
index 41efc5c9be8..15363cfb02f 100644
--- a/src/Api/ResourceClassResolverInterface.php
+++ b/src/Api/ResourceClassResolverInterface.php
@@ -25,6 +25,9 @@ interface ResourceClassResolverInterface
/**
* Guesses the associated resource.
*
+ * @param string $resourceClass The expected resource class
+ * @param bool $strict If true, value must match the expected resource class
+ *
* @throws InvalidArgumentException
*/
public function getResourceClass($value, string $resourceClass = null, bool $strict = false): string;
diff --git a/src/Bridge/Doctrine/EventListener/PublishMercureUpdatesListener.php b/src/Bridge/Doctrine/EventListener/PublishMercureUpdatesListener.php
index 805e2585baa..811c460c442 100644
--- a/src/Bridge/Doctrine/EventListener/PublishMercureUpdatesListener.php
+++ b/src/Bridge/Doctrine/EventListener/PublishMercureUpdatesListener.php
@@ -19,7 +19,7 @@
use ApiPlatform\Core\Exception\InvalidArgumentException;
use ApiPlatform\Core\Exception\RuntimeException;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
-use ApiPlatform\Core\Util\ClassInfoTrait;
+use ApiPlatform\Core\Util\ResourceClassInfoTrait;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\Mercure\Update;
@@ -35,9 +35,8 @@
*/
final class PublishMercureUpdatesListener
{
- use ClassInfoTrait;
+ use ResourceClassInfoTrait;
- private $resourceClassResolver;
private $iriConverter;
private $resourceMetadataFactory;
private $serializer;
@@ -120,8 +119,7 @@ private function reset(): void
*/
private function storeEntityToPublish($entity, string $property): void
{
- $resourceClass = $this->getObjectClass($entity);
- if (!$this->resourceClassResolver->isResourceClass($resourceClass)) {
+ if (null === $resourceClass = $this->getResourceClass($entity)) {
return;
}
diff --git a/src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php b/src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php
index d2a40199b14..82d63abad47 100644
--- a/src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php
+++ b/src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php
@@ -81,6 +81,8 @@ public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGenerator
}
/**
+ * {@inheritdoc}
+ *
* The context may contain serialization groups which helps defining joined entities that are readable.
*/
public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, string $operationName = null, array $context = [])
@@ -111,6 +113,10 @@ private function apply(bool $collection, QueryBuilder $queryBuilder, QueryNameGe
return;
}
+ if (!empty($context[AbstractNormalizer::GROUPS])) {
+ $options['serializer_groups'] = $context[AbstractNormalizer::GROUPS];
+ }
+
$this->joinRelations($queryBuilder, $queryNameGenerator, $resourceClass, $forceEager, $fetchPartial, $queryBuilder->getRootAliases()[0], $options, $context);
}
@@ -134,10 +140,6 @@ private function joinRelations(QueryBuilder $queryBuilder, QueryNameGeneratorInt
$classMetadata = $entityManager->getClassMetadata($resourceClass);
$attributesMetadata = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($resourceClass)->getAttributesMetadata() : null;
- if (!empty($normalizationContext[AbstractNormalizer::GROUPS])) {
- $options['serializer_groups'] = $normalizationContext[AbstractNormalizer::GROUPS];
- }
-
foreach ($classMetadata->associationMappings as $association => $mapping) {
//Don't join if max depth is enabled and the current depth limit is reached
if (0 === $currentDepth && ($normalizationContext[AbstractObjectNormalizer::ENABLE_MAX_DEPTH] ?? false)) {
diff --git a/src/Bridge/Symfony/Bundle/Resources/config/api.xml b/src/Bridge/Symfony/Bundle/Resources/config/api.xml
index 47e1aa9f81d..67473b6fc91 100644
--- a/src/Bridge/Symfony/Bundle/Resources/config/api.xml
+++ b/src/Bridge/Symfony/Bundle/Resources/config/api.xml
@@ -61,6 +61,7 @@
+
diff --git a/src/Bridge/Symfony/Routing/IriConverter.php b/src/Bridge/Symfony/Routing/IriConverter.php
index 83f378cdae0..681e012f42e 100644
--- a/src/Bridge/Symfony/Routing/IriConverter.php
+++ b/src/Bridge/Symfony/Routing/IriConverter.php
@@ -17,6 +17,7 @@
use ApiPlatform\Core\Api\IdentifiersExtractorInterface;
use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\Api\OperationType;
+use ApiPlatform\Core\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\Api\UrlGeneratorInterface;
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\OperationDataProviderTrait;
@@ -29,7 +30,7 @@
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
use ApiPlatform\Core\Util\AttributesExtractor;
-use ApiPlatform\Core\Util\ClassInfoTrait;
+use ApiPlatform\Core\Util\ResourceClassInfoTrait;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Routing\Exception\ExceptionInterface as RoutingExceptionInterface;
@@ -42,14 +43,14 @@
*/
final class IriConverter implements IriConverterInterface
{
- use ClassInfoTrait;
+ use ResourceClassInfoTrait;
use OperationDataProviderTrait;
private $routeNameResolver;
private $router;
private $identifiersExtractor;
- public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ItemDataProviderInterface $itemDataProvider, RouteNameResolverInterface $routeNameResolver, RouterInterface $router, PropertyAccessorInterface $propertyAccessor = null, IdentifiersExtractorInterface $identifiersExtractor = null, SubresourceDataProviderInterface $subresourceDataProvider = null, IdentifierConverterInterface $identifierConverter = null)
+ public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ItemDataProviderInterface $itemDataProvider, RouteNameResolverInterface $routeNameResolver, RouterInterface $router, PropertyAccessorInterface $propertyAccessor = null, IdentifiersExtractorInterface $identifiersExtractor = null, SubresourceDataProviderInterface $subresourceDataProvider = null, IdentifierConverterInterface $identifierConverter = null, ResourceClassResolverInterface $resourceClassResolver = null)
{
$this->itemDataProvider = $itemDataProvider;
$this->routeNameResolver = $routeNameResolver;
@@ -57,6 +58,7 @@ public function __construct(PropertyNameCollectionFactoryInterface $propertyName
$this->identifiersExtractor = $identifiersExtractor;
$this->subresourceDataProvider = $subresourceDataProvider;
$this->identifierConverter = $identifierConverter;
+ $this->resourceClassResolver = $resourceClassResolver;
if (null === $identifiersExtractor) {
@trigger_error(sprintf('Not injecting "%s" is deprecated since API Platform 2.1 and will not be possible anymore in API Platform 3', IdentifiersExtractorInterface::class), E_USER_DEPRECATED);
@@ -115,7 +117,7 @@ public function getItemFromIri(string $iri, array $context = [])
*/
public function getIriFromItem($item, int $referenceType = UrlGeneratorInterface::ABS_PATH): string
{
- $resourceClass = $this->getObjectClass($item);
+ $resourceClass = $this->getResourceClass($item, true);
$routeName = $this->routeNameResolver->getRouteName($resourceClass, OperationType::ITEM);
try {
diff --git a/src/GraphQl/Resolver/Factory/CollectionResolverFactory.php b/src/GraphQl/Resolver/Factory/CollectionResolverFactory.php
index 17de6afd06e..40d3d9d77cf 100644
--- a/src/GraphQl/Resolver/Factory/CollectionResolverFactory.php
+++ b/src/GraphQl/Resolver/Factory/CollectionResolverFactory.php
@@ -77,10 +77,12 @@ public function __invoke(string $resourceClass = null, string $rootClass = null,
}
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
- $dataProviderContext = $resourceMetadata->getGraphqlAttribute($operationName ?? 'query', 'normalization_context', [], true);
- $dataProviderContext['attributes'] = $this->fieldsToAttributes($info);
+ $normalizationContext = $resourceMetadata->getGraphqlAttribute($operationName ?? 'query', 'normalization_context', [], true);
+ $normalizationContext['attributes'] = $this->fieldsToAttributes($info);
+ $dataProviderContext = $normalizationContext;
$dataProviderContext['filters'] = $this->getNormalizedFilters($args);
$dataProviderContext['graphql'] = true;
+ $normalizationContext['resource_class'] = $resourceClass;
if (isset($rootClass, $source[$rootProperty = $info->fieldName], $source[ItemNormalizer::ITEM_KEY])) {
$rootResolvedFields = $this->identifiersExtractor->getIdentifiersFromItem(unserialize($source[ItemNormalizer::ITEM_KEY]));
@@ -95,7 +97,7 @@ public function __invoke(string $resourceClass = null, string $rootClass = null,
if (!$this->paginationEnabled) {
$data = [];
foreach ($collection as $index => $object) {
- $data[$index] = $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $dataProviderContext);
+ $data[$index] = $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $normalizationContext);
}
return $data;
@@ -120,7 +122,7 @@ public function __invoke(string $resourceClass = null, string $rootClass = null,
foreach ($collection as $index => $object) {
$data['edges'][$index] = [
- 'node' => $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $dataProviderContext),
+ 'node' => $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $normalizationContext),
'cursor' => base64_encode((string) ($index + $offset)),
];
}
diff --git a/src/GraphQl/Resolver/Factory/ItemMutationResolverFactory.php b/src/GraphQl/Resolver/Factory/ItemMutationResolverFactory.php
index 705a26d7a94..43e3f5521b2 100644
--- a/src/GraphQl/Resolver/Factory/ItemMutationResolverFactory.php
+++ b/src/GraphQl/Resolver/Factory/ItemMutationResolverFactory.php
@@ -77,12 +77,14 @@ public function __invoke(string $resourceClass = null, string $rootClass = null,
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
$wrapFieldName = lcfirst($resourceMetadata->getShortName());
- $normalizationContext = $resourceMetadata->getGraphqlAttribute($operationName ?? '', 'normalization_context', [], true);
- $normalizationContext['attributes'] = $this->fieldsToAttributes($info)[$wrapFieldName] ?? [];
+ $baseNormalizationContext = $resourceMetadata->getGraphqlAttribute($operationName ?? '', 'normalization_context', [], true);
+ $baseNormalizationContext['attributes'] = $this->fieldsToAttributes($info)[$wrapFieldName] ?? [];
+ $normalizationContext = $baseNormalizationContext;
+ $normalizationContext['resource_class'] = $resourceClass;
if (isset($args['input']['id'])) {
try {
- $item = $this->iriConverter->getItemFromIri($args['input']['id'], $normalizationContext);
+ $item = $this->iriConverter->getItemFromIri($args['input']['id'], $baseNormalizationContext);
} catch (ItemNotFoundException $e) {
throw Error::createLocatedError(sprintf('Item "%s" not found.', $args['input']['id']), $info->fieldNodes, $info->path);
}
diff --git a/src/GraphQl/Resolver/ItemResolver.php b/src/GraphQl/Resolver/ItemResolver.php
index a06dbb05192..94935b61b67 100644
--- a/src/GraphQl/Resolver/ItemResolver.php
+++ b/src/GraphQl/Resolver/ItemResolver.php
@@ -72,6 +72,7 @@ public function __invoke($source, $args, $context, ResolveInfo $info)
$this->canAccess($this->resourceAccessChecker, $resourceMetadata, $resourceClass, $info, $item, 'query');
$normalizationContext = $resourceMetadata->getGraphqlAttribute('query', 'normalization_context', [], true);
+ $normalizationContext['resource_class'] = $resourceClass;
return $this->normalizer->normalize($item, ItemNormalizer::FORMAT, $normalizationContext + $baseNormalizationContext);
}
diff --git a/src/Hal/Serializer/ItemNormalizer.php b/src/Hal/Serializer/ItemNormalizer.php
index 32094c77b95..3ade32c3b2e 100644
--- a/src/Hal/Serializer/ItemNormalizer.php
+++ b/src/Hal/Serializer/ItemNormalizer.php
@@ -56,8 +56,7 @@ public function normalize($object, $format = null, array $context = [])
$context['cache_key'] = $this->getHalCacheKey($format, $context);
}
- // Use resolved resource class instead of given resource class to support multiple inheritance child types
- $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, true);
+ $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, isset($context['resource_class']));
$context = $this->initContext($resourceClass, $context);
$iri = $this->iriConverter->getIriFromItem($object);
$context['iri'] = $iri;
diff --git a/src/Hydra/Serializer/CollectionFiltersNormalizer.php b/src/Hydra/Serializer/CollectionFiltersNormalizer.php
index ff5d750484d..9f23e0a15d5 100644
--- a/src/Hydra/Serializer/CollectionFiltersNormalizer.php
+++ b/src/Hydra/Serializer/CollectionFiltersNormalizer.php
@@ -17,7 +17,6 @@
use ApiPlatform\Core\Api\FilterInterface;
use ApiPlatform\Core\Api\FilterLocatorTrait;
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
-use ApiPlatform\Core\Exception\InvalidArgumentException;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use Psr\Container\ContainerInterface;
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
@@ -71,19 +70,12 @@ public function hasCacheableSupportsMethod(): bool
public function normalize($object, $format = null, array $context = [])
{
$data = $this->collectionNormalizer->normalize($object, $format, $context);
- if (isset($context['api_sub_level'])) {
+
+ if (!isset($context['resource_class']) || isset($context['api_sub_level'])) {
return $data;
}
- try {
- $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, true);
- } catch (InvalidArgumentException $e) {
- if (!isset($context['resource_class'])) {
- return $data;
- }
-
- throw $e;
- }
+ $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class']);
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
$operationName = $context['collection_operation_name'] ?? null;
diff --git a/src/Hydra/Serializer/CollectionNormalizer.php b/src/Hydra/Serializer/CollectionNormalizer.php
index 391fb3b73ed..c7ac9d13f6c 100644
--- a/src/Hydra/Serializer/CollectionNormalizer.php
+++ b/src/Hydra/Serializer/CollectionNormalizer.php
@@ -18,7 +18,6 @@
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\DataProvider\PaginatorInterface;
use ApiPlatform\Core\DataProvider\PartialPaginatorInterface;
-use ApiPlatform\Core\Exception\InvalidArgumentException;
use ApiPlatform\Core\JsonLd\ContextBuilderInterface;
use ApiPlatform\Core\JsonLd\Serializer\JsonLdContextTrait;
use ApiPlatform\Core\Serializer\ContextTrait;
@@ -67,21 +66,13 @@ public function supportsNormalization($data, $format = null)
*/
public function normalize($object, $format = null, array $context = [])
{
- if (isset($context['api_sub_level'])) {
+ if (!isset($context['resource_class']) || isset($context['api_sub_level'])) {
return $this->normalizeRawCollection($object, $format, $context);
}
- try {
- $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, true);
- } catch (InvalidArgumentException $e) {
- if (!isset($context['resource_class'])) {
- return $this->normalizeRawCollection($object, $format, $context);
- }
-
- throw $e;
- }
- $data = $this->addJsonLdContext($this->contextBuilder, $resourceClass, $context);
+ $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class']);
$context = $this->initContext($resourceClass, $context);
+ $data = $this->addJsonLdContext($this->contextBuilder, $resourceClass, $context);
if (isset($context['operation_type']) && OperationType::SUBRESOURCE === $context['operation_type']) {
$data['@id'] = $this->iriConverter->getSubresourceIriFromResourceClass($resourceClass, $context);
diff --git a/src/JsonApi/Serializer/ItemNormalizer.php b/src/JsonApi/Serializer/ItemNormalizer.php
index 58bd954a0bd..0c895e0f7a4 100644
--- a/src/JsonApi/Serializer/ItemNormalizer.php
+++ b/src/JsonApi/Serializer/ItemNormalizer.php
@@ -30,7 +30,6 @@
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
-use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
@@ -75,8 +74,7 @@ public function normalize($object, $format = null, array $context = [])
$context['cache_key'] = $this->getJsonApiCacheKey($format, $context);
}
- // Use resolved resource class instead of given resource class to support multiple inheritance child types
- $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, true);
+ $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, isset($context['resource_class']));
$context = $this->initContext($resourceClass, $context);
$iri = $this->iriConverter->getIriFromItem($object);
$context['iri'] = $iri;
@@ -179,22 +177,11 @@ protected function setAttributeValue($object, $attribute, $value, $format = null
*
* @see http://jsonapi.org/format/#document-resource-object-linkage
*
- * @throws LogicException
* @throws RuntimeException
* @throws NotNormalizableValueException
*/
protected function denormalizeRelation(string $attributeName, PropertyMetadata $propertyMetadata, string $className, $value, ?string $format, array $context)
{
- // Give a chance to other normalizers (e.g.: DateTimeNormalizer)
- if (!$this->resourceClassResolver->isResourceClass($className)) {
- $context['resource_class'] = $className;
-
- if ($this->serializer instanceof DenormalizerInterface) {
- return $this->serializer->denormalize($value, $className, $format, $context);
- }
- throw new LogicException(sprintf('The injected serializer must be an instance of "%s".', DenormalizerInterface::class));
- }
-
if (!\is_array($value) || !isset($value['id'], $value['type'])) {
throw new NotNormalizableValueException('Only resource linkage supported currently, see: http://jsonapi.org/format/#document-resource-object-linkage.');
}
@@ -219,8 +206,6 @@ protected function normalizeRelation(PropertyMetadata $propertyMetadata, $relate
if (isset($context['operation_type'], $context['subresource_resources'][$resourceClass]) && OperationType::SUBRESOURCE === $context['operation_type']) {
$iri = $this->iriConverter->getItemIriFromResourceClass($resourceClass, $context['subresource_resources'][$resourceClass]);
} else {
- unset($context['resource_class']);
-
if ($this->serializer instanceof NormalizerInterface) {
return $this->serializer->normalize($relatedObject, $format, $context);
}
diff --git a/src/JsonApi/Serializer/ObjectNormalizer.php b/src/JsonApi/Serializer/ObjectNormalizer.php
index 5066b8429fd..6b0c778a5c8 100644
--- a/src/JsonApi/Serializer/ObjectNormalizer.php
+++ b/src/JsonApi/Serializer/ObjectNormalizer.php
@@ -75,7 +75,7 @@ public function normalize($object, $format = null, array $context = [])
}
if (isset($originalResource)) {
- $resourceClass = $this->resourceClassResolver->getResourceClass($originalResource, $context['resource_class'] ?? null, true);
+ $resourceClass = $this->resourceClassResolver->getResourceClass($originalResource);
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
$resourceData = [
diff --git a/src/JsonLd/Serializer/ItemNormalizer.php b/src/JsonLd/Serializer/ItemNormalizer.php
index 211789ff286..f27aa11d9f9 100644
--- a/src/JsonLd/Serializer/ItemNormalizer.php
+++ b/src/JsonLd/Serializer/ItemNormalizer.php
@@ -69,8 +69,7 @@ public function normalize($object, $format = null, array $context = [])
return parent::normalize($object, $format, $context);
}
- // Use resolved resource class instead of given resource class to support multiple inheritance child types
- $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, true);
+ $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, isset($context['resource_class']));
$context = $this->initContext($resourceClass, $context);
$iri = $this->iriConverter->getIriFromItem($object);
$context['iri'] = $iri;
diff --git a/src/Serializer/AbstractCollectionNormalizer.php b/src/Serializer/AbstractCollectionNormalizer.php
index bc9711a0119..59268475747 100644
--- a/src/Serializer/AbstractCollectionNormalizer.php
+++ b/src/Serializer/AbstractCollectionNormalizer.php
@@ -16,7 +16,6 @@
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\DataProvider\PaginatorInterface;
use ApiPlatform\Core\DataProvider\PartialPaginatorInterface;
-use ApiPlatform\Core\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
@@ -71,21 +70,13 @@ public function hasCacheableSupportsMethod(): bool
*/
public function normalize($object, $format = null, array $context = [])
{
- if (isset($context['api_sub_level'])) {
+ if (!isset($context['resource_class']) || isset($context['api_sub_level'])) {
return $this->normalizeRawCollection($object, $format, $context);
}
- try {
- $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, true);
- } catch (InvalidArgumentException $e) {
- if (!isset($context['resource_class'])) {
- return $this->normalizeRawCollection($object, $format, $context);
- }
-
- throw $e;
- }
- $data = [];
+ $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class']);
$context = $this->initContext($resourceClass, $context);
+ $data = [];
return array_merge_recursive(
$data,
diff --git a/src/Serializer/AbstractItemNormalizer.php b/src/Serializer/AbstractItemNormalizer.php
index 3fde229d47d..8aec8385724 100644
--- a/src/Serializer/AbstractItemNormalizer.php
+++ b/src/Serializer/AbstractItemNormalizer.php
@@ -116,18 +116,18 @@ public function normalize($object, $format = null, array $context = [])
{
if ($object !== $transformed = $this->transformOutput($object, $context)) {
if (!$this->serializer instanceof NormalizerInterface) {
- throw new LogicException('Cannot normalize the transformed value because the injected serializer is not a normalizer');
+ throw new LogicException('Cannot normalize the output because the injected serializer is not a normalizer');
}
$context['api_normalize'] = true;
$context['api_resource'] = $object;
unset($context['output']);
+ unset($context['resource_class']);
return $this->serializer->normalize($transformed, $format, $context);
}
- // Use resolved resource class instead of given resource class to support multiple inheritance child types
- $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, true);
+ $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, isset($context['resource_class']));
$context = $this->initContext($resourceClass, $context);
$iri = $context['iri'] ?? $this->iriConverter->getIriFromItem($object);
$context['iri'] = $iri;
@@ -171,19 +171,54 @@ public function supportsDenormalization($data, $type, $format = null, array $con
*/
public function denormalize($data, $class, $format = null, array $context = [])
{
+ $resourceClass = $this->resourceClassResolver->getResourceClass(null, $class);
$context['api_denormalize'] = true;
- $context['resource_class'] = $class;
- $inputClass = $this->getInputClass($class, $context);
+ $context['resource_class'] = $resourceClass;
- if (null !== $inputClass && null !== $dataTransformer = $this->getDataTransformer($data, $class, $context)) {
- return $dataTransformer->transform(
- parent::denormalize($data, $inputClass, $format, ['resource_class' => $inputClass] + $context),
- $class,
- $context
- );
+ if (null !== ($inputClass = $this->getInputClass($resourceClass, $context)) && null !== ($dataTransformer = $this->getDataTransformer($data, $resourceClass, $context))) {
+ $dataTransformerContext = $context;
+
+ unset($context['input']);
+ unset($context['resource_class']);
+
+ if (!$this->serializer instanceof DenormalizerInterface) {
+ throw new LogicException('Cannot denormalize the input because the injected serializer is not a denormalizer');
+ }
+ $denormalizedInput = $this->serializer->denormalize($data, $inputClass, $format, $context);
+
+ return $dataTransformer->transform($denormalizedInput, $resourceClass, $dataTransformerContext);
+ }
+
+ $supportsPlainIdentifiers = $this->supportsPlainIdentifiers();
+
+ if (\is_string($data)) {
+ try {
+ return $this->iriConverter->getItemFromIri($data, $context + ['fetch_data' => true]);
+ } catch (ItemNotFoundException $e) {
+ if (!$supportsPlainIdentifiers) {
+ throw new UnexpectedValueException($e->getMessage(), $e->getCode(), $e);
+ }
+ } catch (InvalidArgumentException $e) {
+ if (!$supportsPlainIdentifiers) {
+ throw new UnexpectedValueException(sprintf('Invalid IRI "%s".', $data), $e->getCode(), $e);
+ }
+ }
+ }
+
+ if (!\is_array($data)) {
+ if (!$supportsPlainIdentifiers) {
+ throw new UnexpectedValueException(sprintf('Expected IRI or document for resource "%s", "%s" given.', $resourceClass, \gettype($data)));
+ }
+
+ $item = $this->itemDataProvider->getItem($resourceClass, $data, null, $context + ['fetch_data' => true]);
+ if (null === $item) {
+ throw new ItemNotFoundException(sprintf('Item not found for resource "%s" with id "%s".', $resourceClass, $data));
+ }
+
+ return $item;
}
- return parent::denormalize($data, $class, $format, $context);
+ return parent::denormalize($data, $resourceClass, $format, $context);
}
/**
@@ -317,45 +352,6 @@ protected function setAttributeValue($object, $attribute, $value, $format = null
$this->setValue($object, $attribute, $this->createAttributeValue($attribute, $value, $format, $context));
}
- private function createAttributeValue($attribute, $value, $format = null, array $context = [])
- {
- if (!\is_string($attribute)) {
- throw new InvalidValueException('Invalid value provided (invalid IRI?).');
- }
-
- $propertyMetadata = $this->propertyMetadataFactory->create($context['resource_class'], $attribute, $this->getFactoryOptions($context));
- $type = $propertyMetadata->getType();
-
- if (null === $type) {
- // No type provided, blindly return the value
- return $value;
- }
-
- if (null === $value && $type->isNullable()) {
- return $value;
- }
-
- if (
- $type->isCollection() &&
- null !== ($collectionValueType = $type->getCollectionValueType()) &&
- null !== $className = $collectionValueType->getClassName()
- ) {
- return $this->denormalizeCollection($attribute, $propertyMetadata, $type, $className, $value, $format, $context);
- }
-
- if (null !== $className = $type->getClassName()) {
- return $this->denormalizeRelation($attribute, $propertyMetadata, $className, $value, $format, $this->createChildContext($context, $attribute));
- }
-
- if ($context[static::DISABLE_TYPE_ENFORCEMENT] ?? false) {
- return $value;
- }
-
- $this->validateType($attribute, $type, $value, $format);
-
- return $value;
- }
-
/**
* Validates the type of the value. Allows using integers as floats for JSON formats.
*
@@ -413,74 +409,62 @@ protected function denormalizeCollection(string $attribute, PropertyMetadata $pr
*
* @throws LogicException
* @throws UnexpectedValueException
+ * @throws ItemNotFoundException
*
* @return object|null
*/
protected function denormalizeRelation(string $attributeName, PropertyMetadata $propertyMetadata, string $className, $value, ?string $format, array $context)
{
+ $supportsPlainIdentifiers = $this->supportsPlainIdentifiers();
+
if (\is_string($value)) {
try {
return $this->iriConverter->getItemFromIri($value, $context + ['fetch_data' => true]);
} catch (ItemNotFoundException $e) {
- throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
+ if (!$supportsPlainIdentifiers) {
+ throw new UnexpectedValueException($e->getMessage(), $e->getCode(), $e);
+ }
} catch (InvalidArgumentException $e) {
- // Give a chance to other normalizers (e.g.: DateTimeNormalizer)
+ if (!$supportsPlainIdentifiers) {
+ throw new UnexpectedValueException(sprintf('Invalid IRI "%s".', $value), $e->getCode(), $e);
+ }
}
}
- if (
- !$this->resourceClassResolver->isResourceClass($className) ||
- $propertyMetadata->isWritableLink()
- ) {
- $context['resource_class'] = $className;
+ if ($propertyMetadata->isWritableLink()) {
$context['api_allow_update'] = true;
- try {
- if (!$this->serializer instanceof DenormalizerInterface) {
- throw new LogicException(sprintf('The injected serializer must be an instance of "%s".', DenormalizerInterface::class));
- }
+ if (!$this->serializer instanceof DenormalizerInterface) {
+ throw new LogicException(sprintf('The injected serializer must be an instance of "%s".', DenormalizerInterface::class));
+ }
+ try {
return $this->serializer->denormalize($value, $className, $format, $context);
} catch (InvalidValueException $e) {
- if (!$this->allowPlainIdentifiers || null === $this->itemDataProvider) {
+ if (!$supportsPlainIdentifiers) {
throw $e;
}
}
}
if (!\is_array($value)) {
- // repeat the code so that IRIs keep working with the json format
- if (true === $this->allowPlainIdentifiers && $this->itemDataProvider) {
- $item = $this->itemDataProvider->getItem($className, $value, null, $context + ['fetch_data' => true]);
- if (null === $item) {
- throw new ItemNotFoundException(sprintf('Item not found for "%s".', $value));
- }
+ if (!$supportsPlainIdentifiers) {
+ throw new UnexpectedValueException(sprintf(
+ 'Expected IRI or nested document for attribute "%s", "%s" given.', $attributeName, \gettype($value)
+ ));
+ }
- return $item;
+ $item = $this->itemDataProvider->getItem($className, $value, null, $context + ['fetch_data' => true]);
+ if (null === $item) {
+ throw new ItemNotFoundException(sprintf('Item not found for resource "%s" with id "%s".', $className, $value));
}
- throw new UnexpectedValueException(sprintf(
- 'Expected IRI or nested document for attribute "%s", "%s" given.', $attributeName, \gettype($value)
- ));
+ return $item;
}
throw new UnexpectedValueException(sprintf('Nested documents for attribute "%s" are not allowed. Use IRIs instead.', $attributeName));
}
- /**
- * Sets a value of the object using the PropertyAccess component.
- *
- * @param object $object
- */
- private function setValue($object, string $attributeName, $value)
- {
- try {
- $this->propertyAccessor->setValue($object, $attributeName, $value);
- } catch (NoSuchPropertyException $exception) {
- // Properties not found are ignored
- }
- }
-
/**
* Gets a valid context for property metadata factories.
*
@@ -548,7 +532,11 @@ protected function getAttributeValue($object, $attribute, $format = null, array
($className = $collectionValueType->getClassName()) &&
$this->resourceClassResolver->isResourceClass($className)
) {
- return $this->normalizeCollectionOfRelations($propertyMetadata, $attributeValue, $className, $format, $this->createChildContext($context, $attribute));
+ $resourceClass = $this->resourceClassResolver->getResourceClass($attributeValue, $className);
+ $childContext = $this->createChildContext($context, $attribute);
+ $childContext['resource_class'] = $resourceClass;
+
+ return $this->normalizeCollectionOfRelations($propertyMetadata, $attributeValue, $resourceClass, $format, $childContext);
}
if (
@@ -556,15 +544,19 @@ protected function getAttributeValue($object, $attribute, $format = null, array
($className = $type->getClassName()) &&
$this->resourceClassResolver->isResourceClass($className)
) {
- return $this->normalizeRelation($propertyMetadata, $attributeValue, $className, $format, $this->createChildContext($context, $attribute));
- }
+ $resourceClass = $this->resourceClassResolver->getResourceClass($attributeValue, $className, true);
+ $childContext = $this->createChildContext($context, $attribute);
+ $childContext['resource_class'] = $resourceClass;
- unset($context['resource_class']);
+ return $this->normalizeRelation($propertyMetadata, $attributeValue, $resourceClass, $format, $childContext);
+ }
if (!$this->serializer instanceof NormalizerInterface) {
throw new LogicException(sprintf('The injected serializer must be an instance of "%s".', NormalizerInterface::class));
}
+ unset($context['resource_class']);
+
return $this->serializer->normalize($attributeValue, $format, $context);
}
@@ -593,12 +585,6 @@ protected function normalizeCollectionOfRelations(PropertyMetadata $propertyMeta
protected function normalizeRelation(PropertyMetadata $propertyMetadata, $relatedObject, string $resourceClass, ?string $format, array $context)
{
if (null === $relatedObject || !empty($context['attributes']) || $propertyMetadata->isReadableLink()) {
- if (null === $relatedObject) {
- unset($context['resource_class']);
- } else {
- $context['resource_class'] = $resourceClass;
- }
-
if (!$this->serializer instanceof NormalizerInterface) {
throw new LogicException(sprintf('The injected serializer must be an instance of "%s".', NormalizerInterface::class));
}
@@ -644,4 +630,93 @@ protected function transformOutput($object, array $context = [])
return $object;
}
+
+ private function createAttributeValue($attribute, $value, $format = null, array $context = [])
+ {
+ $propertyMetadata = $this->propertyMetadataFactory->create($context['resource_class'], $attribute, $this->getFactoryOptions($context));
+ $type = $propertyMetadata->getType();
+
+ if (null === $type) {
+ // No type provided, blindly return the value
+ return $value;
+ }
+
+ if (null === $value && $type->isNullable()) {
+ return $value;
+ }
+
+ if (
+ $type->isCollection() &&
+ null !== ($collectionValueType = $type->getCollectionValueType()) &&
+ null !== ($className = $collectionValueType->getClassName()) &&
+ $this->resourceClassResolver->isResourceClass($className)
+ ) {
+ $resourceClass = $this->resourceClassResolver->getResourceClass(null, $className);
+ $context['resource_class'] = $resourceClass;
+
+ return $this->denormalizeCollection($attribute, $propertyMetadata, $type, $resourceClass, $value, $format, $context);
+ }
+
+ if (
+ null !== ($className = $type->getClassName()) &&
+ $this->resourceClassResolver->isResourceClass($className)
+ ) {
+ $resourceClass = $this->resourceClassResolver->getResourceClass(null, $className);
+ $childContext = $this->createChildContext($context, $attribute);
+ $childContext['resource_class'] = $resourceClass;
+
+ return $this->denormalizeRelation($attribute, $propertyMetadata, $resourceClass, $value, $format, $childContext);
+ }
+
+ if (
+ $type->isCollection() &&
+ null !== ($collectionValueType = $type->getCollectionValueType()) &&
+ null !== ($className = $collectionValueType->getClassName())
+ ) {
+ if (!$this->serializer instanceof DenormalizerInterface) {
+ throw new LogicException(sprintf('The injected serializer must be an instance of "%s".', DenormalizerInterface::class));
+ }
+
+ unset($context['resource_class']);
+
+ return $this->serializer->denormalize($value, $className, $format, $context);
+ }
+
+ if (null !== $className = $type->getClassName()) {
+ if (!$this->serializer instanceof DenormalizerInterface) {
+ throw new LogicException(sprintf('The injected serializer must be an instance of "%s".', DenormalizerInterface::class));
+ }
+
+ unset($context['resource_class']);
+
+ return $this->serializer->denormalize($value, $className, $format, $context);
+ }
+
+ if ($context[static::DISABLE_TYPE_ENFORCEMENT] ?? false) {
+ return $value;
+ }
+
+ $this->validateType($attribute, $type, $value, $format);
+
+ return $value;
+ }
+
+ /**
+ * Sets a value of the object using the PropertyAccess component.
+ *
+ * @param object $object
+ */
+ private function setValue($object, string $attributeName, $value)
+ {
+ try {
+ $this->propertyAccessor->setValue($object, $attributeName, $value);
+ } catch (NoSuchPropertyException $exception) {
+ // Properties not found are ignored
+ }
+ }
+
+ private function supportsPlainIdentifiers(): bool
+ {
+ return $this->allowPlainIdentifiers && null !== $this->itemDataProvider;
+ }
}
diff --git a/src/Util/ResourceClassInfoTrait.php b/src/Util/ResourceClassInfoTrait.php
new file mode 100644
index 00000000000..9d64bf561ef
--- /dev/null
+++ b/src/Util/ResourceClassInfoTrait.php
@@ -0,0 +1,54 @@
+
+ *
+ * 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\Core\Util;
+
+use ApiPlatform\Core\Api\ResourceClassResolverInterface;
+
+/**
+ * Retrieves information about a resource class.
+ *
+ * @internal
+ */
+trait ResourceClassInfoTrait
+{
+ use ClassInfoTrait;
+
+ /**
+ * @var ResourceClassResolverInterface|null
+ */
+ private $resourceClassResolver;
+
+ /**
+ * Gets the resource class of the given object.
+ *
+ * @param object $object
+ * @param bool $strict If true, object class is expected to be a resource class
+ *
+ * @return string|null The resource class, or null if object class is not a resource class
+ */
+ private function getResourceClass($object, bool $strict = false): ?string
+ {
+ $objectClass = $this->getObjectClass($object);
+
+ if (null === $this->resourceClassResolver) {
+ return $objectClass;
+ }
+
+ if (!$strict && !$this->resourceClassResolver->isResourceClass($objectClass)) {
+ return null;
+ }
+
+ return $this->resourceClassResolver->getResourceClass($object);
+ }
+}
diff --git a/tests/Api/CachedIdentifiersExtractorTest.php b/tests/Api/CachedIdentifiersExtractorTest.php
index a1c2bff1196..c2f55e16659 100644
--- a/tests/Api/CachedIdentifiersExtractorTest.php
+++ b/tests/Api/CachedIdentifiersExtractorTest.php
@@ -45,29 +45,35 @@ public function itemProvider()
*/
public function testFirstPass($item, $expected)
{
- $key = 'iri_identifiers'.md5(Dummy::class);
+ $cacheItemKey = 'iri_identifiers'.md5(Dummy::class);
- $cacheItem = $this->prophesize(CacheItemInterface::class);
- $cacheItem->isHit()->shouldBeCalled()->willReturn(false);
- $cacheItem->set(['id'])->shouldBeCalled();
+ $cacheItemProphecy = $this->prophesize(CacheItemInterface::class);
+ $cacheItemProphecy->isHit()->willReturn(false);
+ $cacheItemProphecy->set(['id'])->shouldBeCalled();
- $cacheItemPool = $this->prophesize(CacheItemPoolInterface::class);
- $cacheItemPool->getItem($key)->shouldBeCalled()->willReturn($cacheItem);
- $cacheItemPool->save($cacheItem)->shouldBeCalled();
+ $cacheItemPoolProphecy = $this->prophesize(CacheItemPoolInterface::class);
+ $cacheItemPoolProphecy->getItem($cacheItemKey)->willReturn($cacheItemProphecy);
+ $cacheItemPoolProphecy->save($cacheItemProphecy)->shouldBeCalled();
- $decoration = $this->prophesize(IdentifiersExtractorInterface::class);
- $decoration->getIdentifiersFromItem($item)->shouldBeCalled()->willReturn($expected);
+ $decoratedProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+ $decoratedProphecy->getIdentifiersFromItem($item)->willReturn($expected);
+
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass($item)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
+ $resourceClassResolverProphecy->isResourceClass(Uuid::class)->willReturn(false);
- $identifiersExtractor = new CachedIdentifiersExtractor($cacheItemPool->reveal(), $decoration->reveal(), null, $this->getResourceClassResolver());
+ $identifiersExtractor = new CachedIdentifiersExtractor($cacheItemPoolProphecy->reveal(), $decoratedProphecy->reveal(), null, $resourceClassResolverProphecy->reveal());
$this->assertSame($expected, $identifiersExtractor->getIdentifiersFromItem($item));
$this->assertSame($expected, $identifiersExtractor->getIdentifiersFromItem($item), 'Trigger the local cache');
- $decoration->getIdentifiersFromResourceClass(Dummy::class)->shouldBeCalled()->willReturn(['id']);
+ $expected = ['id'];
- $expectedResult = ['id'];
- $this->assertSame($expectedResult, $identifiersExtractor->getIdentifiersFromResourceClass(Dummy::class));
- $this->assertSame($expectedResult, $identifiersExtractor->getIdentifiersFromResourceClass(Dummy::class), 'Trigger the local cache');
+ $decoratedProphecy->getIdentifiersFromResourceClass(Dummy::class)->willReturn($expected);
+
+ $this->assertSame($expected, $identifiersExtractor->getIdentifiersFromResourceClass(Dummy::class));
+ $this->assertSame($expected, $identifiersExtractor->getIdentifiersFromResourceClass(Dummy::class), 'Trigger the local cache');
}
/**
@@ -75,22 +81,34 @@ public function testFirstPass($item, $expected)
*/
public function testSecondPass($item, $expected)
{
- $key = 'iri_identifiers'.md5(Dummy::class);
+ $cacheItemKey = 'iri_identifiers'.md5(Dummy::class);
- $cacheItem = $this->prophesize(CacheItemInterface::class);
- $cacheItem->isHit()->shouldBeCalled()->willReturn(true);
- $cacheItem->get()->shouldBeCalled()->willReturn(['id']);
+ $cacheItemProphecy = $this->prophesize(CacheItemInterface::class);
+ $cacheItemProphecy->isHit()->willReturn(true);
+ $cacheItemProphecy->get()->willReturn(['id']);
- $cacheItemPool = $this->prophesize(CacheItemPoolInterface::class);
- $cacheItemPool->getItem($key)->shouldBeCalled()->willReturn($cacheItem);
+ $cacheItemPoolProphecy = $this->prophesize(CacheItemPoolInterface::class);
+ $cacheItemPoolProphecy->getItem($cacheItemKey)->willReturn($cacheItemProphecy);
- $decoration = $this->prophesize(IdentifiersExtractorInterface::class);
- $decoration->getIdentifiersFromItem($item)->shouldNotBeCalled();
+ $decoratedProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+ $decoratedProphecy->getIdentifiersFromItem($item)->shouldNotBeCalled();
+
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass($item)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
+ $resourceClassResolverProphecy->isResourceClass(Uuid::class)->willReturn(false);
- $identifiersExtractor = new CachedIdentifiersExtractor($cacheItemPool->reveal(), $decoration->reveal(), null, $this->getResourceClassResolver());
+ $identifiersExtractor = new CachedIdentifiersExtractor($cacheItemPoolProphecy->reveal(), $decoratedProphecy->reveal(), null, $resourceClassResolverProphecy->reveal());
$this->assertSame($expected, $identifiersExtractor->getIdentifiersFromItem($item));
$this->assertSame($expected, $identifiersExtractor->getIdentifiersFromItem($item), 'Trigger the local cache');
+
+ $expected = ['id'];
+
+ $decoratedProphecy->getIdentifiersFromResourceClass(Dummy::class)->willReturn($expected);
+
+ $this->assertSame($expected, $identifiersExtractor->getIdentifiersFromResourceClass(Dummy::class));
+ $this->assertSame($expected, $identifiersExtractor->getIdentifiersFromResourceClass(Dummy::class), 'Trigger the local cache');
}
public function identifiersRelatedProvider()
@@ -128,24 +146,31 @@ public function identifiersRelatedProvider()
*/
public function testFirstPassWithRelated($item, $expected)
{
- $key = 'iri_identifiers'.md5(Dummy::class);
- $keyRelated = 'iri_identifiers'.md5(RelatedDummy::class);
+ $cacheItemKey = 'iri_identifiers'.md5(Dummy::class);
+ $relatedCacheItemKey = 'iri_identifiers'.md5(RelatedDummy::class);
- $cacheItem = $this->prophesize(CacheItemInterface::class);
- $cacheItem->isHit()->shouldBeCalled()->willReturn(true);
- $cacheItem->get()->shouldBeCalled()->willReturn(['id', 'relatedDummy']);
+ $cacheItemProphecy = $this->prophesize(CacheItemInterface::class);
+ $cacheItemProphecy->isHit()->willReturn(true);
+ $cacheItemProphecy->get()->willReturn(['id', 'relatedDummy']);
- $cacheItemRelated = $this->prophesize(CacheItemInterface::class);
- $cacheItemRelated->isHit()->shouldBeCalled()->willReturn(false);
+ $relatedCacheItemProphecy = $this->prophesize(CacheItemInterface::class);
+ $relatedCacheItemProphecy->isHit()->willReturn(false);
- $cacheItemPool = $this->prophesize(CacheItemPoolInterface::class);
- $cacheItemPool->getItem($key)->shouldBeCalled()->willReturn($cacheItem);
- $cacheItemPool->getItem($keyRelated)->shouldBeCalled()->willReturn($cacheItemRelated);
+ $cacheItemPoolProphecy = $this->prophesize(CacheItemPoolInterface::class);
+ $cacheItemPoolProphecy->getItem($cacheItemKey)->willReturn($cacheItemProphecy);
+ $cacheItemPoolProphecy->getItem($relatedCacheItemKey)->willReturn($relatedCacheItemProphecy);
- $decoration = $this->prophesize(IdentifiersExtractorInterface::class);
- $decoration->getIdentifiersFromItem($item)->shouldBeCalled()->willReturn($expected);
+ $decoratedProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+ $decoratedProphecy->getIdentifiersFromItem($item)->willReturn($expected);
+
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass($item)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(Argument::type(RelatedDummy::class))->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
+ $resourceClassResolverProphecy->isResourceClass(Uuid::class)->willReturn(false);
- $identifiersExtractor = new CachedIdentifiersExtractor($cacheItemPool->reveal(), $decoration->reveal(), null, $this->getResourceClassResolver());
+ $identifiersExtractor = new CachedIdentifiersExtractor($cacheItemPoolProphecy->reveal(), $decoratedProphecy->reveal(), null, $resourceClassResolverProphecy->reveal());
$this->assertSame($expected, $identifiersExtractor->getIdentifiersFromItem($item));
$this->assertSame($expected, $identifiersExtractor->getIdentifiersFromItem($item), 'Trigger the local cache');
@@ -156,25 +181,32 @@ public function testFirstPassWithRelated($item, $expected)
*/
public function testSecondPassWithRelated($item, $expected)
{
- $key = 'iri_identifiers'.md5(Dummy::class);
- $keyRelated = 'iri_identifiers'.md5(RelatedDummy::class);
+ $cacheItemKey = 'iri_identifiers'.md5(Dummy::class);
+ $relatedCacheItemKey = 'iri_identifiers'.md5(RelatedDummy::class);
- $cacheItem = $this->prophesize(CacheItemInterface::class);
- $cacheItem->isHit()->shouldBeCalled()->willReturn(true);
- $cacheItem->get()->shouldBeCalled()->willReturn(['id', 'relatedDummy']);
+ $cacheItemProphecy = $this->prophesize(CacheItemInterface::class);
+ $cacheItemProphecy->isHit()->willReturn(true);
+ $cacheItemProphecy->get()->willReturn(['id', 'relatedDummy']);
- $cacheItemRelated = $this->prophesize(CacheItemInterface::class);
- $cacheItemRelated->isHit()->shouldBeCalled()->willReturn(true);
- $cacheItemRelated->get()->shouldBeCalled()->willReturn(['id']);
+ $relatedCacheItemProphecy = $this->prophesize(CacheItemInterface::class);
+ $relatedCacheItemProphecy->isHit()->willReturn(true);
+ $relatedCacheItemProphecy->get()->willReturn(['id']);
- $cacheItemPool = $this->prophesize(CacheItemPoolInterface::class);
- $cacheItemPool->getItem($key)->shouldBeCalled()->willReturn($cacheItem);
- $cacheItemPool->getItem($keyRelated)->shouldBeCalled()->willReturn($cacheItemRelated);
+ $cacheItemPoolProphecy = $this->prophesize(CacheItemPoolInterface::class);
+ $cacheItemPoolProphecy->getItem($cacheItemKey)->willReturn($cacheItemProphecy);
+ $cacheItemPoolProphecy->getItem($relatedCacheItemKey)->willReturn($relatedCacheItemProphecy);
- $decoration = $this->prophesize(IdentifiersExtractorInterface::class);
- $decoration->getIdentifiersFromItem($item)->shouldNotBeCalled();
+ $decoratedProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+ $decoratedProphecy->getIdentifiersFromItem($item)->shouldNotBeCalled();
- $identifiersExtractor = new CachedIdentifiersExtractor($cacheItemPool->reveal(), $decoration->reveal(), null, $this->getResourceClassResolver());
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass($item)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(Argument::type(RelatedDummy::class))->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
+ $resourceClassResolverProphecy->isResourceClass(Uuid::class)->willReturn(false);
+
+ $identifiersExtractor = new CachedIdentifiersExtractor($cacheItemPoolProphecy->reveal(), $decoratedProphecy->reveal(), null, $resourceClassResolverProphecy->reveal());
$this->assertSame($expected, $identifiersExtractor->getIdentifiersFromItem($item));
$this->assertSame($expected, $identifiersExtractor->getIdentifiersFromItem($item), 'Trigger the local cache');
@@ -191,14 +223,4 @@ public function testDeprecationResourceClassResolver()
new CachedIdentifiersExtractor($cacheItemPool->reveal(), $decoration->reveal(), null);
}
-
- private function getResourceClassResolver()
- {
- $resourceClassResolver = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolver->isResourceClass(Argument::type('string'))->will(function ($args) {
- return !(Uuid::class === $args[0]);
- });
-
- return $resourceClassResolver->reveal();
- }
}
diff --git a/tests/Api/IdentifiersExtractorTest.php b/tests/Api/IdentifiersExtractorTest.php
index d83eee6d6b8..3983e8f6c61 100644
--- a/tests/Api/IdentifiersExtractorTest.php
+++ b/tests/Api/IdentifiersExtractorTest.php
@@ -23,6 +23,8 @@
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Doctrine\Generator\Uuid;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\RelatedDummy;
+use ApiPlatform\Core\Tests\Fixtures\TestBundle\OtherResources\ResourceInterface;
+use ApiPlatform\Core\Tests\Fixtures\TestBundle\OtherResources\ResourceInterfaceImplementation;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
@@ -31,32 +33,13 @@
*/
class IdentifiersExtractorTest extends TestCase
{
- private function getMetadataFactoryProphecies($class, $identifiers, array $prophecies = null)
- {
- //adds a random property that is not an identifier
- $properties = array_merge(['foo'], $identifiers);
-
- if (!$prophecies) {
- $prophecies = [$this->prophesize(PropertyNameCollectionFactoryInterface::class), $this->prophesize(PropertyMetadataFactoryInterface::class)];
- }
-
- [$propertyNameCollectionFactoryProphecy, $propertyMetadataFactoryProphecy] = $prophecies;
-
- $propertyNameCollectionFactoryProphecy->create($class)->shouldBeCalled()->willReturn(new PropertyNameCollection($properties));
-
- foreach ($properties as $prop) {
- $metadata = new PropertyMetadata();
- $propertyMetadataFactoryProphecy->create($class, $prop)->shouldBeCalled()->willReturn($metadata->withIdentifier(\in_array($prop, $identifiers, true)));
- }
-
- return [$propertyNameCollectionFactoryProphecy, $propertyMetadataFactoryProphecy];
- }
-
public function testGetIdentifiersFromResourceClass()
{
[$propertyNameCollectionFactoryProphecy, $propertyMetadataFactoryProphecy] = $this->getMetadataFactoryProphecies(Dummy::class, ['id']);
- $identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), null, $this->getResourceClassResolver());
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+
+ $identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), null, $resourceClassResolverProphecy->reveal());
$this->assertSame(['id'], $identifiersExtractor->getIdentifiersFromResourceClass(Dummy::class));
}
@@ -65,7 +48,9 @@ public function testGetCompositeIdentifiersFromResourceClass()
{
[$propertyNameCollectionFactoryProphecy, $propertyMetadataFactoryProphecy] = $this->getMetadataFactoryProphecies(Dummy::class, ['id', 'name']);
- $identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), null, $this->getResourceClassResolver());
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+
+ $identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), null, $resourceClassResolverProphecy->reveal());
$this->assertSame(['id', 'name'], $identifiersExtractor->getIdentifiersFromResourceClass(Dummy::class));
}
@@ -89,7 +74,11 @@ public function testGetIdentifiersFromItem($item, $expected)
{
[$propertyNameCollectionFactoryProphecy, $propertyMetadataFactoryProphecy] = $this->getMetadataFactoryProphecies(Dummy::class, ['id']);
- $identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), null, $this->getResourceClassResolver());
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass($item)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->isResourceClass(Uuid::class)->willReturn(false);
+
+ $identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), null, $resourceClassResolverProphecy->reveal());
$this->assertSame($expected, $identifiersExtractor->getIdentifiersFromItem($item));
}
@@ -114,7 +103,11 @@ public function testGetCompositeIdentifiersFromItem($item, $expected)
{
[$propertyNameCollectionFactoryProphecy, $propertyMetadataFactoryProphecy] = $this->getMetadataFactoryProphecies(Dummy::class, ['id', 'name']);
- $identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), null, $this->getResourceClassResolver());
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass($item)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->isResourceClass(Uuid::class)->willReturn(false);
+
+ $identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), null, $resourceClassResolverProphecy->reveal());
$this->assertSame($expected, $identifiersExtractor->getIdentifiersFromItem($item));
}
@@ -148,7 +141,13 @@ public function testGetRelatedIdentifiersFromItem($item, $expected)
$prophecies = $this->getMetadataFactoryProphecies(Dummy::class, ['id', 'relatedDummy']);
[$propertyNameCollectionFactoryProphecy, $propertyMetadataFactoryProphecy] = $this->getMetadataFactoryProphecies(RelatedDummy::class, ['id'], $prophecies);
- $identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), null, $this->getResourceClassResolver());
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass($item)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(Argument::type(RelatedDummy::class))->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
+ $resourceClassResolverProphecy->isResourceClass(Uuid::class)->willReturn(false);
+
+ $identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), null, $resourceClassResolverProphecy->reveal());
$this->assertSame($expected, $identifiersExtractor->getIdentifiersFromItem($item));
}
@@ -158,11 +157,6 @@ public function testThrowNoIdentifierFromItem()
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('No identifier found in "ApiPlatform\\Core\\Tests\\Fixtures\\TestBundle\\Entity\\RelatedDummy" through relation "relatedDummy" of "ApiPlatform\\Core\\Tests\\Fixtures\\TestBundle\\Entity\\Dummy" used as identifier');
- $prophecies = $this->getMetadataFactoryProphecies(Dummy::class, ['id', 'relatedDummy']);
- [$propertyNameCollectionFactoryProphecy, $propertyMetadataFactoryProphecy] = $this->getMetadataFactoryProphecies(RelatedDummy::class, [], $prophecies);
-
- $identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), null, $this->getResourceClassResolver());
-
$related = new RelatedDummy();
$related->setId(2);
@@ -170,17 +164,39 @@ public function testThrowNoIdentifierFromItem()
$dummy->setId(1);
$dummy->setRelatedDummy($related);
+ $prophecies = $this->getMetadataFactoryProphecies(Dummy::class, ['id', 'relatedDummy']);
+ [$propertyNameCollectionFactoryProphecy, $propertyMetadataFactoryProphecy] = $this->getMetadataFactoryProphecies(RelatedDummy::class, [], $prophecies);
+
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass($dummy)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(Argument::type(RelatedDummy::class))->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
+
+ $identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), null, $resourceClassResolverProphecy->reveal());
+
$identifiersExtractor->getIdentifiersFromItem($dummy);
}
- private function getResourceClassResolver()
+ public function testGetsIdentifiersFromCorrectResourceClass(): void
{
- $resourceClassResolver = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolver->isResourceClass(Argument::type('string'))->will(function ($args) {
- return !(Uuid::class === $args[0]);
- });
+ $item = new ResourceInterfaceImplementation();
+ $item->setFoo('woot');
+
+ $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
+ $propertyNameCollectionFactoryProphecy->create(ResourceInterface::class)->willReturn(new PropertyNameCollection(['foo', 'fooz']));
+
+ $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
+ $propertyMetadataFactoryProphecy->create(ResourceInterface::class, 'foo')->willReturn((new PropertyMetadata())->withIdentifier(true));
+ $propertyMetadataFactoryProphecy->create(ResourceInterface::class, 'fooz')->willReturn(new PropertyMetadata());
+
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass($item)->willReturn(ResourceInterface::class);
- return $resourceClassResolver->reveal();
+ $identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), null, $resourceClassResolverProphecy->reveal());
+
+ $identifiersExtractor->getIdentifiersFromItem($item);
+
+ $this->assertSame(['foo' => 'woot'], $identifiersExtractor->getIdentifiersFromItem($item));
}
/**
@@ -198,4 +214,25 @@ public function testLegacyGetIdentifiersFromItem()
$this->assertSame(['id' => 1], $identifiersExtractor->getIdentifiersFromItem($dummy));
}
+
+ private function getMetadataFactoryProphecies($class, $identifiers, array $prophecies = null)
+ {
+ //adds a random property that is not an identifier
+ $properties = array_merge(['foo'], $identifiers);
+
+ if (!$prophecies) {
+ $prophecies = [$this->prophesize(PropertyNameCollectionFactoryInterface::class), $this->prophesize(PropertyMetadataFactoryInterface::class)];
+ }
+
+ [$propertyNameCollectionFactoryProphecy, $propertyMetadataFactoryProphecy] = $prophecies;
+
+ $propertyNameCollectionFactoryProphecy->create($class)->willReturn(new PropertyNameCollection($properties));
+
+ foreach ($properties as $prop) {
+ $metadata = new PropertyMetadata();
+ $propertyMetadataFactoryProphecy->create($class, $prop)->willReturn($metadata->withIdentifier(\in_array($prop, $identifiers, true)));
+ }
+
+ return [$propertyNameCollectionFactoryProphecy, $propertyMetadataFactoryProphecy];
+ }
}
diff --git a/tests/Api/ResourceClassResolverTest.php b/tests/Api/ResourceClassResolverTest.php
index 0e8d6c5c492..0f9adde9c9f 100644
--- a/tests/Api/ResourceClassResolverTest.php
+++ b/tests/Api/ResourceClassResolverTest.php
@@ -33,67 +33,71 @@ class ResourceClassResolverTest extends TestCase
{
public function testGetResourceClassWithIntendedClassName()
{
+ $resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
+ $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([Dummy::class]));
+
$dummy = new Dummy();
$dummy->setName('Smail');
- $resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
- $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([Dummy::class]))->shouldBeCalled();
$resourceClassResolver = new ResourceClassResolver($resourceNameCollectionFactoryProphecy->reveal());
- $resourceClass = $resourceClassResolver->getResourceClass($dummy, Dummy::class);
- $this->assertEquals($resourceClass, Dummy::class);
+
+ $this->assertEquals(Dummy::class, $resourceClassResolver->getResourceClass($dummy, Dummy::class));
}
- public function testGetResourceClassWithOtherClassName()
+ public function testGetResourceClassWithNonResourceClassName()
{
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage('Specified class "ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyCar" is not a resource class.');
+
+ $resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
+ $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([Dummy::class]));
+
$dummy = new Dummy();
$dummy->setName('Smail');
- $resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
- $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([Dummy::class]))->shouldBeCalled();
$resourceClassResolver = new ResourceClassResolver($resourceNameCollectionFactoryProphecy->reveal());
- $resourceClass = $resourceClassResolver->getResourceClass($dummy, DummyCar::class, true);
- $this->assertEquals($resourceClass, Dummy::class);
+
+ $resourceClassResolver->getResourceClass($dummy, DummyCar::class, true);
}
public function testGetResourceClassWithNoClassName()
{
+ $resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
+ $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([Dummy::class]));
+
$dummy = new Dummy();
$dummy->setName('Smail');
- $resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
- $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([Dummy::class]))->shouldBeCalled();
$resourceClassResolver = new ResourceClassResolver($resourceNameCollectionFactoryProphecy->reveal());
- $resourceClass = $resourceClassResolver->getResourceClass($dummy);
- $this->assertEquals($resourceClass, Dummy::class);
+
+ $this->assertEquals(Dummy::class, $resourceClassResolver->getResourceClass($dummy));
}
public function testGetResourceClassWithTraversableAsValue()
{
+ $resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
+ $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([Dummy::class]));
+
$dummy = new Dummy();
$dummy->setName('JLM');
$dummies = new \ArrayIterator([$dummy]);
- $resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
- $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([Dummy::class]))->shouldBeCalled();
-
$resourceClassResolver = new ResourceClassResolver($resourceNameCollectionFactoryProphecy->reveal());
- $resourceClass = $resourceClassResolver->getResourceClass($dummies, Dummy::class);
- $this->assertEquals($resourceClass, Dummy::class);
+ $this->assertEquals(Dummy::class, $resourceClassResolver->getResourceClass($dummies, Dummy::class));
}
public function testGetResourceClassWithPaginatorInterfaceAsValue()
{
- $paginatorProphecy = $this->prophesize(PaginatorInterface::class);
-
$resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
$resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([Dummy::class]))->shouldBeCalled();
+ $paginatorProphecy = $this->prophesize(PaginatorInterface::class);
+
$resourceClassResolver = new ResourceClassResolver($resourceNameCollectionFactoryProphecy->reveal());
- $resourceClass = $resourceClassResolver->getResourceClass($paginatorProphecy->reveal(), Dummy::class);
- $this->assertEquals($resourceClass, Dummy::class);
+ $this->assertEquals(Dummy::class, $resourceClassResolver->getResourceClass($paginatorProphecy->reveal(), Dummy::class));
}
public function testGetResourceClassWithWrongClassName()
@@ -105,82 +109,85 @@ public function testGetResourceClassWithWrongClassName()
$resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([Dummy::class]))->shouldBeCalled();
$resourceClassResolver = new ResourceClassResolver($resourceNameCollectionFactoryProphecy->reveal());
+
$resourceClassResolver->getResourceClass(new \stdClass());
}
public function testGetResourceClassWithNoResourceClassName()
{
$this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessage('No resource class found.');
+ $this->expectExceptionMessage('Resource type could not be determined. Resource class must be specified.');
$resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
$resourceClassResolver = new ResourceClassResolver($resourceNameCollectionFactoryProphecy->reveal());
+
$resourceClassResolver->getResourceClass(new \ArrayIterator([]));
}
public function testIsResourceClassWithIntendedClassName()
{
- $dummy = new Dummy();
- $dummy->setName('Smail');
$resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
- $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([Dummy::class]))->shouldBeCalled();
+ $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([Dummy::class]));
$resourceClassResolver = new ResourceClassResolver($resourceNameCollectionFactoryProphecy->reveal());
- $resourceClass = $resourceClassResolver->isResourceClass(Dummy::class);
- $this->assertTrue($resourceClass);
+
+ $this->assertTrue($resourceClassResolver->isResourceClass(Dummy::class));
}
public function testIsResourceClassWithWrongClassName()
{
$resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
- $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([\ArrayIterator::class]))->shouldBeCalled();
+ $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([\ArrayIterator::class]));
$resourceClassResolver = new ResourceClassResolver($resourceNameCollectionFactoryProphecy->reveal());
- $resourceClass = $resourceClassResolver->isResourceClass('');
- $this->assertFalse($resourceClass);
+
+ $this->assertFalse($resourceClassResolver->isResourceClass(''));
}
public function testGetResourceClassWithNoResourceClassNameAndNoObject()
{
$this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessage('No resource class found.');
+ $this->expectExceptionMessage('Resource type could not be determined. Resource class must be specified.');
$resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
$resourceClassResolver = new ResourceClassResolver($resourceNameCollectionFactoryProphecy->reveal());
+
$resourceClassResolver->getResourceClass(false);
}
public function testGetResourceClassWithResourceClassNameAndNoObject()
{
$resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
- $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([Dummy::class]))->shouldBeCalled();
+ $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([Dummy::class]));
$resourceClassResolver = new ResourceClassResolver($resourceNameCollectionFactoryProphecy->reveal());
- $this->assertEquals($resourceClassResolver->getResourceClass(false, Dummy::class), Dummy::class);
+
+ $this->assertEquals(Dummy::class, $resourceClassResolver->getResourceClass(false, Dummy::class));
}
public function testGetResourceClassWithChildResource()
{
$resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
- $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([DummyTableInheritance::class]))->shouldBeCalled();
+ $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([DummyTableInheritance::class, DummyTableInheritanceChild::class]));
- $t = new DummyTableInheritanceChild();
+ $dummy = new DummyTableInheritanceChild();
$resourceClassResolver = new ResourceClassResolver($resourceNameCollectionFactoryProphecy->reveal());
- $this->assertEquals(DummyTableInheritanceChild::class, $resourceClassResolver->getResourceClass($t, DummyTableInheritance::class));
+ $this->assertEquals(DummyTableInheritanceChild::class, $resourceClassResolver->getResourceClass($dummy, DummyTableInheritance::class));
}
public function testGetResourceClassWithInterfaceResource()
{
- $this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessage("The given object's resource is the interface \"ApiPlatform\Core\Tests\Fixtures\DummyResourceInterface\", finding a class is not possible.");
- $dummy = new DummyResourceImplementation();
$resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
+ $resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([DummyResourceInterface::class]));
+
+ $dummy = new DummyResourceImplementation();
$resourceClassResolver = new ResourceClassResolver($resourceNameCollectionFactoryProphecy->reveal());
- $resourceClassResolver->getResourceClass($dummy, DummyResourceInterface::class, true);
+
+ $this->assertEquals(DummyResourceInterface::class, $resourceClassResolver->getResourceClass($dummy, DummyResourceInterface::class, true));
}
}
diff --git a/tests/Bridge/Doctrine/EventListener/PublishMercureUpdatesListenerTest.php b/tests/Bridge/Doctrine/EventListener/PublishMercureUpdatesListenerTest.php
index 632338a845e..a4d38a1a79a 100644
--- a/tests/Bridge/Doctrine/EventListener/PublishMercureUpdatesListenerTest.php
+++ b/tests/Bridge/Doctrine/EventListener/PublishMercureUpdatesListenerTest.php
@@ -28,6 +28,7 @@
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\UnitOfWork;
use PHPUnit\Framework\TestCase;
+use Prophecy\Argument;
use Symfony\Component\Mercure\Update;
use Symfony\Component\Serializer\SerializerInterface;
@@ -52,6 +53,9 @@ public function testPublishUpdate()
$toDeleteExpressionLanguage->setId(4);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(Argument::type(Dummy::class))->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(Argument::type(DummyCar::class))->willReturn(DummyCar::class);
+ $resourceClassResolverProphecy->getResourceClass(Argument::type(DummyFriend::class))->willReturn(DummyFriend::class);
$resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
$resourceClassResolverProphecy->isResourceClass(NotAResource::class)->willReturn(false);
$resourceClassResolverProphecy->isResourceClass(DummyCar::class)->willReturn(true);
@@ -135,6 +139,7 @@ public function testInvalidMercureAttribute()
$toInsert = new Dummy();
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(Argument::type(Dummy::class))->willReturn(Dummy::class);
$resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
diff --git a/tests/Fixtures/TestBundle/Document/DummyTableInheritance.php b/tests/Fixtures/TestBundle/Document/DummyTableInheritance.php
index 05b6b8f70c2..5ecbf5fe870 100644
--- a/tests/Fixtures/TestBundle/Document/DummyTableInheritance.php
+++ b/tests/Fixtures/TestBundle/Document/DummyTableInheritance.php
@@ -21,7 +21,12 @@
* @ODM\Document
* @ODM\InheritanceType("SINGLE_COLLECTION")
* @ODM\DiscriminatorField(value="discr")
- * @ODM\DiscriminatorMap({"dummyTableInheritance"=DummyTableInheritance::class, "dummyTableInheritanceChild"=DummyTableInheritanceChild::class, "dummyTableInheritanceDifferentChild"=DummyTableInheritanceDifferentChild::class})
+ * @ODM\DiscriminatorMap({
+ * "dummyTableInheritance"=DummyTableInheritance::class,
+ * "dummyTableInheritanceChild"=DummyTableInheritanceChild::class,
+ * "dummyTableInheritanceDifferentChild"=DummyTableInheritanceDifferentChild::class,
+ * "dummyTableInheritanceNotApiResourceChild"=DummyTableInheritanceNotApiResourceChild::class
+ * })
* @ApiResource
*/
class DummyTableInheritance
diff --git a/tests/Fixtures/TestBundle/Document/DummyTableInheritanceNotApiResourceChild.php b/tests/Fixtures/TestBundle/Document/DummyTableInheritanceNotApiResourceChild.php
new file mode 100644
index 00000000000..7eb951f8e07
--- /dev/null
+++ b/tests/Fixtures/TestBundle/Document/DummyTableInheritanceNotApiResourceChild.php
@@ -0,0 +1,45 @@
+
+ *
+ * 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\Core\Tests\Fixtures\TestBundle\Document;
+
+use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
+
+/**
+ * @ODM\Document
+ */
+class DummyTableInheritanceNotApiResourceChild extends DummyTableInheritance
+{
+ /**
+ * @var bool The dummy swagg
+ *
+ * @ODM\Field(type="boolean")
+ */
+ private $swaggerThanParent;
+
+ public function __construct()
+ {
+ // Definitely always swagger than parents
+ $this->swaggerThanParent = true;
+ }
+
+ public function isSwaggerThanParent(): bool
+ {
+ return $this->swaggerThanParent;
+ }
+
+ public function setSwaggerThanParent(bool $swaggerThanParent)
+ {
+ $this->swaggerThanParent = $swaggerThanParent;
+ }
+}
diff --git a/tests/Fixtures/TestBundle/Document/VoDummyCar.php b/tests/Fixtures/TestBundle/Document/VoDummyCar.php
index 4694ee4aec3..93e4fa07deb 100644
--- a/tests/Fixtures/TestBundle/Document/VoDummyCar.php
+++ b/tests/Fixtures/TestBundle/Document/VoDummyCar.php
@@ -21,8 +21,8 @@
/**
* @ApiResource(attributes={
- * "normalization_context"={"groups"={"read", "write"}},
- * "denormalization_context"={"groups"={"write"}}
+ * "normalization_context"={"groups"={"car_read"}},
+ * "denormalization_context"={"groups"={"car_write"}}
* })
* @ODM\Document
*/
@@ -32,7 +32,7 @@ class VoDummyCar extends VoDummyVehicle
* @var int
*
* @ODM\Field(type="integer")
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $mileage;
@@ -40,7 +40,7 @@ class VoDummyCar extends VoDummyVehicle
* @var string
*
* @ODM\Field
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $bodyType;
@@ -48,7 +48,7 @@ class VoDummyCar extends VoDummyVehicle
* @var VoDummyInspection[]|Collection
*
* @ODM\ReferenceMany(targetDocument=VoDummyInspection::class, mappedBy="car", cascade={"persist"})
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $inspections;
diff --git a/tests/Fixtures/TestBundle/Document/VoDummyDriver.php b/tests/Fixtures/TestBundle/Document/VoDummyDriver.php
index 604fcf5c920..5fcaab29ba9 100644
--- a/tests/Fixtures/TestBundle/Document/VoDummyDriver.php
+++ b/tests/Fixtures/TestBundle/Document/VoDummyDriver.php
@@ -29,7 +29,7 @@ class VoDummyDriver
* @var string
*
* @ODM\Field
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $firstName;
@@ -37,7 +37,7 @@ class VoDummyDriver
* @var string
*
* @ODM\Field
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $lastName;
diff --git a/tests/Fixtures/TestBundle/Document/VoDummyInspection.php b/tests/Fixtures/TestBundle/Document/VoDummyInspection.php
index 8f551ebd736..60fd02c77fb 100644
--- a/tests/Fixtures/TestBundle/Document/VoDummyInspection.php
+++ b/tests/Fixtures/TestBundle/Document/VoDummyInspection.php
@@ -19,7 +19,10 @@
use Symfony\Component\Serializer\Annotation\Groups;
/**
- * @ApiResource
+ * @ApiResource(attributes={
+ * "normalization_context"={"groups"={"inspection_read"}},
+ * "denormalization_context"={"groups"={"inspection_write"}}
+ * })
* @ODM\Document
*/
class VoDummyInspection
@@ -30,7 +33,7 @@ class VoDummyInspection
* @var bool
*
* @ODM\Field(type="boolean")
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write", "inspection_read", "inspection_write"})
*/
private $accepted;
@@ -38,7 +41,7 @@ class VoDummyInspection
* @var VoDummyCar
*
* @ODM\ReferenceOne(targetDocument=VoDummyCar::class, inversedBy="inspections")
- * @Groups({"write"})
+ * @Groups({"inspection_read", "inspection_write"})
*/
private $car;
@@ -46,7 +49,7 @@ class VoDummyInspection
* @var DateTime
*
* @ODM\Field(type="date")
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write", "inspection_read", "inspection_write"})
*/
private $performed;
diff --git a/tests/Fixtures/TestBundle/Document/VoDummyInsuranceCompany.php b/tests/Fixtures/TestBundle/Document/VoDummyInsuranceCompany.php
index e0d16e65f00..4264f7fabc2 100644
--- a/tests/Fixtures/TestBundle/Document/VoDummyInsuranceCompany.php
+++ b/tests/Fixtures/TestBundle/Document/VoDummyInsuranceCompany.php
@@ -29,7 +29,7 @@ class VoDummyInsuranceCompany
* @var string
*
* @ODM\Field
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $name;
diff --git a/tests/Fixtures/TestBundle/Document/VoDummyVehicle.php b/tests/Fixtures/TestBundle/Document/VoDummyVehicle.php
index 89f58b965b6..e9ab3f6e42c 100644
--- a/tests/Fixtures/TestBundle/Document/VoDummyVehicle.php
+++ b/tests/Fixtures/TestBundle/Document/VoDummyVehicle.php
@@ -29,7 +29,7 @@ abstract class VoDummyVehicle
* @var string
*
* @ODM\Field
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $make;
@@ -37,7 +37,7 @@ abstract class VoDummyVehicle
* @var VoDummyInsuranceCompany
*
* @ODM\ReferenceOne(targetDocument=VoDummyInsuranceCompany::class, cascade={"persist"})
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $insuranceCompany;
@@ -45,7 +45,7 @@ abstract class VoDummyVehicle
* @var VoDummyDriver[]|Collection
*
* @ODM\ReferenceMany(targetDocument=VoDummyDriver::class, cascade={"persist"})
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $drivers;
diff --git a/tests/Fixtures/TestBundle/Entity/AbstractUser.php b/tests/Fixtures/TestBundle/Entity/AbstractUser.php
new file mode 100644
index 00000000000..bcea76900ec
--- /dev/null
+++ b/tests/Fixtures/TestBundle/Entity/AbstractUser.php
@@ -0,0 +1,92 @@
+
+ *
+ * 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\Core\Tests\Fixtures\TestBundle\Entity;
+
+use ApiPlatform\Core\Annotation\ApiResource;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity
+ * @ORM\InheritanceType("JOINED")
+ * @ApiResource(
+ * collectionOperations={
+ * "get"={"path"="/custom_users"}
+ * },
+ * itemOperations={
+ * "get"={"path"="/custom_users/{id}"}
+ * }
+ * )
+ */
+abstract class AbstractUser
+{
+ /**
+ * @ORM\Column(type="integer")
+ * @ORM\Id
+ * @ORM\GeneratedValue(strategy="AUTO")
+ */
+ private $id;
+ /**
+ * @ORM\Column
+ */
+ private $firstname;
+ /**
+ * @ORM\Column
+ */
+ private $lastname;
+ /**
+ * @ORM\Column
+ */
+ private $email;
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getFirstname(): ?string
+ {
+ return $this->firstname;
+ }
+
+ public function setFirstname(string $firstname): self
+ {
+ $this->firstname = $firstname;
+
+ return $this;
+ }
+
+ public function getLastname(): ?string
+ {
+ return $this->lastname;
+ }
+
+ public function setLastname(string $lastname): self
+ {
+ $this->lastname = $lastname;
+
+ return $this;
+ }
+
+ public function getEmail(): ?string
+ {
+ return $this->email;
+ }
+
+ public function setEmail(string $email): self
+ {
+ $this->email = $email;
+
+ return $this;
+ }
+}
diff --git a/tests/Fixtures/TestBundle/Entity/DummyTableInheritance.php b/tests/Fixtures/TestBundle/Entity/DummyTableInheritance.php
index 4a208281425..23b629d541d 100644
--- a/tests/Fixtures/TestBundle/Entity/DummyTableInheritance.php
+++ b/tests/Fixtures/TestBundle/Entity/DummyTableInheritance.php
@@ -21,7 +21,12 @@
* @ORM\Entity
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="discr", type="string")
- * @ORM\DiscriminatorMap({"dummyTableInheritance"="DummyTableInheritance", "dummyTableInheritanceChild"="DummyTableInheritanceChild", "dummyTableInheritanceDifferentChild"="DummyTableInheritanceDifferentChild"})
+ * @ORM\DiscriminatorMap({
+ * "dummyTableInheritance"="DummyTableInheritance",
+ * "dummyTableInheritanceChild"="DummyTableInheritanceChild",
+ * "dummyTableInheritanceDifferentChild"="DummyTableInheritanceDifferentChild",
+ * "dummyTableInheritanceNotApiResourceChild"="DummyTableInheritanceNotApiResourceChild"
+ * })
* @ApiResource
*/
class DummyTableInheritance
diff --git a/tests/Fixtures/TestBundle/Entity/DummyTableInheritanceNotApiResourceChild.php b/tests/Fixtures/TestBundle/Entity/DummyTableInheritanceNotApiResourceChild.php
new file mode 100644
index 00000000000..0d6912e8a2b
--- /dev/null
+++ b/tests/Fixtures/TestBundle/Entity/DummyTableInheritanceNotApiResourceChild.php
@@ -0,0 +1,45 @@
+
+ *
+ * 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\Core\Tests\Fixtures\TestBundle\Entity;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity
+ */
+class DummyTableInheritanceNotApiResourceChild extends DummyTableInheritance
+{
+ /**
+ * @var bool The dummy swagg
+ *
+ * @ORM\Column(type="boolean")
+ */
+ private $swaggerThanParent;
+
+ public function __construct()
+ {
+ // Definitely always swagger than parents
+ $this->swaggerThanParent = true;
+ }
+
+ public function isSwaggerThanParent(): bool
+ {
+ return $this->swaggerThanParent;
+ }
+
+ public function setSwaggerThanParent(bool $swaggerThanParent)
+ {
+ $this->swaggerThanParent = $swaggerThanParent;
+ }
+}
diff --git a/tests/Fixtures/TestBundle/Entity/DummyTableInheritanceRelated.php b/tests/Fixtures/TestBundle/Entity/DummyTableInheritanceRelated.php
index d1880ea8e43..4e6e90022ec 100644
--- a/tests/Fixtures/TestBundle/Entity/DummyTableInheritanceRelated.php
+++ b/tests/Fixtures/TestBundle/Entity/DummyTableInheritanceRelated.php
@@ -44,6 +44,7 @@ class DummyTableInheritanceRelated
* @var ArrayCollection Related children
*
* @ORM\OneToMany(targetEntity="DummyTableInheritance", mappedBy="parent")
+ * @ORM\OrderBy({"id"="ASC"})
*
* @Groups({"default"})
*/
diff --git a/tests/Fixtures/TestBundle/Entity/ExternalUser.php b/tests/Fixtures/TestBundle/Entity/ExternalUser.php
new file mode 100644
index 00000000000..e98284e664c
--- /dev/null
+++ b/tests/Fixtures/TestBundle/Entity/ExternalUser.php
@@ -0,0 +1,41 @@
+
+ *
+ * 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\Core\Tests\Fixtures\TestBundle\Entity;
+
+use ApiPlatform\Core\Annotation\ApiResource;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity
+ * @ApiResource
+ */
+class ExternalUser extends AbstractUser
+{
+ /**
+ * @ORM\Column
+ */
+ private $externalId;
+
+ public function getExternalId(): ?string
+ {
+ return $this->externalId;
+ }
+
+ public function setExternalId(string $externalId): self
+ {
+ $this->externalId = $externalId;
+
+ return $this;
+ }
+}
diff --git a/tests/Fixtures/TestBundle/Entity/InternalUser.php b/tests/Fixtures/TestBundle/Entity/InternalUser.php
new file mode 100644
index 00000000000..fd89fdb0ac4
--- /dev/null
+++ b/tests/Fixtures/TestBundle/Entity/InternalUser.php
@@ -0,0 +1,39 @@
+
+ *
+ * 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\Core\Tests\Fixtures\TestBundle\Entity;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity
+ */
+class InternalUser extends AbstractUser
+{
+ /**
+ * @ORM\Column
+ */
+ private $internalId;
+
+ public function getInternalId(): ?string
+ {
+ return $this->internalId;
+ }
+
+ public function setInternalId(string $internalId): self
+ {
+ $this->internalId = $internalId;
+
+ return $this;
+ }
+}
diff --git a/tests/Fixtures/TestBundle/Entity/Site.php b/tests/Fixtures/TestBundle/Entity/Site.php
new file mode 100644
index 00000000000..4899d7a8efa
--- /dev/null
+++ b/tests/Fixtures/TestBundle/Entity/Site.php
@@ -0,0 +1,85 @@
+
+ *
+ * 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\Core\Tests\Fixtures\TestBundle\Entity;
+
+use ApiPlatform\Core\Annotation\ApiResource;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ApiResource
+ * @ORM\Entity
+ */
+class Site
+{
+ /**
+ * @ORM\Column(type="integer")
+ * @ORM\Id
+ * @ORM\GeneratedValue(strategy="AUTO")
+ */
+ private $id;
+ /**
+ * @ORM\Column
+ */
+ private $title;
+ /**
+ * @ORM\Column
+ */
+ private $description;
+ /**
+ * @ORM\OneToOne(targetEntity="AbstractUser", cascade={"persist", "remove"})
+ * @ORM\JoinColumn(nullable=false)
+ */
+ private $owner;
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getTitle(): ?string
+ {
+ return $this->title;
+ }
+
+ public function setTitle(string $title): self
+ {
+ $this->title = $title;
+
+ return $this;
+ }
+
+ public function getDescription(): ?string
+ {
+ return $this->description;
+ }
+
+ public function setDescription(string $description): self
+ {
+ $this->description = $description;
+
+ return $this;
+ }
+
+ public function getOwner(): ?AbstractUser
+ {
+ return $this->owner;
+ }
+
+ public function setOwner(AbstractUser $owner): self
+ {
+ $this->owner = $owner;
+
+ return $this;
+ }
+}
diff --git a/tests/Fixtures/TestBundle/Entity/VoDummyCar.php b/tests/Fixtures/TestBundle/Entity/VoDummyCar.php
index 693727d54f1..a1f11c2edca 100644
--- a/tests/Fixtures/TestBundle/Entity/VoDummyCar.php
+++ b/tests/Fixtures/TestBundle/Entity/VoDummyCar.php
@@ -21,8 +21,8 @@
/**
* @ApiResource(attributes={
- * "normalization_context"={"groups"={"read", "write"}},
- * "denormalization_context"={"groups"={"write"}}
+ * "normalization_context"={"groups"={"car_read"}},
+ * "denormalization_context"={"groups"={"car_write"}}
* })
* @ORM\Entity
*/
@@ -32,7 +32,7 @@ class VoDummyCar extends VoDummyVehicle
* @var int
*
* @ORM\Column(type="integer")
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $mileage;
@@ -40,7 +40,7 @@ class VoDummyCar extends VoDummyVehicle
* @var string
*
* @ORM\Column
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $bodyType;
@@ -48,7 +48,7 @@ class VoDummyCar extends VoDummyVehicle
* @var VoDummyInspection[]|Collection
*
* @ORM\OneToMany(targetEntity="VoDummyInspection", mappedBy="car", cascade={"persist"})
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $inspections;
diff --git a/tests/Fixtures/TestBundle/Entity/VoDummyDriver.php b/tests/Fixtures/TestBundle/Entity/VoDummyDriver.php
index edb06b37a6e..f71722046f1 100644
--- a/tests/Fixtures/TestBundle/Entity/VoDummyDriver.php
+++ b/tests/Fixtures/TestBundle/Entity/VoDummyDriver.php
@@ -29,7 +29,7 @@ class VoDummyDriver
* @var string
*
* @ORM\Column
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $firstName;
@@ -37,7 +37,7 @@ class VoDummyDriver
* @var string
*
* @ORM\Column
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $lastName;
diff --git a/tests/Fixtures/TestBundle/Entity/VoDummyInspection.php b/tests/Fixtures/TestBundle/Entity/VoDummyInspection.php
index 816147a4b19..a0aca6022a6 100644
--- a/tests/Fixtures/TestBundle/Entity/VoDummyInspection.php
+++ b/tests/Fixtures/TestBundle/Entity/VoDummyInspection.php
@@ -19,7 +19,10 @@
use Symfony\Component\Serializer\Annotation\Groups;
/**
- * @ApiResource
+ * @ApiResource(attributes={
+ * "normalization_context"={"groups"={"inspection_read"}},
+ * "denormalization_context"={"groups"={"inspection_write"}}
+ * })
* @ORM\Entity
*/
class VoDummyInspection
@@ -30,7 +33,7 @@ class VoDummyInspection
* @var bool
*
* @ORM\Column(type="boolean")
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write", "inspection_read", "inspection_write"})
*/
private $accepted;
@@ -38,7 +41,7 @@ class VoDummyInspection
* @var VoDummyCar
*
* @ORM\ManyToOne(targetEntity="VoDummyCar", inversedBy="inspections")
- * @Groups({"write"})
+ * @Groups({"inspection_read", "inspection_write"})
*/
private $car;
@@ -46,7 +49,7 @@ class VoDummyInspection
* @var DateTime
*
* @ORM\Column(type="datetime")
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write", "inspection_read", "inspection_write"})
*/
private $performed;
diff --git a/tests/Fixtures/TestBundle/Entity/VoDummyInsuranceCompany.php b/tests/Fixtures/TestBundle/Entity/VoDummyInsuranceCompany.php
index e25a44e08bf..d1a6626ebe8 100644
--- a/tests/Fixtures/TestBundle/Entity/VoDummyInsuranceCompany.php
+++ b/tests/Fixtures/TestBundle/Entity/VoDummyInsuranceCompany.php
@@ -29,7 +29,7 @@ class VoDummyInsuranceCompany
* @var string
*
* @ORM\Column
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $name;
diff --git a/tests/Fixtures/TestBundle/Entity/VoDummyVehicle.php b/tests/Fixtures/TestBundle/Entity/VoDummyVehicle.php
index 5fc72d4e2eb..14e03216652 100644
--- a/tests/Fixtures/TestBundle/Entity/VoDummyVehicle.php
+++ b/tests/Fixtures/TestBundle/Entity/VoDummyVehicle.php
@@ -29,7 +29,7 @@ abstract class VoDummyVehicle
* @var string
*
* @ORM\Column
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $make;
@@ -37,7 +37,7 @@ abstract class VoDummyVehicle
* @var VoDummyInsuranceCompany
*
* @ORM\ManyToOne(targetEntity="VoDummyInsuranceCompany", cascade={"persist"})
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $insuranceCompany;
@@ -45,7 +45,7 @@ abstract class VoDummyVehicle
* @var VoDummyDriver[]|Collection
*
* @ORM\ManyToMany(targetEntity="VoDummyDriver", cascade={"persist"})
- * @Groups({"write"})
+ * @Groups({"car_read", "car_write"})
*/
private $drivers;
diff --git a/tests/GraphQl/Serializer/ItemNormalizerTest.php b/tests/GraphQl/Serializer/ItemNormalizerTest.php
index d8d1c9e9293..159db53469f 100644
--- a/tests/GraphQl/Serializer/ItemNormalizerTest.php
+++ b/tests/GraphQl/Serializer/ItemNormalizerTest.php
@@ -72,21 +72,21 @@ public function testNormalize()
$propertyNameCollection = new PropertyNameCollection(['name']);
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn($propertyNameCollection)->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn($propertyNameCollection);
$propertyMetadata = new PropertyMetadata(null, null, true);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn($propertyMetadata)->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn($propertyMetadata);
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1')->shouldBeCalled();
+ $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1');
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($dummy, null, false)->willReturn(Dummy::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
- $serializerProphecy->normalize('hello', ItemNormalizer::FORMAT, Argument::type('array'))->willReturn('hello')->shouldBeCalled();
+ $serializerProphecy->normalize('hello', ItemNormalizer::FORMAT, Argument::type('array'))->willReturn('hello');
$normalizer = new ItemNormalizer(
$propertyNameCollectionFactoryProphecy->reveal(),
@@ -104,7 +104,13 @@ public function testNormalize()
);
$normalizer->setSerializer($serializerProphecy->reveal());
- $this->assertEquals(['name' => 'hello', ItemNormalizer::ITEM_KEY => serialize($dummy)], $normalizer->normalize($dummy, ItemNormalizer::FORMAT, ['resources' => []]));
+ $expected = [
+ 'name' => 'hello',
+ ItemNormalizer::ITEM_KEY => serialize($dummy),
+ ];
+ $this->assertEquals($expected, $normalizer->normalize($dummy, ItemNormalizer::FORMAT, [
+ 'resources' => [],
+ ]));
}
public function testDenormalize()
@@ -122,6 +128,7 @@ public function testDenormalize()
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
diff --git a/tests/Hal/Serializer/CollectionNormalizerTest.php b/tests/Hal/Serializer/CollectionNormalizerTest.php
index cf67c4cfe1f..96d2e1fe7ce 100644
--- a/tests/Hal/Serializer/CollectionNormalizerTest.php
+++ b/tests/Hal/Serializer/CollectionNormalizerTest.php
@@ -113,31 +113,34 @@ public function testNormalizePartialPaginator()
private function normalizePaginator($partial = false)
{
$paginatorProphecy = $this->prophesize($partial ? PartialPaginatorInterface::class : PaginatorInterface::class);
- $paginatorProphecy->getCurrentPage()->willReturn(3)->shouldBeCalled();
- $paginatorProphecy->getItemsPerPage()->willReturn(12)->shouldBeCalled();
- $paginatorProphecy->rewind()->shouldBeCalled();
- $paginatorProphecy->valid()->willReturn(true, false)->shouldBeCalled();
- $paginatorProphecy->current()->willReturn('foo')->shouldBeCalled();
- $paginatorProphecy->next()->willReturn()->shouldBeCalled();
+ $paginatorProphecy->getCurrentPage()->willReturn(3);
+ $paginatorProphecy->getItemsPerPage()->willReturn(12);
+ $paginatorProphecy->rewind()->will(function () {});
+ $paginatorProphecy->valid()->willReturn(true, false);
+ $paginatorProphecy->current()->willReturn('foo');
+ $paginatorProphecy->next()->will(function () {});
if (!$partial) {
- $paginatorProphecy->getLastPage()->willReturn(7)->shouldBeCalled();
- $paginatorProphecy->getTotalItems()->willReturn(1312)->shouldBeCalled();
+ $paginatorProphecy->getLastPage()->willReturn(7);
+ $paginatorProphecy->getTotalItems()->willReturn(1312);
} else {
- $paginatorProphecy->count()->willReturn(12)->shouldBeCalled();
+ $paginatorProphecy->count()->willReturn(12);
}
- $paginator = $paginatorProphecy->reveal();
-
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($paginator, null, true)->willReturn('Foo')->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($paginatorProphecy, 'Foo')->willReturn('Foo');
$itemNormalizer = $this->prophesize(NormalizerInterface::class);
- $itemNormalizer->normalize('foo', null, ['api_sub_level' => true, 'resource_class' => 'Foo'])->willReturn(['_links' => ['self' => '/me'], 'name' => 'Kévin']);
+ $itemNormalizer->normalize('foo', CollectionNormalizer::FORMAT, [
+ 'api_sub_level' => true,
+ 'resource_class' => 'Foo',
+ ])->willReturn(['_links' => ['self' => '/me'], 'name' => 'Kévin']);
$normalizer = new CollectionNormalizer($resourceClassResolverProphecy->reveal(), 'page');
$normalizer->setNormalizer($itemNormalizer->reveal());
- return $normalizer->normalize($paginator);
+ return $normalizer->normalize($paginatorProphecy->reveal(), CollectionNormalizer::FORMAT, [
+ 'resource_class' => 'Foo',
+ ]);
}
}
diff --git a/tests/Hal/Serializer/ItemNormalizerTest.php b/tests/Hal/Serializer/ItemNormalizerTest.php
index 3ac060292f0..0c68d249832 100644
--- a/tests/Hal/Serializer/ItemNormalizerTest.php
+++ b/tests/Hal/Serializer/ItemNormalizerTest.php
@@ -82,8 +82,8 @@ public function testSupportsNormalization()
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true)->shouldBeCalled();
- $resourceClassResolverProphecy->isResourceClass(\stdClass::class)->willReturn(false)->shouldBeCalled();
+ $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
+ $resourceClassResolverProphecy->isResourceClass(\stdClass::class)->willReturn(false);
$nameConverter = $this->prophesize(NameConverterInterface::class);
@@ -111,32 +111,33 @@ public function testNormalize()
$propertyNameCollection = new PropertyNameCollection(['name', 'relatedDummy']);
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn($propertyNameCollection)->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn($propertyNameCollection);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn(
new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true)
- )->shouldBeCalled();
+ );
$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
new PropertyMetadata(new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class), '', true, false, false)
- )->shouldBeCalled();
+ );
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1')->shouldBeCalled();
- $iriConverterProphecy->getIriFromItem($relatedDummy)->willReturn('/related-dummies/2')->shouldBeCalled();
+ $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1');
+ $iriConverterProphecy->getIriFromItem($relatedDummy)->willReturn('/related-dummies/2');
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
- $resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class, true)->willReturn(Dummy::class)->shouldBeCalled();
- $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($dummy, null, false)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class, true)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class, true)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
- $serializerProphecy->normalize('hello', null, Argument::type('array'))->willReturn('hello')->shouldBeCalled();
+ $serializerProphecy->normalize('hello', null, Argument::type('array'))->willReturn('hello');
$nameConverter = $this->prophesize(NameConverterInterface::class);
- $nameConverter->normalize('name', Argument::any(), Argument::any(), Argument::any())->shouldBeCalled()->willReturn('name');
- $nameConverter->normalize('relatedDummy', Argument::any(), Argument::any(), Argument::any())->shouldBeCalled()->willReturn('related_dummy');
+ $nameConverter->normalize('name', Argument::any(), Argument::any(), Argument::any())->willReturn('name');
+ $nameConverter->normalize('relatedDummy', Argument::any(), Argument::any(), Argument::any())->willReturn('related_dummy');
$normalizer = new ItemNormalizer(
$propertyNameCollectionFactoryProphecy->reveal(),
@@ -177,32 +178,33 @@ public function testNormalizeWithoutCache()
$propertyNameCollection = new PropertyNameCollection(['name', 'relatedDummy']);
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn($propertyNameCollection)->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn($propertyNameCollection);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn(
new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true)
- )->shouldBeCalled();
+ );
$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
new PropertyMetadata(new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class), '', true, false, false)
- )->shouldBeCalled();
+ );
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1')->shouldBeCalled();
- $iriConverterProphecy->getIriFromItem($relatedDummy)->willReturn('/related-dummies/2')->shouldBeCalled();
+ $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1');
+ $iriConverterProphecy->getIriFromItem($relatedDummy)->willReturn('/related-dummies/2');
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
- $resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class, true)->willReturn(Dummy::class)->shouldBeCalled();
- $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($dummy, null, false)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class, true)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class, true)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
- $serializerProphecy->normalize('hello', null, Argument::type('array'))->willReturn('hello')->shouldBeCalled();
+ $serializerProphecy->normalize('hello', null, Argument::type('array'))->willReturn('hello');
$nameConverter = $this->prophesize(NameConverterInterface::class);
- $nameConverter->normalize('name', Argument::any(), Argument::any(), Argument::any())->shouldBeCalled()->willReturn('name');
- $nameConverter->normalize('relatedDummy', Argument::any(), Argument::any(), Argument::any())->shouldBeCalled()->willReturn('related_dummy');
+ $nameConverter->normalize('name', Argument::any(), Argument::any(), Argument::any())->willReturn('name');
+ $nameConverter->normalize('relatedDummy', Argument::any(), Argument::any(), Argument::any())->willReturn('related_dummy');
$normalizer = new ItemNormalizer(
$propertyNameCollectionFactoryProphecy->reveal(),
@@ -258,30 +260,31 @@ public function testMaxDepth()
$propertyNameCollection = new PropertyNameCollection(['id', 'name', 'child']);
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(MaxDepthDummy::class, [])->willReturn($propertyNameCollection)->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(MaxDepthDummy::class, [])->willReturn($propertyNameCollection);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create(MaxDepthDummy::class, 'id', [])->willReturn(
new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT), '', true)
- )->shouldBeCalled();
+ );
$propertyMetadataFactoryProphecy->create(MaxDepthDummy::class, 'name', [])->willReturn(
new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true)
- )->shouldBeCalled();
+ );
$propertyMetadataFactoryProphecy->create(MaxDepthDummy::class, 'child', [])->willReturn(
new PropertyMetadata(new Type(Type::BUILTIN_TYPE_OBJECT, false, MaxDepthDummy::class), '', true, false, true)
- )->shouldBeCalled();
+ );
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $iriConverterProphecy->getIriFromItem($level1)->willReturn('/max_depth_dummies/1')->shouldBeCalled();
- $iriConverterProphecy->getIriFromItem($level2)->willReturn('/max_depth_dummies/2')->shouldBeCalled();
- $iriConverterProphecy->getIriFromItem($level3)->willReturn('/max_depth_dummies/3')->shouldBeCalled();
+ $iriConverterProphecy->getIriFromItem($level1)->willReturn('/max_depth_dummies/1');
+ $iriConverterProphecy->getIriFromItem($level2)->willReturn('/max_depth_dummies/2');
+ $iriConverterProphecy->getIriFromItem($level3)->willReturn('/max_depth_dummies/3');
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($level1, null, true)->willReturn(MaxDepthDummy::class)->shouldBeCalled();
- $resourceClassResolverProphecy->getResourceClass($level1, MaxDepthDummy::class, true)->willReturn(MaxDepthDummy::class)->shouldBeCalled();
- $resourceClassResolverProphecy->getResourceClass($level2, MaxDepthDummy::class, true)->willReturn(MaxDepthDummy::class)->shouldBeCalled();
- $resourceClassResolverProphecy->getResourceClass($level3, MaxDepthDummy::class, true)->willReturn(MaxDepthDummy::class)->shouldBeCalled();
- $resourceClassResolverProphecy->isResourceClass(MaxDepthDummy::class)->willReturn(true)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($level1, null, false)->willReturn(MaxDepthDummy::class);
+ $resourceClassResolverProphecy->getResourceClass($level1, MaxDepthDummy::class, true)->willReturn(MaxDepthDummy::class);
+ $resourceClassResolverProphecy->getResourceClass($level2, MaxDepthDummy::class, true)->willReturn(MaxDepthDummy::class);
+ $resourceClassResolverProphecy->getResourceClass($level3, MaxDepthDummy::class, true)->willReturn(MaxDepthDummy::class);
+ $resourceClassResolverProphecy->getResourceClass(null, MaxDepthDummy::class, true)->willReturn(MaxDepthDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(MaxDepthDummy::class)->willReturn(true);
$normalizer = new ItemNormalizer(
$propertyNameCollectionFactoryProphecy->reveal(),
diff --git a/tests/Hydra/Serializer/CollectionFiltersNormalizerTest.php b/tests/Hydra/Serializer/CollectionFiltersNormalizerTest.php
index e6a3ab95d9a..6f48636b200 100644
--- a/tests/Hydra/Serializer/CollectionFiltersNormalizerTest.php
+++ b/tests/Hydra/Serializer/CollectionFiltersNormalizerTest.php
@@ -196,13 +196,16 @@ public function testDoNothingIfNoFilter()
$dummy = new Dummy();
$decoratedProphecy = $this->prophesize(NormalizerInterface::class);
- $decoratedProphecy->normalize($dummy, null, ['collection_operation_name' => 'get'])->willReturn(['name' => 'foo'])->shouldBeCalled();
+ $decoratedProphecy->normalize($dummy, CollectionNormalizer::FORMAT, [
+ 'collection_operation_name' => 'get',
+ 'resource_class' => Dummy::class,
+ ])->willReturn(['name' => 'foo']);
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
$resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata('foo', '', null, [], ['get' => []]));
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class)->willReturn(Dummy::class);
$normalizer = new CollectionFiltersNormalizer(
$decoratedProphecy->reveal(),
@@ -211,7 +214,10 @@ public function testDoNothingIfNoFilter()
$this->prophesize(ContainerInterface::class)->reveal()
);
- $this->assertEquals(['name' => 'foo'], $normalizer->normalize($dummy, null, ['collection_operation_name' => 'get']));
+ $this->assertEquals(['name' => 'foo'], $normalizer->normalize($dummy, CollectionNormalizer::FORMAT, [
+ 'collection_operation_name' => 'get',
+ 'resource_class' => Dummy::class,
+ ]));
}
public function testDoNothingIfNoRequestUri()
@@ -219,13 +225,15 @@ public function testDoNothingIfNoRequestUri()
$dummy = new Dummy();
$decoratedProphecy = $this->prophesize(NormalizerInterface::class);
- $decoratedProphecy->normalize($dummy, null, [])->willReturn(['name' => 'foo'])->shouldBeCalled();
+ $decoratedProphecy->normalize($dummy, CollectionNormalizer::FORMAT, [
+ 'resource_class' => Dummy::class,
+ ])->willReturn(['name' => 'foo']);
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
$resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata('foo', '', null, [], [], ['filters' => ['foo']]));
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class)->willReturn(Dummy::class);
$normalizer = new CollectionFiltersNormalizer(
$decoratedProphecy->reveal(),
@@ -234,7 +242,9 @@ public function testDoNothingIfNoRequestUri()
$this->prophesize(ContainerInterface::class)->reveal()
);
- $this->assertEquals(['name' => 'foo'], $normalizer->normalize($dummy));
+ $this->assertEquals(['name' => 'foo'], $normalizer->normalize($dummy, CollectionNormalizer::FORMAT, [
+ 'resource_class' => Dummy::class,
+ ]));
}
public function testNormalize()
@@ -282,13 +292,16 @@ private function normalize($filterLocator)
$dummy = new Dummy();
$decoratedProphecy = $this->prophesize(NormalizerInterface::class);
- $decoratedProphecy->normalize($dummy, null, ['request_uri' => '/foo?bar=baz'])->willReturn(['name' => 'foo'])->shouldBeCalled();
+ $decoratedProphecy->normalize($dummy, CollectionNormalizer::FORMAT, [
+ 'request_uri' => '/foo?bar=baz',
+ 'resource_class' => Dummy::class,
+ ])->willReturn(['name' => 'foo']);
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
$resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata('foo', '', null, [], [], ['filters' => ['foo']]));
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class)->willReturn(Dummy::class);
$normalizer = new CollectionFiltersNormalizer(
$decoratedProphecy->reveal(),
@@ -312,6 +325,9 @@ private function normalize($filterLocator)
],
],
],
- ], $normalizer->normalize($dummy, null, ['request_uri' => '/foo?bar=baz']));
+ ], $normalizer->normalize($dummy, CollectionNormalizer::FORMAT, [
+ 'request_uri' => '/foo?bar=baz',
+ 'resource_class' => Dummy::class,
+ ]));
}
}
diff --git a/tests/Hydra/Serializer/CollectionNormalizerTest.php b/tests/Hydra/Serializer/CollectionNormalizerTest.php
index 6b91d9030c1..2cbf75637f1 100644
--- a/tests/Hydra/Serializer/CollectionNormalizerTest.php
+++ b/tests/Hydra/Serializer/CollectionNormalizerTest.php
@@ -80,7 +80,7 @@ public function testNormalizeResourceCollection()
$contextBuilderProphecy->getResourceContextUri(Foo::class)->willReturn('/contexts/Foo');
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($data, Foo::class, true)->willReturn(Foo::class);
+ $resourceClassResolverProphecy->getResourceClass($data, Foo::class)->willReturn(Foo::class);
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
$iriConverterProphecy->getIriFromResourceClass(Foo::class)->willReturn('/foos');
@@ -294,21 +294,19 @@ private function normalizePaginator($partial = false)
$paginatorProphecy = $this->prophesize($partial ? PartialPaginatorInterface::class : PaginatorInterface::class);
if (!$partial) {
- $paginatorProphecy->getTotalItems()->willReturn(1312)->shouldBeCalled();
+ $paginatorProphecy->getTotalItems()->willReturn(1312);
}
- $paginatorProphecy->rewind()->shouldBeCalled();
- $paginatorProphecy->valid()->willReturn(true, false)->shouldBeCalled();
- $paginatorProphecy->current()->willReturn('foo')->shouldBeCalled();
- $paginatorProphecy->next()->willReturn()->shouldBeCalled();
-
- $paginator = $paginatorProphecy->reveal();
+ $paginatorProphecy->rewind()->will(function () {});
+ $paginatorProphecy->valid()->willReturn(true, false);
+ $paginatorProphecy->current()->willReturn('foo');
+ $paginatorProphecy->next()->will(function () {});
$serializer = $this->prophesize(SerializerInterface::class);
$serializer->willImplement(NormalizerInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($paginator, null, true)->willReturn('Foo')->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($paginatorProphecy, 'Foo')->willReturn('Foo');
$iriConvert = $this->prophesize(IriConverterInterface::class);
$iriConvert->getIriFromResourceClass('Foo')->willReturn('/foo/1');
@@ -317,11 +315,17 @@ private function normalizePaginator($partial = false)
$contextBuilder->getResourceContextUri('Foo')->willReturn('/contexts/Foo');
$itemNormalizer = $this->prophesize(AbstractItemNormalizer::class);
- $itemNormalizer->normalize('foo', null, ['jsonld_has_context' => true, 'api_sub_level' => true, 'resource_class' => 'Foo'])->willReturn(['name' => 'Kévin', 'friend' => 'Smail']);
+ $itemNormalizer->normalize('foo', CollectionNormalizer::FORMAT, [
+ 'jsonld_has_context' => true,
+ 'api_sub_level' => true,
+ 'resource_class' => 'Foo',
+ ])->willReturn(['name' => 'Kévin', 'friend' => 'Smail']);
$normalizer = new CollectionNormalizer($contextBuilder->reveal(), $resourceClassResolverProphecy->reveal(), $iriConvert->reveal());
$normalizer->setNormalizer($itemNormalizer->reveal());
- return $normalizer->normalize($paginator);
+ return $normalizer->normalize($paginatorProphecy->reveal(), CollectionNormalizer::FORMAT, [
+ 'resource_class' => 'Foo',
+ ]);
}
}
diff --git a/tests/JsonApi/Serializer/CollectionNormalizerTest.php b/tests/JsonApi/Serializer/CollectionNormalizerTest.php
index e062e7d11a3..7096379e25d 100644
--- a/tests/JsonApi/Serializer/CollectionNormalizerTest.php
+++ b/tests/JsonApi/Serializer/CollectionNormalizerTest.php
@@ -42,41 +42,35 @@ public function testSupportsNormalize()
public function testNormalizePaginator()
{
$paginatorProphecy = $this->prophesize(PaginatorInterface::class);
- $paginatorProphecy->getCurrentPage()->willReturn(3.)->shouldBeCalled();
- $paginatorProphecy->getLastPage()->willReturn(7.)->shouldBeCalled();
- $paginatorProphecy->getItemsPerPage()->willReturn(12.)->shouldBeCalled();
- $paginatorProphecy->getTotalItems()->willReturn(1312.)->shouldBeCalled();
- $paginatorProphecy->rewind()->shouldBeCalled();
- $paginatorProphecy->next()->willReturn()->shouldBeCalled();
- $paginatorProphecy->current()->willReturn('foo')->shouldBeCalled();
- $paginatorProphecy->valid()->willReturn(true, false)->shouldBeCalled();
+ $paginatorProphecy->getCurrentPage()->willReturn(3.);
+ $paginatorProphecy->getLastPage()->willReturn(7.);
+ $paginatorProphecy->getItemsPerPage()->willReturn(12.);
+ $paginatorProphecy->getTotalItems()->willReturn(1312.);
+ $paginatorProphecy->rewind()->will(function () {});
+ $paginatorProphecy->next()->will(function () {});
+ $paginatorProphecy->current()->willReturn('foo');
+ $paginatorProphecy->valid()->willReturn(true, false);
$paginator = $paginatorProphecy->reveal();
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($paginator, null, true)->willReturn('Foo')->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($paginator, 'Foo')->willReturn('Foo');
$itemNormalizer = $this->prophesize(NormalizerInterface::class);
- $itemNormalizer
- ->normalize(
- 'foo',
- CollectionNormalizer::FORMAT,
- [
- 'request_uri' => '/foos?page=3',
- 'api_sub_level' => true,
- 'resource_class' => 'Foo',
- ]
- )
- ->willReturn([
- 'data' => [
- 'type' => 'Foo',
+ $itemNormalizer->normalize('foo', CollectionNormalizer::FORMAT, [
+ 'request_uri' => '/foos?page=3',
+ 'api_sub_level' => true,
+ 'resource_class' => 'Foo',
+ ])->willReturn([
+ 'data' => [
+ 'type' => 'Foo',
+ 'id' => 1,
+ 'attributes' => [
'id' => 1,
- 'attributes' => [
- 'id' => 1,
- 'name' => 'Kévin',
- ],
+ 'name' => 'Kévin',
],
- ]);
+ ],
+ ]);
$normalizer = new CollectionNormalizer($resourceClassResolverProphecy->reveal(), 'page');
$normalizer->setNormalizer($itemNormalizer->reveal());
@@ -106,46 +100,43 @@ public function testNormalizePaginator()
],
];
- $this->assertEquals($expected, $normalizer->normalize($paginator, CollectionNormalizer::FORMAT, ['request_uri' => '/foos?page=3']));
+ $this->assertEquals($expected, $normalizer->normalize($paginator, CollectionNormalizer::FORMAT, [
+ 'request_uri' => '/foos?page=3',
+ 'resource_class' => 'Foo',
+ ]));
}
public function testNormalizePartialPaginator()
{
$paginatorProphecy = $this->prophesize(PartialPaginatorInterface::class);
- $paginatorProphecy->getCurrentPage()->willReturn(3.)->shouldBeCalled();
- $paginatorProphecy->getItemsPerPage()->willReturn(12.)->shouldBeCalled();
- $paginatorProphecy->rewind()->shouldBeCalled();
- $paginatorProphecy->next()->willReturn()->shouldBeCalled();
- $paginatorProphecy->current()->willReturn('foo')->shouldBeCalled();
- $paginatorProphecy->valid()->willReturn(true, false)->shouldBeCalled();
- $paginatorProphecy->count()->willReturn(1312)->shouldBeCalled();
+ $paginatorProphecy->getCurrentPage()->willReturn(3.);
+ $paginatorProphecy->getItemsPerPage()->willReturn(12.);
+ $paginatorProphecy->rewind()->will(function () {});
+ $paginatorProphecy->next()->will(function () {});
+ $paginatorProphecy->current()->willReturn('foo');
+ $paginatorProphecy->valid()->willReturn(true, false);
+ $paginatorProphecy->count()->willReturn(1312);
$paginator = $paginatorProphecy->reveal();
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($paginator, null, true)->willReturn('Foo')->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($paginator, 'Foo')->willReturn('Foo');
$itemNormalizer = $this->prophesize(NormalizerInterface::class);
- $itemNormalizer
- ->normalize(
- 'foo',
- CollectionNormalizer::FORMAT,
- [
- 'request_uri' => '/foos?page=3',
- 'api_sub_level' => true,
- 'resource_class' => 'Foo',
- ]
- )
- ->willReturn([
- 'data' => [
- 'type' => 'Foo',
+ $itemNormalizer->normalize('foo', CollectionNormalizer::FORMAT, [
+ 'request_uri' => '/foos?page=3',
+ 'api_sub_level' => true,
+ 'resource_class' => 'Foo',
+ ])->willReturn([
+ 'data' => [
+ 'type' => 'Foo',
+ 'id' => 1,
+ 'attributes' => [
'id' => 1,
- 'attributes' => [
- 'id' => 1,
- 'name' => 'Kévin',
- ],
+ 'name' => 'Kévin',
],
- ]);
+ ],
+ ]);
$normalizer = new CollectionNormalizer($resourceClassResolverProphecy->reveal(), 'page');
$normalizer->setNormalizer($itemNormalizer->reveal());
@@ -172,7 +163,10 @@ public function testNormalizePartialPaginator()
],
];
- $this->assertEquals($expected, $normalizer->normalize($paginator, CollectionNormalizer::FORMAT, ['request_uri' => '/foos?page=3']));
+ $this->assertEquals($expected, $normalizer->normalize($paginator, CollectionNormalizer::FORMAT, [
+ 'request_uri' => '/foos?page=3',
+ 'resource_class' => 'Foo',
+ ]));
}
public function testNormalizeArray()
@@ -180,29 +174,23 @@ public function testNormalizeArray()
$data = ['foo'];
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($data, null, true)->willReturn('Foo')->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($data, 'Foo')->willReturn('Foo');
$itemNormalizer = $this->prophesize(NormalizerInterface::class);
- $itemNormalizer
- ->normalize(
- 'foo',
- CollectionNormalizer::FORMAT,
- [
- 'request_uri' => '/foos',
- 'api_sub_level' => true,
- 'resource_class' => 'Foo',
- ]
- )
- ->willReturn([
- 'data' => [
- 'type' => 'Foo',
+ $itemNormalizer->normalize('foo', CollectionNormalizer::FORMAT, [
+ 'request_uri' => '/foos',
+ 'api_sub_level' => true,
+ 'resource_class' => 'Foo',
+ ])->willReturn([
+ 'data' => [
+ 'type' => 'Foo',
+ 'id' => 1,
+ 'attributes' => [
'id' => 1,
- 'attributes' => [
- 'id' => 1,
- 'name' => 'Baptiste',
- ],
+ 'name' => 'Baptiste',
],
- ]);
+ ],
+ ]);
$normalizer = new CollectionNormalizer($resourceClassResolverProphecy->reveal(), 'page');
$normalizer->setNormalizer($itemNormalizer->reveal());
@@ -222,7 +210,10 @@ public function testNormalizeArray()
'meta' => ['totalItems' => 1],
];
- $this->assertEquals($expected, $normalizer->normalize($data, CollectionNormalizer::FORMAT, ['request_uri' => '/foos']));
+ $this->assertEquals($expected, $normalizer->normalize($data, CollectionNormalizer::FORMAT, [
+ 'request_uri' => '/foos',
+ 'resource_class' => 'Foo',
+ ]));
}
public function testNormalizeIncludedData()
@@ -230,39 +221,33 @@ public function testNormalizeIncludedData()
$data = ['foo'];
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($data, null, true)->willReturn('Foo')->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($data, 'Foo')->willReturn('Foo');
$itemNormalizer = $this->prophesize(NormalizerInterface::class);
- $itemNormalizer
- ->normalize(
- 'foo',
- CollectionNormalizer::FORMAT,
+ $itemNormalizer->normalize('foo', CollectionNormalizer::FORMAT, [
+ 'request_uri' => '/foos',
+ 'api_sub_level' => true,
+ 'resource_class' => 'Foo',
+ ])->willReturn([
+ 'data' => [
+ 'type' => 'Foo',
+ 'id' => 1,
+ 'attributes' => [
+ 'id' => 1,
+ 'name' => 'Baptiste',
+ ],
+ ],
+ 'included' => [
[
- 'request_uri' => '/foos',
- 'api_sub_level' => true,
- 'resource_class' => 'Foo',
- ]
- )
- ->willReturn([
- 'data' => [
- 'type' => 'Foo',
+ 'type' => 'Bar',
'id' => 1,
'attributes' => [
'id' => 1,
- 'name' => 'Baptiste',
- ],
- ],
- 'included' => [
- [
- 'type' => 'Bar',
- 'id' => 1,
- 'attributes' => [
- 'id' => 1,
- 'name' => 'Anto',
- ],
+ 'name' => 'Anto',
],
],
- ]);
+ ],
+ ]);
$normalizer = new CollectionNormalizer($resourceClassResolverProphecy->reveal(), 'page');
$normalizer->setNormalizer($itemNormalizer->reveal());
@@ -292,7 +277,10 @@ public function testNormalizeIncludedData()
],
];
- $this->assertEquals($expected, $normalizer->normalize($data, CollectionNormalizer::FORMAT, ['request_uri' => '/foos']));
+ $this->assertEquals($expected, $normalizer->normalize($data, CollectionNormalizer::FORMAT, [
+ 'request_uri' => '/foos',
+ 'resource_class' => 'Foo',
+ ]));
}
public function testNormalizeWithoutDataKey()
@@ -303,24 +291,21 @@ public function testNormalizeWithoutDataKey()
$data = ['foo'];
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($data, null, true)->willReturn('Foo')->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($data, 'Foo')->willReturn('Foo');
$itemNormalizer = $this->prophesize(NormalizerInterface::class);
- $itemNormalizer
- ->normalize(
- 'foo',
- CollectionNormalizer::FORMAT,
- [
- 'request_uri' => '/foos',
- 'api_sub_level' => true,
- 'resource_class' => 'Foo',
- ]
- )
- ->willReturn([]);
+ $itemNormalizer->normalize('foo', CollectionNormalizer::FORMAT, [
+ 'request_uri' => '/foos',
+ 'api_sub_level' => true,
+ 'resource_class' => 'Foo',
+ ])->willReturn([]);
$normalizer = new CollectionNormalizer($resourceClassResolverProphecy->reveal(), 'page');
$normalizer->setNormalizer($itemNormalizer->reveal());
- $normalizer->normalize($data, CollectionNormalizer::FORMAT, ['request_uri' => '/foos']);
+ $normalizer->normalize($data, CollectionNormalizer::FORMAT, [
+ 'request_uri' => '/foos',
+ 'resource_class' => 'Foo',
+ ]);
}
}
diff --git a/tests/JsonApi/Serializer/ItemNormalizerTest.php b/tests/JsonApi/Serializer/ItemNormalizerTest.php
index 24fbd80ad3a..1f07121e070 100644
--- a/tests/JsonApi/Serializer/ItemNormalizerTest.php
+++ b/tests/JsonApi/Serializer/ItemNormalizerTest.php
@@ -114,7 +114,7 @@ public function testNormalize()
$iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/10');
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass($dummy, null, false)->willReturn(Dummy::class);
$resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class, true)->willReturn(Dummy::class);
$propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
@@ -170,7 +170,7 @@ public function testNormalizeCircularReference()
$iriConverterProphecy->getIriFromItem($circularReferenceEntity)->willReturn('/circular_references/1');
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($circularReferenceEntity, null, true)->willReturn(CircularReference::class);
+ $resourceClassResolverProphecy->getResourceClass($circularReferenceEntity, null, false)->willReturn(CircularReference::class);
$resourceClassResolverProphecy->getResourceClass($circularReferenceEntity, CircularReference::class, true)->willReturn(CircularReference::class);
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
@@ -226,7 +226,7 @@ public function testNormalizeNonExistentProperty()
$iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1');
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass($dummy, null, false)->willReturn(Dummy::class);
$resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class, true)->willReturn(Dummy::class);
$propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
@@ -252,66 +252,67 @@ public function testNormalizeNonExistentProperty()
public function testDenormalize()
{
+ $data = [
+ 'data' => [
+ 'type' => 'dummy',
+ 'attributes' => [
+ 'name' => 'foo',
+ 'ghost' => 'invisible',
+ ],
+ 'relationships' => [
+ 'relatedDummy' => [
+ 'data' => [
+ 'type' => 'related-dummy',
+ 'id' => '/related_dummies/1',
+ ],
+ ],
+ 'relatedDummies' => [
+ 'data' => [
+ [
+ 'type' => 'related-dummy',
+ 'id' => '/related_dummies/2',
+ ],
+ ],
+ ],
+ ],
+ ],
+ ];
+
$relatedDummy1 = new RelatedDummy();
$relatedDummy1->setId(1);
$relatedDummy2 = new RelatedDummy();
$relatedDummy2->setId(2);
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['name', 'ghost', 'relatedDummy', 'relatedDummies'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['name', 'ghost', 'relatedDummy', 'relatedDummies']));
+
+ $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
+ $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn(
- new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', false, true)
- )->shouldBeCalled();
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'ghost', [])->willReturn(
- new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', false, true)
- )->shouldBeCalled();
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
- new PropertyMetadata(
- new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class),
- '',
- false,
- true,
- false,
- false
- )
- )->shouldBeCalled();
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(
- new PropertyMetadata(
- new Type(Type::BUILTIN_TYPE_OBJECT,
- false,
- ArrayCollection::class,
- true,
- new Type(Type::BUILTIN_TYPE_INT),
- new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)
- ),
- '',
- false,
- true,
- false,
- false
- )
- )->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', false, true));
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'ghost', [])->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', false, true));
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(new PropertyMetadata($relatedDummyType, '', false, true, false, false));
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(new PropertyMetadata($relatedDummiesType, '', false, true, false, false));
$getItemFromIriSecondArgCallback = function ($arg) {
return \is_array($arg) && isset($arg['fetch_data']) && true === $arg['fetch_data'];
};
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $iriConverterProphecy->getItemFromIri('/related_dummies/1', Argument::that($getItemFromIriSecondArgCallback))->willReturn($relatedDummy1)->shouldBeCalled();
- $iriConverterProphecy->getItemFromIri('/related_dummies/2', Argument::that($getItemFromIriSecondArgCallback))->willReturn($relatedDummy2)->shouldBeCalled();
+ $iriConverterProphecy->getItemFromIri('/related_dummies/1', Argument::that($getItemFromIriSecondArgCallback))->willReturn($relatedDummy1);
+ $iriConverterProphecy->getItemFromIri('/related_dummies/2', Argument::that($getItemFromIriSecondArgCallback))->willReturn($relatedDummy2);
$propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
- $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'name', 'foo')->shouldBeCalled();
- $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'ghost', 'invisible')->willThrow(new NoSuchPropertyException())->shouldBeCalled();
- $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummy', $relatedDummy1)->shouldBeCalled();
- $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummies', [$relatedDummy2])->shouldBeCalled();
+ $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'name', 'foo')->will(function () {});
+ $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'ghost', 'invisible')->willThrow(new NoSuchPropertyException());
+ $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummy', $relatedDummy1)->will(function () {});
+ $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummies', [$relatedDummy2])->will(function () {});
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
@@ -332,38 +333,7 @@ public function testDenormalize()
);
$normalizer->setSerializer($serializerProphecy->reveal());
- $this->assertInstanceOf(
- Dummy::class,
- $normalizer->denormalize(
- [
- 'data' => [
- 'type' => 'dummy',
- 'attributes' => [
- 'name' => 'foo',
- 'ghost' => 'invisible',
- ],
- 'relationships' => [
- 'relatedDummy' => [
- 'data' => [
- 'type' => 'related-dummy',
- 'id' => '/related_dummies/1',
- ],
- ],
- 'relatedDummies' => [
- 'data' => [
- [
- 'type' => 'related-dummy',
- 'id' => '/related_dummies/2',
- ],
- ],
- ],
- ],
- ],
- ],
- Dummy::class,
- ItemNormalizer::FORMAT
- )
- );
+ $this->assertInstanceOf(Dummy::class, $normalizer->denormalize($data, Dummy::class, ItemNormalizer::FORMAT));
}
public function testDenormalizeUpdateOperationNotAllowed()
@@ -403,8 +373,19 @@ public function testDenormalizeCollectionIsNotArray()
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The type of the "relatedDummies" attribute must be "array", "string" given.');
+ $data = [
+ 'data' => [
+ 'type' => 'dummy',
+ 'relationships' => [
+ 'relatedDummies' => [
+ 'data' => 'foo',
+ ],
+ ],
+ ],
+ ];
+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummies']))->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummies']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(
@@ -422,7 +403,16 @@ public function testDenormalizeCollectionIsNotArray()
false,
false
)
- )->shouldBeCalled();
+ );
+
+ $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
+
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
+
+ $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$resourceMetadataFactory = $this->prophesize(ResourceMetadataFactoryInterface::class);
$resourceMetadataFactory->create(Dummy::class)->willThrow(ResourceClassNotFoundException::class);
@@ -430,29 +420,16 @@ public function testDenormalizeCollectionIsNotArray()
$normalizer = new ItemNormalizer(
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
- $this->prophesize(IriConverterInterface::class)->reveal(),
- $this->prophesize(ResourceClassResolverInterface::class)->reveal(),
- $this->prophesize(PropertyAccessorInterface::class)->reveal(),
+ $iriConverterProphecy->reveal(),
+ $resourceClassResolverProphecy->reveal(),
+ $propertyAccessorProphecy->reveal(),
new ReservedAttributeNameConverter(),
$resourceMetadataFactory->reveal(),
[],
[]
);
- $normalizer->denormalize(
- [
- 'data' => [
- 'type' => 'dummy',
- 'relationships' => [
- 'relatedDummies' => [
- 'data' => 'foo',
- ],
- ],
- ],
- ],
- Dummy::class,
- ItemNormalizer::FORMAT
- );
+ $normalizer->denormalize($data, Dummy::class, ItemNormalizer::FORMAT);
}
public function testDenormalizeCollectionWithInvalidKey()
@@ -460,8 +437,24 @@ public function testDenormalizeCollectionWithInvalidKey()
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('The type of the key "0" must be "string", "integer" given.');
+ $data = [
+ 'data' => [
+ 'type' => 'dummy',
+ 'relationships' => [
+ 'relatedDummies' => [
+ 'data' => [
+ [
+ 'type' => 'related-dummy',
+ 'id' => '2',
+ ],
+ ],
+ ],
+ ],
+ ],
+ ];
+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummies']))->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummies']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(
@@ -479,7 +472,16 @@ public function testDenormalizeCollectionWithInvalidKey()
false,
false
)
- )->shouldBeCalled();
+ );
+
+ $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
+
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
+
+ $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$resourceMetadataFactory = $this->prophesize(ResourceMetadataFactoryInterface::class);
$resourceMetadataFactory->create(Dummy::class)->willThrow(ResourceClassNotFoundException::class);
@@ -487,34 +489,16 @@ public function testDenormalizeCollectionWithInvalidKey()
$normalizer = new ItemNormalizer(
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
- $this->prophesize(IriConverterInterface::class)->reveal(),
- $this->prophesize(ResourceClassResolverInterface::class)->reveal(),
- $this->prophesize(PropertyAccessorInterface::class)->reveal(),
+ $iriConverterProphecy->reveal(),
+ $resourceClassResolverProphecy->reveal(),
+ $propertyAccessorProphecy->reveal(),
new ReservedAttributeNameConverter(),
$resourceMetadataFactory->reveal(),
[],
[]
);
- $normalizer->denormalize(
- [
- 'data' => [
- 'type' => 'dummy',
- 'relationships' => [
- 'relatedDummies' => [
- 'data' => [
- [
- 'type' => 'related-dummy',
- 'id' => '2',
- ],
- ],
- ],
- ],
- ],
- ],
- Dummy::class,
- ItemNormalizer::FORMAT
- );
+ $normalizer->denormalize($data, Dummy::class, ItemNormalizer::FORMAT);
}
public function testDenormalizeRelationIsNotResourceLinkage()
@@ -522,8 +506,19 @@ public function testDenormalizeRelationIsNotResourceLinkage()
$this->expectException(NotNormalizableValueException::class);
$this->expectExceptionMessage('Only resource linkage supported currently, see: http://jsonapi.org/format/#document-resource-object-linkage.');
+ $data = [
+ 'data' => [
+ 'type' => 'dummy',
+ 'relationships' => [
+ 'relatedDummy' => [
+ 'data' => 'foo',
+ ],
+ ],
+ ],
+ ];
+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummy']))->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummy']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
@@ -535,10 +530,16 @@ public function testDenormalizeRelationIsNotResourceLinkage()
false,
false
)
- )->shouldBeCalled();
+ );
+
+ $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
+
+ $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$resourceMetadataFactory = $this->prophesize(ResourceMetadataFactoryInterface::class);
$resourceMetadataFactory->create(Dummy::class)->willThrow(ResourceClassNotFoundException::class);
@@ -546,28 +547,15 @@ public function testDenormalizeRelationIsNotResourceLinkage()
$normalizer = new ItemNormalizer(
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
- $this->prophesize(IriConverterInterface::class)->reveal(),
+ $iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal(),
- $this->prophesize(PropertyAccessorInterface::class)->reveal(),
+ $propertyAccessorProphecy->reveal(),
new ReservedAttributeNameConverter(),
$resourceMetadataFactory->reveal(),
[],
[]
);
- $normalizer->denormalize(
- [
- 'data' => [
- 'type' => 'dummy',
- 'relationships' => [
- 'relatedDummy' => [
- 'data' => 'foo',
- ],
- ],
- ],
- ],
- Dummy::class,
- ItemNormalizer::FORMAT
- );
+ $normalizer->denormalize($data, Dummy::class, ItemNormalizer::FORMAT);
}
}
diff --git a/tests/JsonLd/Serializer/ItemNormalizerTest.php b/tests/JsonLd/Serializer/ItemNormalizerTest.php
index fb5b1bf984f..fbea14f6392 100644
--- a/tests/JsonLd/Serializer/ItemNormalizerTest.php
+++ b/tests/JsonLd/Serializer/ItemNormalizerTest.php
@@ -97,22 +97,22 @@ public function testNormalize()
$resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata('Dummy'));
$propertyNameCollection = new PropertyNameCollection(['name']);
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn($propertyNameCollection)->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn($propertyNameCollection);
$propertyMetadata = new PropertyMetadata(null, null, true);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn($propertyMetadata)->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn($propertyMetadata);
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1988')->shouldBeCalled();
+ $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1988');
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
- $resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class, true)->willReturn(Dummy::class)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($dummy, null, false)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class, true)->willReturn(Dummy::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
- $serializerProphecy->normalize('hello', null, Argument::type('array'))->willReturn('hello')->shouldBeCalled();
+ $serializerProphecy->normalize('hello', null, Argument::type('array'))->willReturn('hello');
$contextBuilderProphecy = $this->prophesize(ContextBuilderInterface::class);
$contextBuilderProphecy->getResourceContextUri(Dummy::class)->willReturn('/contexts/Dummy');
@@ -131,7 +131,8 @@ public function testNormalize()
);
$normalizer->setSerializer($serializerProphecy->reveal());
- $expected = ['@context' => '/contexts/Dummy',
+ $expected = [
+ '@context' => '/contexts/Dummy',
'@id' => '/dummies/1988',
'@type' => 'Dummy',
'name' => 'hello',
diff --git a/tests/Serializer/AbstractItemNormalizerTest.php b/tests/Serializer/AbstractItemNormalizerTest.php
index f38d04bdd18..edc0a17ac96 100644
--- a/tests/Serializer/AbstractItemNormalizerTest.php
+++ b/tests/Serializer/AbstractItemNormalizerTest.php
@@ -130,56 +130,39 @@ public function testNormalize()
$dummy->setRelatedDummy($relatedDummy);
$dummy->relatedDummies->add(new RelatedDummy());
+ $relatedDummies = new ArrayCollection([$relatedDummy]);
+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['name', 'alias', 'relatedDummy', 'relatedDummies'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['name', 'alias', 'relatedDummy', 'relatedDummies']));
+
+ $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
+ $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn(
- new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true)
- )->shouldBeCalled();
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'alias', [])->willReturn(
- new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true)
- )->shouldBeCalled();
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
- new PropertyMetadata(new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class), '', true, false, false)
- )->shouldBeCalled();
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(
- new PropertyMetadata(
- new Type(Type::BUILTIN_TYPE_OBJECT,
- false,
- ArrayCollection::class,
- true,
- new Type(Type::BUILTIN_TYPE_INT),
- new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)
- ),
- '',
- true,
- false,
- false
- )
- )->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true));
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'alias', [])->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true));
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(new PropertyMetadata($relatedDummyType, '', true, false, false));
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(new PropertyMetadata($relatedDummiesType, '', true, false, false));
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1')->shouldBeCalled();
- $iriConverterProphecy->getIriFromItem($relatedDummy)->willReturn('/dummies/2')->shouldBeCalled();
+ $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1');
+ $iriConverterProphecy->getIriFromItem($relatedDummy)->willReturn('/dummies/2');
$propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
- $propertyAccessorProphecy->getValue($dummy, 'name')->willReturn('foo')->shouldBeCalled();
- $propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy)->shouldBeCalled();
- $propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn(
- new ArrayCollection([$relatedDummy])
- )->shouldBeCalled();
+ $propertyAccessorProphecy->getValue($dummy, 'name')->willReturn('foo');
+ $propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy);
+ $propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn($relatedDummies);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
- $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(RelatedDummy::class)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($dummy, null, false)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class, true)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->getResourceClass($relatedDummies, RelatedDummy::class)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(RelatedDummy::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
- $serializerProphecy->normalize('foo', null, Argument::type('array'))->willReturn('foo')->shouldBeCalled();
- $serializerProphecy->normalize(['/dummies/2'], null, Argument::type('array'))->willReturn(['/dummies/2'])->shouldBeCalled();
+ $serializerProphecy->normalize('foo', null, Argument::type('array'))->willReturn('foo');
+ $serializerProphecy->normalize(['/dummies/2'], null, Argument::type('array'))->willReturn(['/dummies/2']);
$normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
$propertyNameCollectionFactoryProphecy->reveal(),
@@ -202,11 +185,15 @@ public function testNormalize()
$normalizer->setIgnoredAttributes(['alias']);
}
- $this->assertEquals([
+ $expected = [
'name' => 'foo',
'relatedDummy' => '/dummies/2',
'relatedDummies' => ['/dummies/2'],
- ], $normalizer->normalize($dummy, null, ['resources' => [], 'ignored_attributes' => ['alias']]));
+ ];
+ $this->assertEquals($expected, $normalizer->normalize($dummy, null, [
+ 'resources' => [],
+ 'ignored_attributes' => ['alias'],
+ ]));
}
public function testNormalizeReadableLinks()
@@ -217,48 +204,36 @@ public function testNormalizeReadableLinks()
$dummy->setRelatedDummy($relatedDummy);
$dummy->relatedDummies->add(new RelatedDummy());
+ $relatedDummies = new ArrayCollection([$relatedDummy]);
+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['relatedDummy', 'relatedDummies'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummy', 'relatedDummies']));
+
+ $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
+ $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
- new PropertyMetadata(new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class), '', true, false, true)
- )->shouldBeCalled();
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(
- new PropertyMetadata(
- new Type(
- Type::BUILTIN_TYPE_OBJECT,
- false,
- ArrayCollection::class,
- true,
- new Type(Type::BUILTIN_TYPE_INT),
- new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)
- ),
- '',
- true,
- false,
- true
- )
- )->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(new PropertyMetadata($relatedDummyType, '', true, false, true));
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(new PropertyMetadata($relatedDummiesType, '', true, false, true));
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1')->shouldBeCalled();
+ $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1');
$propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
- $propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy)->shouldBeCalled();
- $propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn(new ArrayCollection([$relatedDummy]))->shouldBeCalled();
+ $propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy);
+ $propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn($relatedDummies);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
- $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(RelatedDummy::class)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($dummy, null, false)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class, true)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->getResourceClass($relatedDummies, RelatedDummy::class)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(RelatedDummy::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
- $serializerProphecy->normalize($relatedDummy, null, Argument::type('array'))->willReturn(['foo' => 'hello'])->shouldBeCalled();
- $serializerProphecy->normalize(['foo' => 'hello'], null, Argument::type('array'))->willReturn(['foo' => 'hello'])->shouldBeCalled();
- $serializerProphecy->normalize([['foo' => 'hello']], null, Argument::type('array'))->willReturn([['foo' => 'hello']])->shouldBeCalled();
+ $serializerProphecy->normalize($relatedDummy, null, Argument::type('array'))->willReturn(['foo' => 'hello']);
+ $serializerProphecy->normalize(['foo' => 'hello'], null, Argument::type('array'))->willReturn(['foo' => 'hello']);
+ $serializerProphecy->normalize([['foo' => 'hello']], null, Argument::type('array'))->willReturn([['foo' => 'hello']]);
$normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
$propertyNameCollectionFactoryProphecy->reveal(),
@@ -277,63 +252,47 @@ public function testNormalizeReadableLinks()
]);
$normalizer->setSerializer($serializerProphecy->reveal());
- $this->assertEquals([
+ $expected = [
'relatedDummy' => ['foo' => 'hello'],
'relatedDummies' => [['foo' => 'hello']],
- ], $normalizer->normalize($dummy, null, ['resources' => []]));
+ ];
+ $this->assertEquals($expected, $normalizer->normalize($dummy, null, [
+ 'resources' => [],
+ ]));
}
public function testDenormalize()
{
+ $data = [
+ 'name' => 'foo',
+ 'relatedDummy' => '/dummies/1',
+ 'relatedDummies' => ['/dummies/2'],
+ ];
+
$relatedDummy1 = new RelatedDummy();
$relatedDummy2 = new RelatedDummy();
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['name', 'relatedDummy', 'relatedDummies'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['name', 'relatedDummy', 'relatedDummies']));
+
+ $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
+ $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn(
- new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', false, true)
- )->shouldBeCalled();
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
- new PropertyMetadata(
- new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class),
- '',
- false,
- true,
- false,
- false
- )
- )->shouldBeCalled();
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(
- new PropertyMetadata(
- new Type(Type::BUILTIN_TYPE_OBJECT,
- false,
- ArrayCollection::class,
- true,
- new Type(Type::BUILTIN_TYPE_INT),
- new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)
- ),
- '',
- false,
- true,
- false,
- false
- )
- )->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', false, true));
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(new PropertyMetadata($relatedDummyType, '', false, true, false, false));
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(new PropertyMetadata($relatedDummiesType, '', false, true, false, false));
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $iriConverterProphecy->getItemFromIri('/dummies/1', Argument::type('array'))->willReturn($relatedDummy1)->shouldBeCalled();
- $iriConverterProphecy->getItemFromIri('/dummies/2', Argument::type('array'))->willReturn($relatedDummy2)->shouldBeCalled();
+ $iriConverterProphecy->getItemFromIri('/dummies/1', Argument::type('array'))->willReturn($relatedDummy1);
+ $iriConverterProphecy->getItemFromIri('/dummies/2', Argument::type('array'))->willReturn($relatedDummy2);
$propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
- $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'name', 'foo')->shouldBeCalled();
- $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummy', $relatedDummy1)->shouldBeCalled();
- $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummies', [$relatedDummy2])->shouldBeCalled();
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
@@ -355,110 +314,97 @@ public function testDenormalize()
]);
$normalizer->setSerializer($serializerProphecy->reveal());
- $normalizer->denormalize([
- 'name' => 'foo',
- 'relatedDummy' => '/dummies/1',
- 'relatedDummies' => ['/dummies/2'],
- ], Dummy::class);
+ $actual = $normalizer->denormalize($data, Dummy::class);
+
+ $this->assertInstanceOf(Dummy::class, $actual);
+
+ $propertyAccessorProphecy->setValue($actual, 'name', 'foo')->shouldHaveBeenCalled();
+ $propertyAccessorProphecy->setValue($actual, 'relatedDummy', $relatedDummy1)->shouldHaveBeenCalled();
+ $propertyAccessorProphecy->setValue($actual, 'relatedDummies', [$relatedDummy2])->shouldHaveBeenCalled();
}
public function testCanDenormalizeInputClassWithDifferentFieldsThanResourceClass()
{
+ $data = [
+ 'dummyName' => 'Dummy Name',
+ ];
+
+ $context = [
+ 'resource_class' => DummyForAdditionalFields::class,
+ 'input' => ['class' => DummyForAdditionalFieldsInput::class],
+ 'output' => ['class' => DummyForAdditionalFields::class],
+ ];
+ $augmentedContext = $context + ['api_denormalize' => true];
+ $cleanedContext = array_diff_key($augmentedContext, [
+ 'input' => null,
+ 'resource_class' => null,
+ ]);
+
+ $dummyInputDto = new DummyForAdditionalFieldsInput('Dummy Name');
+ $dummy = new DummyForAdditionalFields('Dummy Name', 'dummy-name');
+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(DummyForAdditionalFieldsInput::class, [])->willReturn(
- new PropertyNameCollection(['dummyName'])
- );
- $propertyNameCollectionFactoryProphecy->create(DummyForAdditionalFields::class, [])->willReturn(
- new PropertyNameCollection(['id', 'name', 'slug'])
- );
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- // Create DummyForAdditionalFieldsInput mocks
- $propertyMetadataFactoryProphecy->create(DummyForAdditionalFieldsInput::class, 'dummyName', [])->willReturn(
- (new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true, false))->withInitializable(true)
- );
- // Create DummyForAdditionalFields mocks
- $propertyMetadataFactoryProphecy->create(DummyForAdditionalFields::class, 'id', [])->willReturn(
- (new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT), '', true, false))->withInitializable(false)
- );
- $propertyMetadataFactoryProphecy->create(DummyForAdditionalFields::class, 'name', [])->willReturn(
- (new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true, false))->withInitializable(true)
- );
- $propertyMetadataFactoryProphecy->create(DummyForAdditionalFields::class, 'slug', [])->willReturn(
- (new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true, false))->withInitializable(true)
- );
- $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $this->prophesize(IriConverterInterface::class)->reveal(), $this->prophesize(ResourceClassResolverInterface::class)->reveal(), null, null, null, null, false, [], [], null) extends AbstractItemNormalizer {
+ $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
+
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(null, DummyForAdditionalFields::class)->willReturn(DummyForAdditionalFields::class);
+
+ $inputDataTransformerProphecy = $this->prophesize(DataTransformerInterface::class);
+ $inputDataTransformerProphecy->supportsTransformation($data, DummyForAdditionalFields::class, $augmentedContext)->willReturn(true);
+ $inputDataTransformerProphecy->transform($dummyInputDto, DummyForAdditionalFields::class, $augmentedContext)->willReturn($dummy);
+
+ $serializerProphecy = $this->prophesize(SerializerInterface::class);
+ $serializerProphecy->willImplement(DenormalizerInterface::class);
+ $serializerProphecy->denormalize($data, DummyForAdditionalFieldsInput::class, 'json', $cleanedContext)->willReturn($dummyInputDto);
+
+ $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), null, null, null, null, false, [], [$inputDataTransformerProphecy->reveal()], null) extends AbstractItemNormalizer {
};
+ $normalizer->setSerializer($serializerProphecy->reveal());
- /** @var DummyForAdditionalFieldsInput $res */
- $res = $normalizer->denormalize([
- 'dummyName' => 'Dummy Name',
- ], DummyForAdditionalFieldsInput::class, 'json', [
- 'resource_class' => DummyForAdditionalFields::class,
- 'input' => ['class' => DummyForAdditionalFieldsInput::class],
- 'output' => ['class' => DummyForAdditionalFields::class],
- ]);
+ $actual = $normalizer->denormalize($data, DummyForAdditionalFields::class, 'json', $context);
- $this->assertInstanceOf(DummyForAdditionalFieldsInput::class, $res);
- $this->assertEquals('Dummy Name', $res->getDummyName());
+ $this->assertInstanceOf(DummyForAdditionalFields::class, $actual);
+ $this->assertEquals('Dummy Name', $actual->getName());
}
public function testDenormalizeWritableLinks()
{
+ $data = [
+ 'name' => 'foo',
+ 'relatedDummy' => ['foo' => 'bar'],
+ 'relatedDummies' => [['bar' => 'baz']],
+ ];
+
$relatedDummy1 = new RelatedDummy();
$relatedDummy2 = new RelatedDummy();
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['name', 'relatedDummy', 'relatedDummies'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['name', 'relatedDummy', 'relatedDummies']));
+
+ $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
+ $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn(
- new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', false, true)
- )->shouldBeCalled();
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
- new PropertyMetadata(new Type(
- Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class
- ), '', false, true, false, true)
- )->shouldBeCalled();
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(
- new PropertyMetadata(
- new Type(
- Type::BUILTIN_TYPE_OBJECT,
- false,
- ArrayCollection::class,
- true,
- new Type(Type::BUILTIN_TYPE_INT),
- new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)
- ),
- '',
- false,
- true,
- false,
- true
- )
- )->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', false, true));
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(new PropertyMetadata($relatedDummyType, '', false, true, false, true));
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(new PropertyMetadata($relatedDummiesType, '', false, true, false, true));
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
$propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
- $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'name', 'foo')->shouldBeCalled();
- $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummy', $relatedDummy1)->shouldBeCalled();
- $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummies', [$relatedDummy2])->shouldBeCalled();
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
- $serializerProphecy->denormalize(
- ['foo' => 'bar'], RelatedDummy::class, null, Argument::type('array')
- )->willReturn($relatedDummy1)->shouldBeCalled();
- $serializerProphecy->denormalize(
- ['bar' => 'baz'], RelatedDummy::class, null, Argument::type('array')
- )->willReturn($relatedDummy2)->shouldBeCalled();
+ $serializerProphecy->denormalize(['foo' => 'bar'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy1);
+ $serializerProphecy->denormalize(['bar' => 'baz'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy2);
$normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
$propertyNameCollectionFactoryProphecy->reveal(),
@@ -477,11 +423,13 @@ public function testDenormalizeWritableLinks()
]);
$normalizer->setSerializer($serializerProphecy->reveal());
- $normalizer->denormalize([
- 'name' => 'foo',
- 'relatedDummy' => ['foo' => 'bar'],
- 'relatedDummies' => [['bar' => 'baz']],
- ], Dummy::class);
+ $actual = $normalizer->denormalize($data, Dummy::class);
+
+ $this->assertInstanceOf(Dummy::class, $actual);
+
+ $propertyAccessorProphecy->setValue($actual, 'name', 'foo')->shouldHaveBeenCalled();
+ $propertyAccessorProphecy->setValue($actual, 'relatedDummy', $relatedDummy1)->shouldHaveBeenCalled();
+ $propertyAccessorProphecy->setValue($actual, 'relatedDummies', [$relatedDummy2])->shouldHaveBeenCalled();
}
public function testBadRelationType()
@@ -489,10 +437,12 @@ public function testBadRelationType()
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Expected IRI or nested document for attribute "relatedDummy", "integer" given.');
+ $data = [
+ 'relatedDummy' => 22,
+ ];
+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['relatedDummy'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummy']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
@@ -504,13 +454,16 @@ public function testBadRelationType()
false,
false
)
- )->shouldBeCalled();
+ );
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
+
+ $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -532,7 +485,7 @@ public function testBadRelationType()
]);
$normalizer->setSerializer($serializerProphecy->reveal());
- $normalizer->denormalize(['relatedDummy' => 22], Dummy::class);
+ $normalizer->denormalize($data, Dummy::class);
}
public function testInnerDocumentNotAllowed()
@@ -540,10 +493,14 @@ public function testInnerDocumentNotAllowed()
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Nested documents for attribute "relatedDummy" are not allowed. Use IRIs instead.');
+ $data = [
+ 'relatedDummy' => [
+ 'foo' => 'bar',
+ ],
+ ];
+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['relatedDummy'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummy']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
@@ -555,13 +512,16 @@ public function testInnerDocumentNotAllowed()
false,
false
)
- )->shouldBeCalled();
+ );
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
+
+ $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -583,7 +543,7 @@ public function testInnerDocumentNotAllowed()
]);
$normalizer->setSerializer($serializerProphecy->reveal());
- $normalizer->denormalize(['relatedDummy' => ['foo' => 'bar']], Dummy::class);
+ $normalizer->denormalize($data, Dummy::class);
}
public function testBadType()
@@ -591,19 +551,22 @@ public function testBadType()
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The type of the "foo" attribute must be "float", "integer" given.');
+ $data = [
+ 'foo' => 42,
+ ];
+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['foo'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['foo']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn(
- new PropertyMetadata(new Type(Type::BUILTIN_TYPE_FLOAT), '', false, true, false, false)
- )->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_FLOAT), '', false, true, false, false));
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
+
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+
+ $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -625,24 +588,27 @@ public function testBadType()
]);
$normalizer->setSerializer($serializerProphecy->reveal());
- $normalizer->denormalize(['foo' => 42], Dummy::class);
+ $normalizer->denormalize($data, Dummy::class);
}
public function testTypeChecksCanBeDisabled()
{
+ $data = [
+ 'foo' => 42,
+ ];
+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['foo'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['foo']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn(
- new PropertyMetadata(new Type(Type::BUILTIN_TYPE_FLOAT), '', false, true, false, false)
- )->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_FLOAT), '', false, true, false, false));
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
+
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+
+ $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -664,25 +630,31 @@ public function testTypeChecksCanBeDisabled()
]);
$normalizer->setSerializer($serializerProphecy->reveal());
- $normalizer->denormalize(['foo' => 42], Dummy::class, null, ['disable_type_enforcement' => true]);
+ $actual = $normalizer->denormalize($data, Dummy::class, null, ['disable_type_enforcement' => true]);
+
+ $this->assertInstanceOf(Dummy::class, $actual);
+
+ $propertyAccessorProphecy->setValue($actual, 'foo', 42)->shouldHaveBeenCalled();
}
public function testJsonAllowIntAsFloat()
{
+ $data = [
+ 'foo' => 42,
+ ];
+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['foo'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['foo']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn(
- new PropertyMetadata(new Type(Type::BUILTIN_TYPE_FLOAT), '', false, true, false, false)
- )->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_FLOAT), '', false, true, false, false));
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
- $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'foo', 42)->shouldBeCalled();
+
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+
+ $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -704,7 +676,11 @@ public function testJsonAllowIntAsFloat()
]);
$normalizer->setSerializer($serializerProphecy->reveal());
- $normalizer->denormalize(['foo' => 42], Dummy::class, 'jsonfoo');
+ $actual = $normalizer->denormalize($data, Dummy::class, 'jsonfoo');
+
+ $this->assertInstanceOf(Dummy::class, $actual);
+
+ $propertyAccessorProphecy->setValue($actual, 'foo', 42)->shouldHaveBeenCalled();
}
public function testDenormalizeBadKeyType()
@@ -712,10 +688,20 @@ public function testDenormalizeBadKeyType()
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The type of the key "a" must be "int", "string" given.');
+ $data = [
+ 'name' => 'foo',
+ 'relatedDummy' => [
+ 'foo' => 'bar',
+ ],
+ 'relatedDummies' => [
+ 'a' => [
+ 'bar' => 'baz',
+ ],
+ ],
+ ];
+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['relatedDummies'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummies']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(
@@ -734,12 +720,16 @@ public function testDenormalizeBadKeyType()
false,
true
)
- )->shouldBeCalled();
+ );
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
+
+ $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -761,28 +751,27 @@ public function testDenormalizeBadKeyType()
]);
$normalizer->setSerializer($serializerProphecy->reveal());
- $normalizer->denormalize([
- 'name' => 'foo',
- 'relatedDummy' => ['foo' => 'bar'],
- 'relatedDummies' => ['a' => ['bar' => 'baz']],
- ], Dummy::class);
+ $normalizer->denormalize($data, Dummy::class);
}
public function testNullable()
{
+ $data = [
+ 'name' => null,
+ ];
+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['name'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['name']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn(
- new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING, true), '', false, true, false, false)
- )->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING, true), '', false, true, false, false));
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
+
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+
+ $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -804,7 +793,11 @@ public function testNullable()
]);
$normalizer->setSerializer($serializerProphecy->reveal());
- $normalizer->denormalize(['name' => null], Dummy::class);
+ $actual = $normalizer->denormalize($data, Dummy::class);
+
+ $this->assertInstanceOf(Dummy::class, $actual);
+
+ $propertyAccessorProphecy->setValue($actual, 'name', null)->shouldHaveBeenCalled();
}
public function testChildInheritedProperty()
@@ -865,37 +858,32 @@ public function testChildInheritedProperty()
public function testDenormalizeRelationWithPlainId()
{
+ $data = [
+ 'relatedDummy' => 1,
+ ];
+
$relatedDummy = new RelatedDummy();
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['relatedDummy'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummy']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
- new PropertyMetadata(
- new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class),
- '',
- false,
- true,
- false,
- false
- )
- )->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class), '', false, true, false, false));
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
- $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummy', $relatedDummy)->shouldBeCalled();
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
+
+ $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
$itemDataProviderProphecy = $this->prophesize(ItemDataProviderInterface::class);
- $itemDataProviderProphecy->getItem(RelatedDummy::class, 1, null, Argument::type('array'))->willReturn($relatedDummy)->shouldBeCalled();
+ $itemDataProviderProphecy->getItem(RelatedDummy::class, 1, null, Argument::type('array'))->willReturn($relatedDummy);
$normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
$propertyNameCollectionFactoryProphecy->reveal(),
@@ -914,18 +902,24 @@ public function testDenormalizeRelationWithPlainId()
]);
$normalizer->setSerializer($serializerProphecy->reveal());
- $normalizer->denormalize(['relatedDummy' => 1], Dummy::class, 'jsonld');
+ $actual = $normalizer->denormalize($data, Dummy::class, 'jsonld');
+
+ $this->assertInstanceOf(Dummy::class, $actual);
+
+ $propertyAccessorProphecy->setValue($actual, 'relatedDummy', $relatedDummy)->shouldHaveBeenCalled();
}
public function testDenormalizeRelationWithPlainIdNotFound()
{
$this->expectException(ItemNotFoundException::class);
- $this->expectExceptionMessage('Item not found for "1".');
+ $this->expectExceptionMessage(sprintf('Item not found for resource "%s" with id "1".', RelatedDummy::class));
+
+ $data = [
+ 'relatedDummy' => 1,
+ ];
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['relatedDummy'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummy']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
@@ -937,19 +931,22 @@ public function testDenormalizeRelationWithPlainIdNotFound()
false,
false
)
- )->shouldBeCalled();
+ );
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
+
+ $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
$itemDataProviderProphecy = $this->prophesize(ItemDataProviderInterface::class);
- $itemDataProviderProphecy->getItem(RelatedDummy::class, 1, null, Argument::type('array'))->willReturn(null)->shouldBeCalled();
+ $itemDataProviderProphecy->getItem(RelatedDummy::class, 1, null, Argument::type('array'))->willReturn(null);
$normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
$propertyNameCollectionFactoryProphecy->reveal(),
@@ -968,7 +965,7 @@ public function testDenormalizeRelationWithPlainIdNotFound()
]);
$normalizer->setSerializer($serializerProphecy->reveal());
- $normalizer->denormalize(['relatedDummy' => 1], Dummy::class, 'jsonld');
+ $normalizer->denormalize($data, Dummy::class, 'jsonld');
}
public function testDoNotDenormalizeRelationWithPlainIdWhenPlainIdentifiersAreNotAllowed()
@@ -976,10 +973,12 @@ public function testDoNotDenormalizeRelationWithPlainIdWhenPlainIdentifiersAreNo
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Expected IRI or nested document for attribute "relatedDummy", "integer" given.');
+ $data = [
+ 'relatedDummy' => 1,
+ ];
+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(
- new PropertyNameCollection(['relatedDummy'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummy']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
@@ -991,13 +990,15 @@ public function testDoNotDenormalizeRelationWithPlainIdWhenPlainIdentifiersAreNo
false,
false
)
- )->shouldBeCalled();
+ );
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
$propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
+ $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -1022,7 +1023,7 @@ public function testDoNotDenormalizeRelationWithPlainIdWhenPlainIdentifiersAreNo
]);
$normalizer->setSerializer($serializerProphecy->reveal());
- $normalizer->denormalize(['relatedDummy' => 1], Dummy::class, 'jsonld');
+ $normalizer->denormalize($data, Dummy::class, 'jsonld');
}
/**
@@ -1037,35 +1038,24 @@ public function testDoNotDenormalizeRelationWithPlainIdWhenPlainIdentifiersAreNo
public function testNormalizationWithDataTransformer()
{
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(InputDto::class, Argument::any())->willReturn(
- new PropertyNameCollection()
- )->shouldBeCalled();
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::any())->willReturn(
- new PropertyNameCollection(['name'])
- )->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::any())->willReturn(new PropertyNameCollection(['name']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', Argument::any())->willReturn(
- new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', false, true)
- )->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', Argument::any())->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', false, true));
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
- $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'name', 'Dummy')->shouldBeCalled();
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $serializerProphecy = $this->prophesize(SerializerInterface::class);
- $serializerProphecy->willImplement(DenormalizerInterface::class);
- $itemDataProviderProphecy = $this->prophesize(ItemDataProviderInterface::class);
- $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
- $resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata(
- 'dummy', '', '', null, null, ['input' => ['class' => InputDto::class]]
- ));
+ $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
- $jsonInput = ['foo' => 'f', 'bar' => 'b'];
+ $jsonInput = ['foo' => 'f', 'bar' => 8];
+ $inputDto = new InputDto();
+ $inputDto->foo = 'f';
+ $inputDto->bar = 8;
$transformed = new Dummy();
- $requestContext = [
+ $context = [
'operation_type' => 'collection',
'collection_operation_name' => 'post',
'resource_class' => Dummy::class,
@@ -1076,21 +1066,32 @@ public function testNormalizationWithDataTransformer()
'output' => ['class' => 'null'],
'api_denormalize' => true, // this is added by the normalizer
];
+ $cleanedContext = array_diff_key($context, [
+ 'input' => null,
+ 'resource_class' => null,
+ ]);
$secondJsonInput = ['name' => 'Dummy'];
$secondContext = ['api_denormalize' => true, 'resource_class' => Dummy::class];
$secondTransformed = new Dummy();
$secondTransformed->setName('Dummy');
+ $serializerProphecy = $this->prophesize(SerializerInterface::class);
+ $serializerProphecy->willImplement(DenormalizerInterface::class);
+ $serializerProphecy->denormalize($jsonInput, InputDto::class, 'jsonld', $cleanedContext)->willReturn($inputDto);
+
+ $itemDataProviderProphecy = $this->prophesize(ItemDataProviderInterface::class);
+
+ $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
+ $resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata('dummy', '', '', null, null, ['input' => ['class' => InputDto::class]]));
+
$dataTransformerProphecy = $this->prophesize(DataTransformerInterface::class);
- $dataTransformerProphecy->supportsTransformation($jsonInput, Dummy::class, $requestContext)->shouldBeCalled()->willReturn(true);
- $dataTransformerProphecy->supportsTransformation($secondJsonInput, Dummy::class, $secondContext)->shouldBeCalled()->willReturn(false);
- $dataTransformerProphecy->transform(Argument::that(function ($arg) {
- return $arg instanceof InputDto;
- }), Dummy::class, $requestContext)->shouldBeCalled()->willReturn($transformed);
+ $dataTransformerProphecy->supportsTransformation($jsonInput, Dummy::class, $context)->willReturn(true);
+ $dataTransformerProphecy->supportsTransformation($secondJsonInput, Dummy::class, $secondContext)->willReturn(false);
+ $dataTransformerProphecy->transform($inputDto, Dummy::class, $context)->willReturn($transformed);
$secondDataTransformerProphecy = $this->prophesize(DataTransformerInterface::class);
- $secondDataTransformerProphecy->supportsTransformation(Argument::any(), Dummy::class, Argument::any())->shouldBeCalled()->willReturn(false);
+ $secondDataTransformerProphecy->supportsTransformation(Argument::any(), Dummy::class, Argument::any())->willReturn(false);
$normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
$propertyNameCollectionFactoryProphecy->reveal(),
@@ -1110,8 +1111,13 @@ public function testNormalizationWithDataTransformer()
$normalizer->setSerializer($serializerProphecy->reveal());
// This is step 1-3, {InputDto} to Dummy
- $this->assertEquals($transformed, $normalizer->denormalize($jsonInput, Dummy::class, 'jsonld', $requestContext));
+ $this->assertEquals($transformed, $normalizer->denormalize($jsonInput, Dummy::class, 'jsonld', $context));
+
// Messenger sends {InputDto}
- $this->assertInstanceOf(Dummy::class, $normalizer->denormalize($secondJsonInput, Dummy::class, 'jsonld'));
+ $actualDummy = $normalizer->denormalize($secondJsonInput, Dummy::class, 'jsonld');
+
+ $this->assertInstanceOf(Dummy::class, $actualDummy);
+
+ $propertyAccessorProphecy->setValue($actualDummy, 'name', 'Dummy')->shouldHaveBeenCalled();
}
}
diff --git a/tests/Serializer/ItemNormalizerTest.php b/tests/Serializer/ItemNormalizerTest.php
index 9aa8b4e3efe..30eb6feb21b 100644
--- a/tests/Serializer/ItemNormalizerTest.php
+++ b/tests/Serializer/ItemNormalizerTest.php
@@ -50,8 +50,8 @@ public function testSupportNormalization()
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true)->shouldBeCalled();
- $resourceClassResolverProphecy->isResourceClass(\stdClass::class)->willReturn(false)->shouldBeCalled();
+ $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
+ $resourceClassResolverProphecy->isResourceClass(\stdClass::class)->willReturn(false);
$normalizer = new ItemNormalizer(
$propertyNameCollectionFactoryProphecy->reveal(),
@@ -77,21 +77,21 @@ public function testNormalize()
$propertyNameCollection = new PropertyNameCollection(['name']);
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
- $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn($propertyNameCollection)->shouldBeCalled();
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn($propertyNameCollection);
$propertyMetadata = new PropertyMetadata(null, null, true);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn($propertyMetadata)->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn($propertyMetadata);
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
- $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1')->shouldBeCalled();
+ $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1');
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
+ $resourceClassResolverProphecy->getResourceClass($dummy, null, false)->willReturn(Dummy::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
- $serializerProphecy->normalize('hello', null, Argument::type('array'))->willReturn('hello')->shouldBeCalled();
+ $serializerProphecy->normalize('hello', null, Argument::type('array'))->willReturn('hello');
$normalizer = new ItemNormalizer(
$propertyNameCollectionFactoryProphecy->reveal(),
@@ -127,6 +127,7 @@ public function testDenormalize()
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -166,6 +167,7 @@ public function testDenormalizeWithIri()
$iriConverterProphecy->getItemFromIri('/dummies/12', ['resource_class' => Dummy::class, 'api_allow_update' => true, 'fetch_data' => true])->shouldBeCalled();
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -241,6 +243,7 @@ public function testDenormalizeWithIdAndNoResourceClass()
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);