diff --git a/src/Bridge/Symfony/Bundle/Resources/config/api.xml b/src/Bridge/Symfony/Bundle/Resources/config/api.xml
index 2cd54de37bb..a50cdca7897 100644
--- a/src/Bridge/Symfony/Bundle/Resources/config/api.xml
+++ b/src/Bridge/Symfony/Bundle/Resources/config/api.xml
@@ -79,6 +79,7 @@
+
diff --git a/src/Bridge/Symfony/Bundle/Resources/config/hal.xml b/src/Bridge/Symfony/Bundle/Resources/config/hal.xml
index 9e6a3f5d358..340c1701340 100644
--- a/src/Bridge/Symfony/Bundle/Resources/config/hal.xml
+++ b/src/Bridge/Symfony/Bundle/Resources/config/hal.xml
@@ -34,6 +34,7 @@
+
diff --git a/src/Bridge/Symfony/Bundle/Resources/config/jsonld.xml b/src/Bridge/Symfony/Bundle/Resources/config/jsonld.xml
index 924d1b5760d..4c8a398777a 100644
--- a/src/Bridge/Symfony/Bundle/Resources/config/jsonld.xml
+++ b/src/Bridge/Symfony/Bundle/Resources/config/jsonld.xml
@@ -25,6 +25,7 @@
+
diff --git a/src/Bridge/Symfony/Routing/IriConverter.php b/src/Bridge/Symfony/Routing/IriConverter.php
index fd8aa5a40db..a60377e7ea5 100644
--- a/src/Bridge/Symfony/Routing/IriConverter.php
+++ b/src/Bridge/Symfony/Routing/IriConverter.php
@@ -57,7 +57,7 @@ public function __construct(PropertyNameCollectionFactoryInterface $propertyName
$this->propertyAccessor = $propertyAccessor ?? PropertyAccess::createPropertyAccessor();
if (null === $identifiersExtractor) {
- @trigger_error('Not injecting ItemIdentifiersExtractor is deprecated since API Platform 2.1 and will not be possible anymore in API Platform 3');
+ @trigger_error('Not injecting IdentifiersExtractorInterface is deprecated since API Platform 2.1 and will not be possible anymore in API Platform 3');
$this->identifiersExtractor = new IdentifiersExtractor($this->propertyNameCollectionFactory, $this->propertyMetadataFactory, $this->propertyAccessor);
} else {
$this->identifiersExtractor = $identifiersExtractor;
diff --git a/src/JsonLd/Serializer/ItemNormalizer.php b/src/JsonLd/Serializer/ItemNormalizer.php
index c096ae27faa..0a13e002701 100644
--- a/src/JsonLd/Serializer/ItemNormalizer.php
+++ b/src/JsonLd/Serializer/ItemNormalizer.php
@@ -13,10 +13,12 @@
namespace ApiPlatform\Core\JsonLd\Serializer;
+use ApiPlatform\Core\Api\IdentifiersExtractorInterface;
use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\Exception\InvalidArgumentException;
use ApiPlatform\Core\JsonLd\ContextBuilderInterface;
+use ApiPlatform\Core\JsonLd\Util\BlankNodeIdentifiersGenerator;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
@@ -40,13 +42,16 @@ final class ItemNormalizer extends AbstractItemNormalizer
private $resourceMetadataFactory;
private $contextBuilder;
+ private $blankNodeIdentifiersGenerator;
- public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, ContextBuilderInterface $contextBuilder, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null)
+ public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, ContextBuilderInterface $contextBuilder, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null, IdentifiersExtractorInterface $identifiersExtractor = null)
{
- parent::__construct($propertyNameCollectionFactory, $propertyMetadataFactory, $iriConverter, $resourceClassResolver, $propertyAccessor, $nameConverter, $classMetadataFactory);
+ parent::__construct($propertyNameCollectionFactory, $propertyMetadataFactory, $iriConverter, $resourceClassResolver, $propertyAccessor, $nameConverter, $classMetadataFactory, $identifiersExtractor);
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->contextBuilder = $contextBuilder;
+
+ $this->blankNodeIdentifiersGenerator = new BlankNodeIdentifiersGenerator();
}
/**
@@ -68,14 +73,20 @@ public function normalize($object, $format = null, array $context = [])
// Use resolved resource class instead of given resource class to support multiple inheritance child types
$context['resource_class'] = $resourceClass;
- $context['iri'] = $this->iriConverter->getIriFromItem($object);
+
+ $context = $this->addJsonLdDocumentContext($object, $context);
+
+ $jsonLdIdentifier = $this->getJsonLdNodeIdentifier($object, $context);
+ if ($this->hasIri($object)) {
+ $context['iri'] = $jsonLdIdentifier;
+ }
$rawData = parent::normalize($object, $format, $context);
if (!is_array($rawData)) {
return $rawData;
}
- $data['@id'] = $context['iri'];
+ $data['@id'] = $jsonLdIdentifier;
$data['@type'] = $resourceMetadata->getIri() ?: $resourceMetadata->getShortName();
return $data + $rawData;
@@ -96,6 +107,12 @@ public function supportsDenormalization($data, $type, $format = null)
*/
public function denormalize($data, $class, $format = null, array $context = [])
{
+ // Blank node identifiers cannot be used in denormalization
+ // Denormalize into new object
+ if (isset($data['@id']) && $this->isBlankNodeIdentifier($data['@id'])) {
+ unset($data['@id']);
+ }
+
// Avoid issues with proxies if we populated the object
if (isset($data['@id']) && !isset($context['object_to_populate'])) {
if (isset($context['api_allow_update']) && true !== $context['api_allow_update']) {
@@ -107,4 +124,44 @@ public function denormalize($data, $class, $format = null, array $context = [])
return parent::denormalize($data, $class, $format, $context);
}
+
+ /**
+ * Adds information related to the JSON-LD document to the serializer context.
+ *
+ * @param object $object
+ * @param array $context
+ *
+ * @return array
+ */
+ private function addJsonLdDocumentContext($object, array $context)
+ {
+ $context['jsonld_document_root'] ?? $context['jsonld_document_root'] = spl_object_hash($object);
+
+ return $context;
+ }
+
+ /**
+ * Gets the identifier for a JSON-LD node.
+ *
+ * @param object $object
+ * @param array $context
+ *
+ * @return string
+ */
+ private function getJsonLdNodeIdentifier($object, array $context): string
+ {
+ return $this->hasIri($object) ? $this->iriConverter->getIriFromItem($object) : $this->blankNodeIdentifiersGenerator->getBlankNodeIdentifier($object, $context['jsonld_document_root']);
+ }
+
+ /**
+ * Determines whether an IRI is a JSON-LD blank node identifier.
+ *
+ * @param string $iri
+ *
+ * @return bool
+ */
+ private function isBlankNodeIdentifier(string $iri): bool
+ {
+ return '_:' === substr($iri, 0, 2);
+ }
}
diff --git a/src/JsonLd/Util/BlankNodeIdentifiersGenerator.php b/src/JsonLd/Util/BlankNodeIdentifiersGenerator.php
new file mode 100644
index 00000000000..f3517c33dc4
--- /dev/null
+++ b/src/JsonLd/Util/BlankNodeIdentifiersGenerator.php
@@ -0,0 +1,51 @@
+
+ *
+ * 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\JsonLd\Util;
+
+/**
+ * Generates blank node identifiers scoped to each JSON-LD document.
+ *
+ * @author Teoh Han Hui
+ *
+ * @internal
+ */
+final class BlankNodeIdentifiersGenerator
+{
+ const IDENTIFIER_PREFIX = '_:b';
+
+ private $blankNodeCounts = [];
+ private $identifiers = [];
+
+ /**
+ * Gets a blank node identifier for an object, scoped to a JSON-LD document.
+ *
+ * @param object $object
+ * @param string $documentRootHash
+ *
+ * @return string
+ */
+ public function getBlankNodeIdentifier($object, string $documentRootHash): string
+ {
+ $objectHash = spl_object_hash($object);
+
+ if (!isset($this->identifiers[$documentRootHash][$objectHash])) {
+ $this->blankNodeCounts[$documentRootHash] ?? $this->blankNodeCounts[$documentRootHash] = 0;
+ $this->identifiers[$documentRootHash] ?? $this->identifiers[$documentRootHash] = [];
+
+ $this->identifiers[$documentRootHash][$objectHash] = sprintf('%s%d', self::IDENTIFIER_PREFIX, $this->blankNodeCounts[$documentRootHash]++);
+ }
+
+ return $this->identifiers[$documentRootHash][$objectHash];
+ }
+}
diff --git a/src/Serializer/AbstractItemNormalizer.php b/src/Serializer/AbstractItemNormalizer.php
index 1d46fe5b103..a48f169955c 100644
--- a/src/Serializer/AbstractItemNormalizer.php
+++ b/src/Serializer/AbstractItemNormalizer.php
@@ -13,6 +13,8 @@
namespace ApiPlatform\Core\Serializer;
+use ApiPlatform\Core\Api\IdentifiersExtractor;
+use ApiPlatform\Core\Api\IdentifiersExtractorInterface;
use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\Api\OperationType;
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
@@ -43,8 +45,9 @@ abstract class AbstractItemNormalizer extends AbstractObjectNormalizer
protected $iriConverter;
protected $resourceClassResolver;
protected $propertyAccessor;
+ protected $identifiersExtractor;
- public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null)
+ public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null, IdentifiersExtractorInterface $identifiersExtractor = null)
{
parent::__construct($classMetadataFactory, $nameConverter);
@@ -54,8 +57,15 @@ public function __construct(PropertyNameCollectionFactoryInterface $propertyName
$this->resourceClassResolver = $resourceClassResolver;
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
+ if (null === $identifiersExtractor) {
+ @trigger_error('Not injecting IdentifiersExtractorInterface is deprecated since API Platform 2.1 and will not be possible anymore in API Platform 3');
+ $this->identifiersExtractor = new IdentifiersExtractor($this->propertyNameCollectionFactory, $this->propertyMetadataFactory, $this->propertyAccessor);
+ } else {
+ $this->identifiersExtractor = $identifiersExtractor;
+ }
+
$this->setCircularReferenceHandler(function ($object) {
- return $this->iriConverter->getIriFromItem($object);
+ return $this->hasIri($object) ? $this->iriConverter->getIriFromItem($object) : spl_object_hash($object);
});
}
@@ -86,7 +96,7 @@ public function normalize($object, $format = null, array $context = [])
$context = $this->initContext($resourceClass, $context);
$context['api_normalize'] = true;
- if (isset($context['resources'])) {
+ if (isset($context['resources']) && $this->hasIri($object)) {
$resource = $context['iri'] ?? $this->iriConverter->getIriFromItem($object);
$context['resources'][$resource] = $resource;
}
@@ -421,7 +431,7 @@ protected function getAttributeValue($object, $attribute, $format = null, array
}
/**
- * Normalizes a relation as an URI if is a Link or as a JSON-LD object.
+ * Normalizes a relation.
*
* @param PropertyMetadata $propertyMetadata
* @param mixed $relatedObject
@@ -433,7 +443,7 @@ protected function getAttributeValue($object, $attribute, $format = null, array
*/
private function normalizeRelation(PropertyMetadata $propertyMetadata, $relatedObject, string $resourceClass, string $format = null, array $context)
{
- if ($propertyMetadata->isReadableLink()) {
+ if ($propertyMetadata->isReadableLink() || !$this->hasIri($relatedObject)) {
return $this->serializer->normalize($relatedObject, $format, $this->createRelationSerializationContext($resourceClass, $context));
}
@@ -444,4 +454,20 @@ private function normalizeRelation(PropertyMetadata $propertyMetadata, $relatedO
return $iri;
}
+
+ /**
+ * Determines whether an item has an IRI.
+ *
+ * @param object $object
+ *
+ * @return bool
+ *
+ * @internal
+ */
+ protected function hasIri($object): bool
+ {
+ $identifiers = $this->identifiersExtractor->getIdentifiersFromItem($object);
+
+ return (bool) array_filter($identifiers);
+ }
}
diff --git a/tests/Hal/Serializer/ItemNormalizerTest.php b/tests/Hal/Serializer/ItemNormalizerTest.php
index bb3cea8b279..acdd5445ecd 100644
--- a/tests/Hal/Serializer/ItemNormalizerTest.php
+++ b/tests/Hal/Serializer/ItemNormalizerTest.php
@@ -13,6 +13,7 @@
namespace ApiPlatform\Core\Tests\Hal\Serializer;
+use ApiPlatform\Core\Api\IdentifiersExtractorInterface;
use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\Exception\InvalidArgumentException;
@@ -34,18 +35,24 @@ class ItemNormalizerTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \ApiPlatform\Core\Exception\RuntimeException
*/
- public function testDonTSupportDenormalization()
+ public function testDontSupportDenormalization()
{
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$normalizer = new ItemNormalizer(
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
$iriConverterProphecy->reveal(),
- $resourceClassResolverProphecy->reveal()
+ $resourceClassResolverProphecy->reveal(),
+ null,
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal()
);
$this->assertFalse($normalizer->supportsDenormalization('foo', ItemNormalizer::FORMAT));
@@ -66,11 +73,17 @@ public function testSupportNormalization()
$resourceClassResolverProphecy->getResourceClass($dummy)->willReturn(Dummy::class)->shouldBeCalled();
$resourceClassResolverProphecy->getResourceClass($std)->willThrow(new InvalidArgumentException())->shouldBeCalled();
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$normalizer = new ItemNormalizer(
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
$iriConverterProphecy->reveal(),
- $resourceClassResolverProphecy->reveal()
+ $resourceClassResolverProphecy->reveal(),
+ null,
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal()
);
$this->assertTrue($normalizer->supportsNormalization($dummy, 'jsonhal'));
@@ -98,6 +111,9 @@ public function testNormalize()
$resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
$resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class, true)->willReturn(Dummy::class)->shouldBeCalled();
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+ $identifiersExtractorProphecy->getIdentifiersFromItem($dummy)->willReturn(['id']);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
$serializerProphecy->normalize('hello', null, Argument::type('array'))->willReturn('hello')->shouldBeCalled();
@@ -106,7 +122,11 @@ public function testNormalize()
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
$iriConverterProphecy->reveal(),
- $resourceClassResolverProphecy->reveal()
+ $resourceClassResolverProphecy->reveal(),
+ null,
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal()
);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -141,6 +161,9 @@ public function testNormalizeWithoutCache()
$resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
$resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class, true)->willReturn(Dummy::class)->shouldBeCalled();
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+ $identifiersExtractorProphecy->getIdentifiersFromItem($dummy)->willReturn(['id']);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
$serializerProphecy->normalize('hello', null, Argument::type('array'))->willReturn('hello')->shouldBeCalled();
@@ -149,7 +172,11 @@ public function testNormalizeWithoutCache()
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
$iriConverterProphecy->reveal(),
- $resourceClassResolverProphecy->reveal()
+ $resourceClassResolverProphecy->reveal(),
+ null,
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal()
);
$normalizer->setSerializer($serializerProphecy->reveal());
diff --git a/tests/Hydra/Serializer/ItemNormalizerTest.php b/tests/JsonLd/Serializer/ItemNormalizerTest.php
similarity index 76%
rename from tests/Hydra/Serializer/ItemNormalizerTest.php
rename to tests/JsonLd/Serializer/ItemNormalizerTest.php
index 14fa9e73162..0a235b3098f 100644
--- a/tests/Hydra/Serializer/ItemNormalizerTest.php
+++ b/tests/JsonLd/Serializer/ItemNormalizerTest.php
@@ -11,8 +11,9 @@
declare(strict_types=1);
-namespace ApiPlatform\Core\Tests\Hydra\Serializer;
+namespace ApiPlatform\Core\Tests\JsonLd\Serializer;
+use ApiPlatform\Core\Api\IdentifiersExtractorInterface;
use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\Exception\InvalidArgumentException;
@@ -44,9 +45,11 @@ public function testDontSupportDenormalization()
$contextBuilderProphecy = $this->prophesize(ContextBuilderInterface::class);
$resourceClassResolverProphecy->getResourceClass(['dummy'], 'Dummy')->willReturn(Dummy::class);
$propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['name' => 'name']));
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn(new PropertyMetadata())->shouldBeCalled(1);
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn(new PropertyMetadata());
- $normalizer = new ItemNormalizer($resourceMetadataFactoryProphecy->reveal(), $propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $contextBuilderProphecy->reveal());
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
+ $normalizer = new ItemNormalizer($resourceMetadataFactoryProphecy->reveal(), $propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $contextBuilderProphecy->reveal(), null, null, null, $identifiersExtractorProphecy->reveal());
$this->assertFalse($normalizer->supportsDenormalization('foo', ItemNormalizer::FORMAT));
$normalizer->denormalize(['foo'], Dummy::class, 'jsonld', ['jsonld_has_context' => true, 'jsonld_sub_level' => true, 'resource_class' => Dummy::class]);
@@ -65,17 +68,12 @@ public function testSupportNormalization()
$contextBuilderProphecy = $this->prophesize(ContextBuilderInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
- $resourceClassResolverProphecy->getResourceClass($dummy)->willReturn(Dummy::class)->shouldBeCalled();
- $resourceClassResolverProphecy->getResourceClass($std)->willThrow(new InvalidArgumentException())->shouldBeCalled();
-
- $normalizer = new ItemNormalizer(
- $resourceMetadataFactoryProphecy->reveal(),
- $propertyNameCollectionFactoryProphecy->reveal(),
- $propertyMetadataFactoryProphecy->reveal(),
- $iriConverterProphecy->reveal(),
- $resourceClassResolverProphecy->reveal(),
- $contextBuilderProphecy->reveal()
- );
+ $resourceClassResolverProphecy->getResourceClass($dummy)->willReturn(Dummy::class);
+ $resourceClassResolverProphecy->getResourceClass($std)->willThrow(new InvalidArgumentException());
+
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
+ $normalizer = new ItemNormalizer($resourceMetadataFactoryProphecy->reveal(), $propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $contextBuilderProphecy->reveal(), null, null, null, $identifiersExtractorProphecy->reveal());
$this->assertTrue($normalizer->supportsNormalization($dummy, 'jsonld'));
$this->assertFalse($normalizer->supportsNormalization($dummy, 'xml'));
@@ -91,33 +89,29 @@ 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);
- $propertyMetadataFactory = new PropertyMetadata(null, null, true);
+ $propertyMetadataProphecy = new PropertyMetadata(null, null, true);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
- $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn($propertyMetadataFactory)->shouldBeCalled();
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn($propertyMetadataProphecy);
$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, true)->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');
- $normalizer = new ItemNormalizer(
- $resourceMetadataFactoryProphecy->reveal(),
- $propertyNameCollectionFactoryProphecy->reveal(),
- $propertyMetadataFactoryProphecy->reveal(),
- $iriConverterProphecy->reveal(),
- $resourceClassResolverProphecy->reveal(),
- $contextBuilderProphecy->reveal()
- );
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+ $identifiersExtractorProphecy->getIdentifiersFromItem($dummy)->willReturn(['id']);
+
+ $normalizer = new ItemNormalizer($resourceMetadataFactoryProphecy->reveal(), $propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $contextBuilderProphecy->reveal(), null, null, null, $identifiersExtractorProphecy->reveal());
$normalizer->setSerializer($serializerProphecy->reveal());
$expected = ['@context' => '/contexts/Dummy',
diff --git a/tests/Serializer/AbstractItemNormalizerTest.php b/tests/Serializer/AbstractItemNormalizerTest.php
index ab23083538e..b814c4d5c49 100644
--- a/tests/Serializer/AbstractItemNormalizerTest.php
+++ b/tests/Serializer/AbstractItemNormalizerTest.php
@@ -13,6 +13,7 @@
namespace ApiPlatform\Core\Tests\Serializer;
+use ApiPlatform\Core\Api\IdentifiersExtractorInterface;
use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\Exception\InvalidArgumentException;
@@ -56,12 +57,17 @@ public function testSupportNormalizationAndSupportDenormalization()
$resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true)->shouldBeCalled();
$resourceClassResolverProphecy->isResourceClass(\stdClass::class)->willReturn(false)->shouldBeCalled();
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
$iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal(),
$propertyAccessorProphecy->reveal(),
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal(),
]);
$this->assertTrue($normalizer->supportsNormalization($dummy));
@@ -126,6 +132,10 @@ public function testNormalize()
$resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
$resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(RelatedDummy::class)->shouldBeCalled();
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+ $identifiersExtractorProphecy->getIdentifiersFromItem($dummy)->willReturn(['id']);
+ $identifiersExtractorProphecy->getIdentifiersFromItem($relatedDummy)->willReturn(['id']);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
$serializerProphecy->normalize('foo', null, Argument::type('array'))->willReturn('foo')->shouldBeCalled();
@@ -137,6 +147,9 @@ public function testNormalize()
$iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal(),
$propertyAccessorProphecy->reveal(),
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal(),
]);
$normalizer->setSerializer($serializerProphecy->reveal());
$normalizer->setIgnoredAttributes(['alias']);
@@ -193,6 +206,10 @@ public function testNormalizeReadableLinks()
$resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
$resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(RelatedDummy::class)->shouldBeCalled();
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+ $identifiersExtractorProphecy->getIdentifiersFromItem($dummy)->willReturn(['id']);
+ $identifiersExtractorProphecy->getIdentifiersFromItem($relatedDummy)->willReturn(['id']);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
$serializerProphecy->normalize($relatedDummy, null, Argument::type('array'))->willReturn(['foo' => 'hello'])->shouldBeCalled();
@@ -205,6 +222,9 @@ public function testNormalizeReadableLinks()
$iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal(),
$propertyAccessorProphecy->reveal(),
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal(),
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -266,6 +286,8 @@ public function testDenormalize()
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
@@ -275,6 +297,9 @@ public function testDenormalize()
$iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal(),
$propertyAccessorProphecy->reveal(),
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal(),
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -332,6 +357,8 @@ public function testDenormalizeWritableLinks()
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
$resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true)->shouldBeCalled();
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
$serializerProphecy->denormalize(
@@ -347,6 +374,9 @@ public function testDenormalizeWritableLinks()
$iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal(),
$propertyAccessorProphecy->reveal(),
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal(),
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -386,6 +416,8 @@ public function testBadRelationType()
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
$resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true)->shouldBeCalled();
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -395,6 +427,9 @@ public function testBadRelationType()
$iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal(),
$propertyAccessorProphecy->reveal(),
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal(),
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -430,6 +465,8 @@ public function testInnerDocumentNotAllowed()
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
$resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true)->shouldBeCalled();
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -439,6 +476,9 @@ public function testInnerDocumentNotAllowed()
$iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal(),
$propertyAccessorProphecy->reveal(),
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal(),
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -465,6 +505,8 @@ public function testBadType()
$propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -474,6 +516,9 @@ public function testBadType()
$iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal(),
$propertyAccessorProphecy->reveal(),
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal(),
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -497,6 +542,8 @@ public function testJsonAllowIntAsFloat()
$propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'foo', 42)->shouldBeCalled();
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -506,6 +553,9 @@ public function testJsonAllowIntAsFloat()
$iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal(),
$propertyAccessorProphecy->reveal(),
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal(),
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -547,6 +597,8 @@ public function testDenormalizeBadKeyType()
$propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -556,6 +608,9 @@ public function testDenormalizeBadKeyType()
$iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal(),
$propertyAccessorProphecy->reveal(),
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal(),
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -582,6 +637,8 @@ public function testNullable()
$propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -591,6 +648,9 @@ public function testNullable()
$iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal(),
$propertyAccessorProphecy->reveal(),
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal(),
]);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -625,6 +685,9 @@ public function testChildInheritedProperty()
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
$resourceClassResolverProphecy->getResourceClass($dummy, DummyTableInheritance::class, true)->willReturn(DummyTableInheritance::class)->shouldBeCalled();
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+ $identifiersExtractorProphecy->getIdentifiersFromItem($dummy)->willReturn(['id']);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
$serializerProphecy->normalize('foo', null, Argument::type('array'))->willReturn('foo')->shouldBeCalled();
@@ -636,6 +699,9 @@ public function testChildInheritedProperty()
$iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal(),
$propertyAccessorProphecy->reveal(),
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal(),
]);
$normalizer->setSerializer($serializerProphecy->reveal());
diff --git a/tests/Serializer/ItemNormalizerTest.php b/tests/Serializer/ItemNormalizerTest.php
index d7b06de224d..4e01c192b2a 100644
--- a/tests/Serializer/ItemNormalizerTest.php
+++ b/tests/Serializer/ItemNormalizerTest.php
@@ -13,6 +13,7 @@
namespace ApiPlatform\Core\Tests\Serializer;
+use ApiPlatform\Core\Api\IdentifiersExtractorInterface;
use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\Exception\InvalidArgumentException;
@@ -48,11 +49,17 @@ public function testSupportNormalization()
$resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true)->shouldBeCalled();
$resourceClassResolverProphecy->isResourceClass(\stdClass::class)->willReturn(false)->shouldBeCalled();
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$normalizer = new ItemNormalizer(
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
$iriConverterProphecy->reveal(),
- $resourceClassResolverProphecy->reveal()
+ $resourceClassResolverProphecy->reveal(),
+ null,
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal()
);
$this->assertTrue($normalizer->supportsNormalization($dummy));
@@ -83,6 +90,9 @@ public function testNormalize()
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
$resourceClassResolverProphecy->getResourceClass($dummy, null, true)->willReturn(Dummy::class)->shouldBeCalled();
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+ $identifiersExtractorProphecy->getIdentifiersFromItem($dummy)->willReturn(['id']);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(NormalizerInterface::class);
$serializerProphecy->normalize('hello', null, Argument::type('array'))->willReturn('hello')->shouldBeCalled();
@@ -91,7 +101,11 @@ public function testNormalize()
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
$iriConverterProphecy->reveal(),
- $resourceClassResolverProphecy->reveal()
+ $resourceClassResolverProphecy->reveal(),
+ null,
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal()
);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -114,6 +128,8 @@ public function testDenormalize()
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -121,7 +137,11 @@ public function testDenormalize()
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
$iriConverterProphecy->reveal(),
- $resourceClassResolverProphecy->reveal()
+ $resourceClassResolverProphecy->reveal(),
+ null,
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal()
);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -145,6 +165,8 @@ public function testDenormalizeWithIri()
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -152,7 +174,11 @@ public function testDenormalizeWithIri()
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
$iriConverterProphecy->reveal(),
- $resourceClassResolverProphecy->reveal()
+ $resourceClassResolverProphecy->reveal(),
+ null,
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal()
);
$normalizer->setSerializer($serializerProphecy->reveal());
@@ -175,6 +201,8 @@ public function testDenormalizeWithIdAndUpdateNotAllowed()
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);
+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
@@ -182,7 +210,11 @@ public function testDenormalizeWithIdAndUpdateNotAllowed()
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
$iriConverterProphecy->reveal(),
- $resourceClassResolverProphecy->reveal()
+ $resourceClassResolverProphecy->reveal(),
+ null,
+ null,
+ null,
+ $identifiersExtractorProphecy->reveal()
);
$normalizer->setSerializer($serializerProphecy->reveal());
$normalizer->denormalize(['id' => '12', 'name' => 'hello'], Dummy::class, null, $context);