From 9261f14e3107942550999854078c17d00e9afdf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sun, 1 Mar 2015 20:32:13 +0100 Subject: [PATCH 1/2] Change directory structure --- Controller/ResourceController.php | 2 +- Doctrine/Orm/DataManipulator.php | 4 +- Event/ObjectEvent.php | 2 +- .../ApiDocumentationBuilder.php | 7 +-- .../ContextBuilder.php | 2 +- .../EntrypointBuilder.php | 4 +- Resource.php => JsonLd/Resource.php | 2 +- Resources.php => JsonLd/Resources.php | 4 +- Model/DataManipulatorInterface.php | 2 +- README.md | 12 ++-- Resources/config/api.xml | 8 +-- Routing/ResourcesLoader.php | 2 +- Serializer/JsonLdNormalizer.php | 8 +-- composer.json | 2 +- features/fixtures/TestApp/config/config.yml | 4 +- .../Serializer/JsonLdNormalizerSpec.php | 57 +++++++++++++++++++ 16 files changed, 88 insertions(+), 34 deletions(-) rename ApiDocumentationBuilder.php => JsonLd/ApiDocumentationBuilder.php (98%) rename ContextBuilder.php => JsonLd/ContextBuilder.php (98%) rename EntrypointBuilder.php => JsonLd/EntrypointBuilder.php (94%) rename Resource.php => JsonLd/Resource.php (99%) rename Resources.php => JsonLd/Resources.php (97%) create mode 100644 spec/Dunglas/JsonLdApiBundle/Serializer/JsonLdNormalizerSpec.php diff --git a/Controller/ResourceController.php b/Controller/ResourceController.php index 3203dda6a90..80c194f3be9 100644 --- a/Controller/ResourceController.php +++ b/Controller/ResourceController.php @@ -14,7 +14,7 @@ use Doctrine\ORM\Tools\Pagination\Paginator; use Dunglas\JsonLdApiBundle\Event\Events; use Dunglas\JsonLdApiBundle\Event\ObjectEvent; -use Dunglas\JsonLdApiBundle\Resource; +use Dunglas\JsonLdApiBundle\JsonLd\Resource; use Dunglas\JsonLdApiBundle\Response\JsonLdResponse; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; diff --git a/Doctrine/Orm/DataManipulator.php b/Doctrine/Orm/DataManipulator.php index d09186a5586..2c04c9c4694 100644 --- a/Doctrine/Orm/DataManipulator.php +++ b/Doctrine/Orm/DataManipulator.php @@ -14,8 +14,8 @@ use Doctrine\Common\Persistence\ManagerRegistry; use Doctrine\ORM\Tools\Pagination\Paginator; use Dunglas\JsonLdApiBundle\Model\DataManipulatorInterface; -use Dunglas\JsonLdApiBundle\Resource; -use Dunglas\JsonLdApiBundle\Resources; +use Dunglas\JsonLdApiBundle\JsonLd\Resource; +use Dunglas\JsonLdApiBundle\JsonLd\Resources; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\RouterInterface; diff --git a/Event/ObjectEvent.php b/Event/ObjectEvent.php index 09e59a9cdf8..c1087906f91 100644 --- a/Event/ObjectEvent.php +++ b/Event/ObjectEvent.php @@ -11,7 +11,7 @@ namespace Dunglas\JsonLdApiBundle\Event; -use Dunglas\JsonLdApiBundle\Resource; +use Dunglas\JsonLdApiBundle\JsonLd\Resource; use Symfony\Component\EventDispatcher\Event; /** diff --git a/ApiDocumentationBuilder.php b/JsonLd/ApiDocumentationBuilder.php similarity index 98% rename from ApiDocumentationBuilder.php rename to JsonLd/ApiDocumentationBuilder.php index 90a77a3a8ba..5bea8275b05 100644 --- a/ApiDocumentationBuilder.php +++ b/JsonLd/ApiDocumentationBuilder.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Dunglas\JsonLdApiBundle; +namespace Dunglas\JsonLdApiBundle\JsonLd; use Dunglas\JsonLdApiBundle\Mapping\ClassMetadataFactory; use Symfony\Component\Routing\RouterInterface; @@ -21,11 +21,6 @@ */ class ApiDocumentationBuilder { - /** - * @var string - */ - const HYDRA_NS = 'http://www.w3.org/ns/hydra/core#'; - /** * @var Resources */ diff --git a/ContextBuilder.php b/JsonLd/ContextBuilder.php similarity index 98% rename from ContextBuilder.php rename to JsonLd/ContextBuilder.php index c12382ef92d..88c4805f043 100644 --- a/ContextBuilder.php +++ b/JsonLd/ContextBuilder.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Dunglas\JsonLdApiBundle; +namespace Dunglas\JsonLdApiBundle\JsonLd; use Dunglas\JsonLdApiBundle\Mapping\ClassMetadataFactory; use Symfony\Component\Routing\RouterInterface; diff --git a/EntrypointBuilder.php b/JsonLd/EntrypointBuilder.php similarity index 94% rename from EntrypointBuilder.php rename to JsonLd/EntrypointBuilder.php index 17b1516fa58..3b255e24a2c 100644 --- a/EntrypointBuilder.php +++ b/JsonLd/EntrypointBuilder.php @@ -9,12 +9,12 @@ * file that was distributed with this source code. */ -namespace Dunglas\JsonLdApiBundle; +namespace Dunglas\JsonLdApiBundle\JsonLd; use Symfony\Component\Routing\RouterInterface; /** - * Hydra's Entrypoint builder. + * API Entrypoint builder. * * @author Kévin Dunglas */ diff --git a/Resource.php b/JsonLd/Resource.php similarity index 99% rename from Resource.php rename to JsonLd/Resource.php index ef838bc4090..4064f4e735a 100644 --- a/Resource.php +++ b/JsonLd/Resource.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Dunglas\JsonLdApiBundle; +namespace Dunglas\JsonLdApiBundle\JsonLd; use Doctrine\Common\Inflector\Inflector; use Symfony\Component\Routing\Route; diff --git a/Resources.php b/JsonLd/Resources.php similarity index 97% rename from Resources.php rename to JsonLd/Resources.php index cc33c405863..a9a54fa11cf 100644 --- a/Resources.php +++ b/JsonLd/Resources.php @@ -9,7 +9,9 @@ * file that was distributed with this source code. */ -namespace Dunglas\JsonLdApiBundle; +namespace Dunglas\JsonLdApiBundle\JsonLd; + +use ArrayObject; /** * A collection of {@see Resource} classes. diff --git a/Model/DataManipulatorInterface.php b/Model/DataManipulatorInterface.php index cdd5f4c04c4..3a3b8271c79 100644 --- a/Model/DataManipulatorInterface.php +++ b/Model/DataManipulatorInterface.php @@ -11,7 +11,7 @@ namespace Dunglas\JsonLdApiBundle\Model; -use Dunglas\JsonLdApiBundle\Resource; +use Dunglas\JsonLdApiBundle\JsonLd\Resource; /** * Manipulates data. diff --git a/README.md b/README.md index 50bc8562afb..ddae11bd288 100644 --- a/README.md +++ b/README.md @@ -156,12 +156,12 @@ Register the following services (for example in `app/config/services.yml`): ```yaml services: "resource.product": - class: "Dunglas\JsonLdApiBundle\Resource" + class: "Dunglas\JsonLdApiBundle\JsonLd\Resource" arguments: [ "AppBundle\Entity\Product" ] tags: [ { name: "json-ld.resource" } ] "resource.offer": - class: "Dunglas\JsonLdApiBundle\Resource" + class: "Dunglas\JsonLdApiBundle\JsonLd\Resource" arguments: [ "AppBundle\Entity\Offer" ] tags: [ { name: "json-ld.resource" } ] ``` @@ -187,7 +187,7 @@ To allow filtering the list of offers: ```yaml services: "resource.offer": - class: "Dunglas\JsonLdApiBundle\Resource" + class: "Dunglas\JsonLdApiBundle\JsonLd\Resource" arguments: - "AppBundle\Entity\Offer" - @@ -206,7 +206,7 @@ It also possible to filter by relations: ```yaml services: "resource.offer": - class: "Dunglas\JsonLdApiBundle\Resource" + class: "Dunglas\JsonLdApiBundle\JsonLd\Resource" arguments: - "AppBundle\Entity\Offer" - @@ -228,7 +228,7 @@ in the Serializer component. Specifying to the API system the groups to use is d ```yaml services: "resource.product": - class: "Dunglas\JsonLdApiBundle\Resource" + class: "Dunglas\JsonLdApiBundle\JsonLd\Resource" arguments: [ "AppBundle\Entity\Product", ~, [ "serialization_group1", "serialization_group2" ], [ "deserialization_group1", "deserialization_group2" ] ] tags: [ { name: "json-ld.resource" } ] ``` @@ -245,7 +245,7 @@ No problem. Edit your service declaration and add groups you want to use when th ```yaml services: "resource.product": - class: "Dunglas\JsonLdApiBundle\Resource" + class: "Dunglas\JsonLdApiBundle\JsonLd\Resource" arguments: [ "AppBundle\Entity\Product", ~, ~, ~, [ "group1", "group2" ] ] tags: [ { name: "json-ld.resource" } ] ``` diff --git a/Resources/config/api.xml b/Resources/config/api.xml index bfa4d6b024b..f53da907199 100644 --- a/Resources/config/api.xml +++ b/Resources/config/api.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + @@ -13,12 +13,12 @@ - + - + @@ -52,7 +52,7 @@ - + diff --git a/Routing/ResourcesLoader.php b/Routing/ResourcesLoader.php index 36b3fc3f979..83d9ab0f955 100644 --- a/Routing/ResourcesLoader.php +++ b/Routing/ResourcesLoader.php @@ -11,7 +11,7 @@ namespace Dunglas\JsonLdApiBundle\Routing; -use Dunglas\JsonLdApiBundle\Resources; +use Dunglas\JsonLdApiBundle\JsonLd\Resources; use Symfony\Component\Config\Loader\Loader; use Symfony\Component\Routing\RouteCollection; diff --git a/Serializer/JsonLdNormalizer.php b/Serializer/JsonLdNormalizer.php index 8d69a230266..58bc63ea6fd 100644 --- a/Serializer/JsonLdNormalizer.php +++ b/Serializer/JsonLdNormalizer.php @@ -13,8 +13,8 @@ use Doctrine\ORM\Tools\Pagination\Paginator; use Dunglas\JsonLdApiBundle\Model\DataManipulatorInterface; -use Dunglas\JsonLdApiBundle\Resource; -use Dunglas\JsonLdApiBundle\Resources; +use Dunglas\JsonLdApiBundle\JsonLd\Resource; +use Dunglas\JsonLdApiBundle\JsonLd\Resources; use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; @@ -212,7 +212,7 @@ public function denormalize($data, $class, $format = null, array $context = []) { $resource = $this->guessResource($data, $context); - $allowedAttributes = $this->getAllowedAttributes($class, $context); + $allowedAttributes = $this->getAllowedAttributes($class, $context, true); $normalizedData = $this->prepareForDenormalization($data); $reflectionClass = new \ReflectionClass($class); @@ -263,7 +263,7 @@ public function denormalize($data, $class, $format = null, array $context = []) * @param mixed $type * @param array|null $context * - * @return \Dunglas\JsonLdApiBundle\Resource + * @return \Dunglas\JsonLdApiBundle\JsonLd\Resource * * @throws InvalidArgumentException */ diff --git a/composer.json b/composer.json index e8f1290e626..12930cbbce5 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ ], "require": { "php": ">=5.5", - "symfony/symfony": "dev-new_serializer as 2.7.0", + "symfony/symfony": "dev-jsonld_ready as 2.7.0", "doctrine/orm": "~2.2,>=2.2.3", "doctrine/doctrine-bundle": "~1.2", "dunglas/php-property-info": "~0.2", diff --git a/features/fixtures/TestApp/config/config.yml b/features/fixtures/TestApp/config/config.yml index 678843e31a8..cb7bdca4d47 100644 --- a/features/fixtures/TestApp/config/config.yml +++ b/features/fixtures/TestApp/config/config.yml @@ -30,7 +30,7 @@ dunglas_json_ld_api: services: my_dummy_resource: - class: Dunglas\JsonLdApiBundle\Resource + class: Dunglas\JsonLdApiBundle\JsonLd\Resource arguments: - Dunglas\JsonLdApiBundle\Tests\Behat\TestBundle\Entity\Dummy - [ { name: "id" }, { name: "name", "exact": false } ] @@ -38,7 +38,7 @@ services: - { name: json-ld.resource } my_related_dummy_resource: - class: Dunglas\JsonLdApiBundle\Resource + class: Dunglas\JsonLdApiBundle\JsonLd\Resource arguments: - Dunglas\JsonLdApiBundle\Tests\Behat\TestBundle\Entity\RelatedDummy tags: diff --git a/spec/Dunglas/JsonLdApiBundle/Serializer/JsonLdNormalizerSpec.php b/spec/Dunglas/JsonLdApiBundle/Serializer/JsonLdNormalizerSpec.php new file mode 100644 index 00000000000..18664c3fa3c --- /dev/null +++ b/spec/Dunglas/JsonLdApiBundle/Serializer/JsonLdNormalizerSpec.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + + +namespace spec\Dunglas\JsonLdApiBundle\Serializer; + +use Dunglas\JsonLdApiBundle\Mapping\ClassMetadata; +use Dunglas\JsonLdApiBundle\Mapping\ClassMetadataFactory; +use Dunglas\JsonLdApiBundle\Model\DataManipulatorInterface; +use Dunglas\JsonLdApiBundle\JsonLd\Resource; +use Dunglas\JsonLdApiBundle\JsonLd\Resources; +use PhpSpec\ObjectBehavior; +use Prophecy\Argument; +use Symfony\Component\Routing\RouterInterface; + +/** + * @author Kévin Dunglas + */ +class JsonLdNormalizerSpec extends ObjectBehavior +{ + function let( + Resources $resources, + RouterInterface $router, + DataManipulatorInterface $dataManipulator, + ClassMetadataFactory $classMetadataFactory, + ClassMetadata $classMetadata) + { + $this->beConstructedWith($resources, $router, $dataManipulator, $classMetadataFactory); + } + + function it_is_initializable() + { + $this->shouldHaveType('Dunglas\JsonLdApiBundle\Serializer\JsonLdNormalizer'); + $this->shouldImplement('Symfony\Component\Serializer\Normalizer\NormalizerInterface'); + $this->shouldImplement('Symfony\Component\Serializer\Normalizer\DenormalizerInterface'); + } + + /*function it_ignores_non_existing_attributes(Resource $resource) + { + $this + ->denormalize(array('nonExisting' => true), __NAMESPACE__.'\Dummy', null, array('resource' => $resource)) + ->willReturn(new Dummy()) + ; + }*/ +} + +class Dummy +{ +} From 29d9c7756b8615cd89baf4088e2f4cdd3a4ce070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Tue, 3 Mar 2015 00:20:41 +0100 Subject: [PATCH 2/2] Enhanced metadata support --- JsonLd/ApiDocumentationBuilder.php | 33 +- JsonLd/ContextBuilder.php | 9 +- Mapping/AttributeMetadata.php | 204 +++++++++++ Mapping/ClassMetadata.php | 329 +++--------------- Mapping/ClassMetadataFactory.php | 252 ++++++++++++-- README.md | 3 +- Resources/config/api.xml | 1 - Serializer/JsonLdNormalizer.php | 72 ++-- composer.json | 2 +- .../Serializer/JsonLdNormalizerSpec.php | 6 +- 10 files changed, 549 insertions(+), 362 deletions(-) create mode 100644 Mapping/AttributeMetadata.php diff --git a/JsonLd/ApiDocumentationBuilder.php b/JsonLd/ApiDocumentationBuilder.php index 5bea8275b05..ab4835634e8 100644 --- a/JsonLd/ApiDocumentationBuilder.php +++ b/JsonLd/ApiDocumentationBuilder.php @@ -188,7 +188,12 @@ public function getApiDocumentation() // Resources foreach ($this->resources as $resource) { - $metadata = $this->classMetadataFactory->getMetadataFor($resource->getEntityClass()); + $classMetadata = $this->classMetadataFactory->getMetadataFor( + $resource->getEntityClass(), + $resource->getNormalizationGroups(), + $resource->getDenormalizationGroups(), + $resource->getValidationGroups() + ); $shortName = $resource->getShortName(); $supportedClass = [ @@ -197,35 +202,31 @@ public function getApiDocumentation() 'hydra:title' => $resource->getShortName(), ]; - $description = $metadata->getDescription(); + $description = $classMetadata->getDescription(); if ($description) { $supportedClass['hydra:description'] = $description; } - $attributes = $metadata->getAttributes( - $resource->getNormalizationGroups(), - $resource->getDenormalizationGroups(), - $resource->getValidationGroups() - ); + $attributes = $classMetadata->getAttributes(); $supportedClass['hydra:supportedProperty'] = []; - foreach ($attributes as $name => $details) { + foreach ($attributes as $attributeName => $attribute) { $supportedProperty = [ '@type' => 'hydra:SupportedProperty', 'hydra:property' => [ - '@id' => sprintf('%s/%s', $shortName, $name), + '@id' => sprintf('%s/%s', $shortName, $attributeName), '@type' => 'rdf:Property', - 'rdfs:label' => $name, + 'rdfs:label' => $attributeName, 'domain' => $shortName, ], - 'hydra:title' => $name, - 'hydra:required' => $details['required'], - 'hydra:readable' => $details['readable'], - 'hydra:writable' => $details['writable'], + 'hydra:title' => $attributeName, + 'hydra:required' => $attribute->isRequired(), + 'hydra:readable' => $attribute->isReadable(), + 'hydra:writable' => $attribute->isWritable(), ]; - if ($details['description']) { - $supportedProperty['hydra:description'] = $details['description']; + if ($description = $attribute->getDescription()) { + $supportedProperty['hydra:description'] = $description; } $supportedClass['hydra:supportedProperty'][] = $supportedProperty; diff --git a/JsonLd/ContextBuilder.php b/JsonLd/ContextBuilder.php index 88c4805f043..ab186563591 100644 --- a/JsonLd/ContextBuilder.php +++ b/JsonLd/ContextBuilder.php @@ -60,14 +60,15 @@ public function buildContext(Resource $resource = null) ]; if ($resource) { - $attributes = $this->classMetadataFactory->getMetadataFor($resource->getEntityClass())->getAttributes( + $attributes = $this->classMetadataFactory->getMetadataFor( + $resource->getEntityClass(), $resource->getNormalizationGroups(), $resource->getDenormalizationGroups(), $resource->getValidationGroups() - ); + )->getAttributes(); - foreach ($attributes as $attributeName => $data) { - if ($data['type']) { + foreach ($attributes as $attributeName => $attribute) { + if (isset($attribute->getTypes()[0]) && 'object' === $attribute->getTypes()[0]->getType()) { $context[$attributeName] = ['@type' => '@id']; } } diff --git a/Mapping/AttributeMetadata.php b/Mapping/AttributeMetadata.php new file mode 100644 index 00000000000..a97fed9dc57 --- /dev/null +++ b/Mapping/AttributeMetadata.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Dunglas\JsonLdApiBundle\Mapping; + +/** + * AttributeMetadata. + * + * @author Kévin Dunglas + */ +class AttributeMetadata +{ + /** + * @var string + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link getName()} instead. + */ + public $name; + /** + * @var \PropertyInfo\Type[] + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link getTypes()} instead. + */ + public $types; + /** + * @var string + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link getDescription()} instead. + */ + public $description; + /** + * @var bool + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link isReadable()} instead. + */ + public $readable = false; + /** + * @var bool + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link isWritable()} instead. + */ + public $writable = false; + /** + * @var bool + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link isRequired()} instead. + */ + public $required = false; + + /** + * @param string $name + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * Gets name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set types. + * + * @param \PropertyInfo\Type[] $types + */ + public function setTypes(array $types) + { + $this->types = $types; + } + + /** + * Gets types. + * + * @return \PropertyInfo\Type[] + */ + public function getTypes() + { + return $this->types; + } + + /** + * Gets description. + * + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets description. + * + * @param string $description + */ + public function setDescription($description) + { + $this->description = $description; + } + + /** + * Is readable? + * + * @return bool + */ + public function isReadable() + { + return $this->readable; + } + + /** + * Sets readable. + * + * @param bool $readable + */ + public function setReadable($readable) + { + $this->readable = $readable; + } + + /** + * Is writable? + * + * @return boolean + */ + public function isWritable() + { + return $this->writable; + } + + /** + * Sets writable. + * + * @param boolean $writable + */ + public function setWritable($writable) + { + $this->writable = $writable; + } + + /** + * Is required? + * + * @return boolean + */ + public function isRequired() + { + return $this->required; + } + + /** + * Sets required. + * + * @param boolean $required + */ + public function setRequired($required) + { + $this->required = $required; + } + + /** + * Returns the names of the properties that should be serialized. + * + * @return string[] + */ + public function __sleep() + { + return [ + 'name', + 'types', + 'description', + 'readable', + 'writable', + 'required', + ]; + } +} diff --git a/Mapping/ClassMetadata.php b/Mapping/ClassMetadata.php index b8641ad1fec..4c2fd5c6b01 100644 --- a/Mapping/ClassMetadata.php +++ b/Mapping/ClassMetadata.php @@ -11,32 +11,14 @@ namespace Dunglas\JsonLdApiBundle\Mapping; -use Doctrine\Common\Persistence\Mapping\ClassMetadata as DoctrineClassMetadata; -use phpDocumentor\Reflection\DocBlock; -use phpDocumentor\Reflection\FileReflector; -use PropertyInfo\PropertyInfoInterface; -use Symfony\Component\Serializer\Mapping\ClassMetadata as SerializerClassMetadata; -use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\Mapping\ClassMetadataInterface as ValidatorClassMetadata; +/** + * ClassMetadata. + * + * @author Kévin Dunglas + */ class ClassMetadata { - /** - * @var string[] A list of constraint classes making the entity required. - */ - public static $requiredConstraints = [ - 'Symfony\Component\Validator\Constraints\NotBlank', - 'Symfony\Component\Validator\Constraints\NotNull', - ]; - /** - * @var FileReflector[] - */ - private static $fileReflectors = []; - - /** - * @var PropertyInfoInterface - */ - private $propertyInfo; /** * @var string * @@ -54,316 +36,113 @@ class ClassMetadata */ public $description; /** - * @var array + * @var AttributeMetadata[] * * @internal This property is public in order to reduce the size of the * class' serialized representation. Do not access it. Use * {@link getAttributes()} instead. */ public $attributes; - /** - * @var SerializerClassMetadata|null - * - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. - */ - public $serializerClassMetadata; - /** - * @var ValidatorClassMetadata|null - * - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. - */ - public $validatorClassMetadata; - /** - * @var DoctrineClassMetadataInterface|null - * - * @internal This property is public in order to reduce the size of the - * class' serialized representation. Do not access it. - */ - public $doctrineClassMetadata; /** * @var \ReflectionClass */ - private $reflClass; - /** - * @var \phpDocumentor\Reflection\ClassReflector - */ - private $classReflector; - /** - * @var DocBlock - */ - private $docBlock; + private $reflectionClass; /** * Constructs a metadata for the given class. * - * @param string $class - * @param PropertyInfoInterface $propertyInfo - * @param SerializerClassMetadata|null $serializerClassMetadata - * @param ValidatorClassMetadata|null $validatorClassMetadata - * @param DoctrineClassMetadata|null $doctrineClassMetadata + * @param string $name */ - public function __construct( - $class, - PropertyInfoInterface $propertyInfo, - SerializerClassMetadata $serializerClassMetadata = null, - ValidatorClassMetadata $validatorClassMetadata = null, - DoctrineClassMetadata $doctrineClassMetadata = null - ) { - $this->name = $class; - $this->propertyInfo = $propertyInfo; - $this->serializerClassMetadata = $serializerClassMetadata; - $this->validatorClassMetadata = $validatorClassMetadata; - $this->doctrineClassMetadata = $doctrineClassMetadata; + public function __construct($name) + { + $this->name = $name; } /** - * Gets the description of this class coming from the PHPDoc. + * Gets the class name. * * @return string */ - public function getDescription() + public function getName() { - if (null === $this->description) { - $this->description = $this->getClassReflector()->getDocBlock()->getShortDescription(); - } - - return $this->description; + return $this->name; } /** - * Gets relevant properties for the given groups. + * Sets description. * - * @param string[] $normalizationGroups - * @param string[] $denormalizationGroups - * @param string[] $validationGroups - * - * @return array + * @param string $description */ - public function getAttributes( - array $normalizationGroups = null, - array $denormalizationGroups = null, - array $validationGroups = null - ) { - $key = serialize([$normalizationGroups, $denormalizationGroups, $validationGroups]); - if (isset($this->attributes[$key])) { - return $this->attributes[$key]; - } - - $attributes = []; - if ($this->serializerClassMetadata && null !== $normalizationGroups && null !== $denormalizationGroups) { - foreach ($this->serializerClassMetadata->getAttributesGroups() as $group => $normalizationAttributes) { - if (in_array($group, $normalizationGroups)) { - foreach ($normalizationAttributes as $attributeName) { - $attributes[$attributeName]['readable'] = true; - } - } - - if (in_array($group, $denormalizationGroups)) { - foreach ($normalizationAttributes as $attributeName) { - $attributes[$attributeName]['writable'] = true; - } - } - - if (isset($attributeName)) { - $this->populateAttribute($attributeName, $attributes[$attributeName], $validationGroups); - unset($attributeName); - } - } - } else { - $reflClass = $this->getReflectionClass(); - - // methods - foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflMethod) { - $methodName = $reflMethod->name; - $numberOfRequiredParameters = $reflMethod->getNumberOfRequiredParameters(); - - // setters - if (1 === $numberOfRequiredParameters && strpos($methodName, 'set') === 0) { - $attributes[lcfirst(substr($methodName, 3))]['writable'] = true; - - continue; - } - - if (0 !== $numberOfRequiredParameters) { - continue; - } - - // getters and hassers - if (strpos($methodName, 'get') === 0 || strpos($methodName, 'has') === 0) { - $attributes[lcfirst(substr($methodName, 3))]['readable'] = true; - - continue; - } - - // issers - if (strpos($methodName, 'is') === 0) { - $attributes[lcfirst(substr($methodName, 2))]['readable'] = true; - } - } - - // properties - foreach ($reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $reflProperty) { - $attributes[$reflProperty->name] = [ - 'readable' => true, - 'writable' => true, - ]; - } - - foreach ($attributes as $attributeName => &$data) { - $this->populateAttribute($attributeName, $data, $validationGroups); - } - } - - return $this->attributes[$key] = $attributes; + public function setDescription($description) + { + $this->description = $description; } /** - * Returns a ReflectionClass instance for this class. + * Gets the description. * - * @return \ReflectionClass + * @return string */ - public function getReflectionClass() + public function getDescription() { - if (!$this->reflClass) { - if ($this->serializerClassMetadata) { - return $this->reflClass = $this->serializerClassMetadata->getReflectionClass(); - } - - if ($this->doctrineClassMetadata) { - return $this->reflClass = $this->doctrineClassMetadata->getReflectionClass(); - } - - return $this->reflClass = new \ReflectionClass($this->getClassName()); - } - - return $this->reflClass; + return $this->description; } /** - * Returns the names of the properties that should be serialized. + * Adds an {@link AttributeMetadata}. * - * @return string[] + * @param AttributeMetadata $attributeMetadata */ - public function __sleep() + public function addAttribute(AttributeMetadata $attributeMetadata) { - return [ - 'name', - 'description', - 'attributes', - 'serializerClassMetadata', - 'validatorClassMetadata', - 'doctrineClassMetadata', - ]; + $this->attributes[$attributeMetadata->getName()] = $attributeMetadata; } /** - * Populates an attribute. + * Gets attributes. * - * @param string $name - * @param array $attribute - * @param array|null $validationGroups + * @return AttributeMetadata[] */ - private function populateAttribute($name, array &$attribute, array $validationGroups = null) + public function getAttributes() { - if (!isset($attribute['readable'])) { - $attribute['readable'] = false; - } - - if (!isset($attribute['writable'])) { - $attribute['writable'] = false; - } - - foreach ($this->validatorClassMetadata->getPropertyMetadata($name) as $propertyMetadata) { - if (null === $validationGroups) { - foreach ($propertyMetadata->findConstraints($this->validatorClassMetadata->getDefaultGroup()) as $constraint) { - if ($this->isRequired($constraint)) { - $attribute['required'] = true; - - break 2; - } - } - } else { - foreach ($validationGroups as $validationGroup) { - foreach ($propertyMetadata->findConstraints($validationGroup) as $constraint) { - if ($this->isRequired($constraint)) { - $attribute['required'] = true; - - break 3; - } - } - } - } - } - - if (!isset($attribute['required'])) { - $attribute['required'] = false; - } - - if ($this->doctrineClassMetadata && $this->doctrineClassMetadata->hasAssociation($name)) { - $attribute['type'] = $this->doctrineClassMetadata->getAssociationTargetClass($name); - } else { - $attribute['type'] = null; - } - - $reflClass = $this->getReflectionClass(); - if ($reflClass->hasProperty($name)) { - $attribute['description'] = $this->propertyInfo->getShortDescription($reflClass->getProperty($name)); - } + return $this->attributes; } /** - * Is this constraint making the related property required? - * - * @param Constraint $constraint + * Sets {@see \ReflectionClass}. * - * @return bool + * @param \ReflectionClass $reflectionClass */ - private function isRequired(Constraint $constraint) + public function setReflectionClass(\ReflectionClass $reflectionClass) { - foreach (self::$requiredConstraints as $requiredConstraint) { - if ($constraint instanceof $requiredConstraint) { - return true; - } - } - - return false; + $this->reflectionClass = $reflectionClass; } /** - * Gets the ClassReflector associated with this class. + * Returns a {@see \ReflectionClass} instance for this class. * - * @return \phpDocumentor\Reflection\ClassReflector + * @return \ReflectionClass */ - private function getClassReflector() + public function getReflectionClass() { - if ($this->classReflector) { - return $this->classReflector; + if (!$this->reflectionClass) { + return $this->reflectionClass = new \ReflectionClass($this->name); } - $reflectionClass = $this->getReflectionClass(); - - if (!($fileName = $reflectionClass->getFileName())) { - return; - } - - if (isset(self::$fileReflectors[$fileName])) { - $fileReflector = self::$fileReflectors[$fileName]; - } else { - $fileReflector = self::$fileReflectors[$fileName] = new FileReflector($fileName); - $fileReflector->process(); - } - - foreach ($fileReflector->getClasses() as $classReflector) { - $className = $classReflector->getName(); - if ('\\' === $className[0]) { - $className = substr($className, 1); - } + return $this->reflectionClass; + } - if ($className === $reflectionClass->getName()) { - return $this->classReflector = $classReflector; - } - } + /** + * Returns the names of the properties that should be serialized. + * + * @return string[] + */ + public function __sleep() + { + return [ + 'name', + 'description', + 'attributes', + ]; } } diff --git a/Mapping/ClassMetadataFactory.php b/Mapping/ClassMetadataFactory.php index d7840f0ea5a..6d541815c92 100644 --- a/Mapping/ClassMetadataFactory.php +++ b/Mapping/ClassMetadataFactory.php @@ -12,10 +12,12 @@ namespace Dunglas\JsonLdApiBundle\Mapping; use Doctrine\Common\Cache\Cache; -use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory as DoctrineClassMetadataFactory; +use phpDocumentor\Reflection\FileReflector; use PropertyInfo\PropertyInfoInterface; +use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface as SerializerClassMetadataFactory; +use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface as ValidatorMetadataFactory; -use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory as SerializerClassMetadataFactory; /** * ClassMetadata Factory for the JSON-LD normalizer. @@ -26,6 +28,21 @@ */ class ClassMetadataFactory { + /** + * @var string[] A list of constraint classes making the entity required. + */ + public static $requiredConstraints = [ + 'Symfony\Component\Validator\Constraints\NotBlank', + 'Symfony\Component\Validator\Constraints\NotNull', + ]; + /** + * @var FileReflector[] + */ + private static $fileReflectors = []; + /** + * @var ClassReflector[] + */ + private static $classReflectors = []; /** * @var PropertyInfoInterface */ @@ -38,10 +55,6 @@ class ClassMetadataFactory * @var SerializerClassMetadataFactory|null */ private $serializerClassMetadataFactory; - /** - * @var DoctrineClassMetadataFactory|null - */ - private $doctrineClassMetadataFactory; /** * @var Cache|null */ @@ -55,13 +68,11 @@ public function __construct( PropertyInfoInterface $propertyInfo, ValidatorMetadataFactory $validatorMetadataFactory = null, SerializerClassMetadataFactory $serializerClassMetadataFactory = null, - DoctrineClassMetadataFactory $doctrineClassMetadataFactory = null, Cache $cache = null ) { $this->propertyInfo = $propertyInfo; $this->validatorMetadataFactory = $validatorMetadataFactory; $this->serializerClassMetadataFactory = $serializerClassMetadataFactory; - $this->doctrineClassMetadataFactory = $doctrineClassMetadataFactory; $this->cache = $cache; } @@ -84,36 +95,55 @@ public function __construct( * * @throws \InvalidArgumentException */ - public function getMetadataFor($value) - { + public function getMetadataFor( + $value, + array $normalizationGroups = null, + array $denormalizationGroups = null, + array $validationGroups = null + ) { $class = $this->getClass($value); if (!$class) { throw new \InvalidArgumentException(sprintf('Cannot create metadata for non-objects. Got: %s', gettype($value))); } - if (isset($this->loadedClasses[$class])) { - return $this->loadedClasses[$class]; + $classKey = serialize([$class, $normalizationGroups, $denormalizationGroups, $validationGroups]); + + if (isset($this->loadedClasses[$classKey])) { + return $this->loadedClasses[$classKey]; } - if ($this->cache && ($this->loadedClasses[$class] = $this->cache->fetch($class))) { - return $this->loadedClasses[$class]; + if ($this->cache && ($this->loadedClasses[$classKey] = $this->cache->fetch($classKey))) { + return $this->loadedClasses[$classKey]; } if (!class_exists($class) && !interface_exists($class)) { throw new \InvalidArgumentException(sprintf('The class or interface "%s" does not exist.', $class)); } - $serializerMetadata = $this->serializerClassMetadataFactory ? $this->serializerClassMetadataFactory->getMetadataFor($class) : null; - $validatorMetadata = $this->validatorMetadataFactory ? $this->validatorMetadataFactory->getMetadataFor($class) : null; - $doctrineMetadata = $this->doctrineClassMetadataFactory ? $this->doctrineClassMetadataFactory->getMetadataFor($class) : null; + $classMetadata = new ClassMetadata($class); - $metadata = new ClassMetadata($class, $this->propertyInfo, $serializerMetadata, $validatorMetadata, $doctrineMetadata); + $serializerClassMetadata = $this->serializerClassMetadataFactory ? $this->serializerClassMetadataFactory->getMetadataFor($class) : null; + if ($serializerClassMetadata) { + $classMetadata->setReflectionClass($serializerClassMetadata->getReflectionClass()); + } + + if ($classReflector = $this->getClassReflector($classMetadata->getReflectionClass())) { + $classMetadata->setDescription($classReflector->getDocBlock()->getShortDescription()); + } + + $this->loadAttributes( + $classMetadata, + $serializerClassMetadata, + $normalizationGroups, + $denormalizationGroups, + $validationGroups + ); if ($this->cache) { - $this->cache->save($class, $metadata); + $this->cache->save($classKey, $classMetadata); } - return $this->loadedClasses[$class] = $metadata; + return $this->loadedClasses[$classKey] = $classMetadata; } /** @@ -131,19 +161,191 @@ public function hasMetadataFor($value) } /** - * Gets Serializer's ClassMetadataFactory. + * Gets relevant properties for the given groups. + * + * @param string[] $normalizationGroups + * @param string[] $denormalizationGroups + * @param string[] $validationGroups + */ + private function loadAttributes( + ClassMetadata $classMetadata, + ClassMetadataInterface $serializerClassMetadata = null, + array $normalizationGroups = null, + array $denormalizationGroups = null, + array $validationGroups = null + ) { + if ($serializerClassMetadata && null !== $normalizationGroups && null !== $denormalizationGroups) { + foreach ($serializerClassMetadata->getAttributesMetadata() as $normalizationAttribute) { + if (count(array_intersect($normalizationAttribute->getGroups(), $normalizationGroups))) { + $attribute = $this->getOrCreateAttribute($classMetadata, $normalizationAttribute->getName(), $validationGroups); + $attribute->setReadable(true); + } + + if (count(array_intersect($normalizationAttribute->getGroups(), $denormalizationGroups))) { + $attribute = $this->getOrCreateAttribute($classMetadata, $normalizationAttribute->getName(), $validationGroups); + $attribute->setWritable(true); + } + } + } else { + $reflectionClass = $classMetadata->getReflectionClass(); + + // methods + foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { + $methodName = $reflectionMethod->name; + $numberOfRequiredParameters = $reflectionMethod->getNumberOfRequiredParameters(); + + // setters + if (1 === $numberOfRequiredParameters && strpos($methodName, 'set') === 0) { + $attribute = $this->getOrCreateAttribute($classMetadata, lcfirst(substr($methodName, 3)), $validationGroups); + $attribute->setWritable(true); + + continue; + } + + if (0 !== $numberOfRequiredParameters) { + continue; + } + + // getters and hassers + if (strpos($methodName, 'get') === 0 || strpos($methodName, 'has') === 0) { + $attribute = $this->getOrCreateAttribute($classMetadata, lcfirst(substr($methodName, 3)), $validationGroups); + $attribute->setReadable(true); + + continue; + } + + // issers + if (strpos($methodName, 'is') === 0) { + $attribute = $this->getOrCreateAttribute($classMetadata, lcfirst(substr($methodName, 2)), $validationGroups); + $attribute->setReadable(true); + } + } + + // properties + foreach ($reflectionClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $reflectionProperty) { + $attribute = $this->getOrCreateAttribute($classMetadata, $reflectionProperty->name, $validationGroups); + $attribute->setReadable(true); + $attribute->setWritable(true); + } + } + } + + /** + * Gets or creates the {@see AttributeMetadata} of the given name. + * + * @param ClassMetadata $classMetadata + * @param string $attributeName + * @param string[]|null $validationGroups * - * @return SerializerClassMetadataFactory|null + * @return AttributeMetadata */ - public function getSerializerClassMetadataFactory() + private function getOrCreateAttribute(ClassMetadata $classMetadata, $attributeName, array $validationGroups = null) { - return $this->serializerClassMetadataFactory; + if (isset($classMetadata->getAttributes()[$attributeName])) { + return $classMetadata->getAttributes()[$attributeName]; + } + + $attribute = new AttributeMetadata($attributeName); + $reflectionClass = $classMetadata->getReflectionClass(); + + if ($reflectionClass->hasProperty($attributeName)) { + $reflectionProperty = $reflectionClass->getProperty($attributeName); + + $attribute->setDescription($this->propertyInfo->getShortDescription($reflectionProperty)); + $attribute->setTypes($this->propertyInfo->getTypes($reflectionProperty)); + } + + if ($this->validatorMetadataFactory) { + $validatorClassMetadata = $this->validatorMetadataFactory->getMetadataFor($classMetadata->getName()); + + foreach ($validatorClassMetadata->getPropertyMetadata($attributeName) as $propertyMetadata) { + if (null === $validationGroups) { + foreach ($propertyMetadata->findConstraints($validatorClassMetadata->getDefaultGroup()) as $constraint) { + if ($this->isRequired($constraint)) { + $attribute->setRequired(true); + + break 2; + } + } + } else { + foreach ($validationGroups as $validationGroup) { + foreach ($propertyMetadata->findConstraints($validationGroup) as $constraint) { + if ($this->isRequired($constraint)) { + $attribute->setRequired(true); + + break 3; + } + } + } + } + } + } + + $classMetadata->addAttribute($attribute); + + return $attribute; + } + + /** + * Is this constraint making the related property required? + * + * @param Constraint $constraint + * + * @return bool + */ + private function isRequired(Constraint $constraint) + { + foreach (self::$requiredConstraints as $requiredConstraint) { + if ($constraint instanceof $requiredConstraint) { + return true; + } + } + + return false; + } + + /** + * Gets the ClassReflector associated with this class. + * + * @param \ReflectionClass $reflectionClass + * + * @return \phpDocumentor\Reflection\ClassReflector|null + */ + private function getClassReflector(\ReflectionClass $reflectionClass) + { + $className = $reflectionClass->getName(); + + if (isset(self::$classReflectors[$className])) { + return self::$classReflectors[$className]; + } + + if (!($fileName = $reflectionClass->getFileName())) { + return; + } + + if (isset(self::$fileReflectors[$fileName])) { + $fileReflector = self::$fileReflectors[$fileName]; + } else { + $fileReflector = self::$fileReflectors[$fileName] = new FileReflector($fileName); + $fileReflector->process(); + } + + foreach ($fileReflector->getClasses() as $classReflector) { + $className = $classReflector->getName(); + if ('\\' === $className[0]) { + $className = substr($className, 1); + } + + if ($className === $reflectionClass->getName()) { + return self::$classReflectors[$className] = $classReflector; + } + } } /** * Gets a class name for a given class or instance. * - * @param $value + * @param mixed $value * * @return string|bool */ diff --git a/README.md b/README.md index ddae11bd288..1853720e722 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **JSON-LD + Hydra REST API generator for Symfony** This a work in progress under active development. -This bundle *is not usable in production yet*. +This bundle rely on Symfony 2.7 and *is not usable in production yet*. [![Build Status](https://travis-ci.org/dunglas/DunglasJsonLdApiBundle.svg)](https://travis-ci.org/dunglas/DunglasJsonLdApiBundle) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/a93f5a40-483f-4c46-ba09-3e1033b62552/mini.png)](https://insight.sensiolabs.com/projects/a93f5a40-483f-4c46-ba09-3e1033b62552) @@ -29,7 +29,6 @@ This bundle is documented and tested with Behat (take a look at [the `features/` ## Installation **This bundle relies heavily on features that will be introduced in Symfony 2.7.** -To test it now, you must use an experimental Symfony branch, see https://github.com/symfony/symfony/pull/13257#issuecomment-68943401 Use [Composer](http://getcomposer.org) to install the bundle: diff --git a/Resources/config/api.xml b/Resources/config/api.xml index f53da907199..790e9d9d8c3 100644 --- a/Resources/config/api.xml +++ b/Resources/config/api.xml @@ -49,7 +49,6 @@ - diff --git a/Serializer/JsonLdNormalizer.php b/Serializer/JsonLdNormalizer.php index 58bc63ea6fd..0b3b0f0598c 100644 --- a/Serializer/JsonLdNormalizer.php +++ b/Serializer/JsonLdNormalizer.php @@ -65,10 +65,7 @@ public function __construct( NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null ) { - parent::__construct( - $jsonLdClassMetadataFactory ? $jsonLdClassMetadataFactory->getSerializerClassMetadataFactory() : null, - $nameConverter - ); + parent::__construct(null, $nameConverter); $this->resources = $resources; $this->router = $router; @@ -167,20 +164,29 @@ public function normalize($object, $format = null, array $context = []) ); $data['@type'] = $resource->getShortName(); - foreach ($this->getAttributes($resource, $object) as $attribute => $details) { - if ($details['readable'] && 'id' !== $attribute) { - $attributeValue = $this->propertyAccessor->getValue($object, $attribute); + $attributes = $this->jsonLdClassMetadataFactory->getMetadataFor( + $resource->getEntityClass(), + $resource->getNormalizationGroups(), + $resource->getDenormalizationGroups(), + $resource->getValidationGroups() + )->getAttributes(); - if ($details['type']) { - if (is_array($attributeValue) || $attributeValue instanceof \Traversable) { + foreach ($attributes as $attributeName => $attribute) { + if ($attribute->isReadable() && 'id' !== $attributeName) { + $attributeValue = $this->propertyAccessor->getValue($object, $attributeName); + + if (isset($attribute->getTypes()[0])) { + $type = $attribute->getTypes()[0]; + + if ($type->isCollection()) { $uris = []; foreach ($attributeValue as $obj) { - $uris[] = $this->dataManipulator->getUriFromObject($obj, $details['type']); + $uris[] = $this->dataManipulator->getUriFromObject($obj, $type->getCollectionType()->getClass()); } $attributeValue = $uris; } elseif (is_object($attributeValue)) { - $attributeValue = $this->dataManipulator->getUriFromObject($attributeValue, $details['type']); + $attributeValue = $this->dataManipulator->getUriFromObject($attributeValue, $type->getClass()); } } @@ -188,7 +194,7 @@ public function normalize($object, $format = null, array $context = []) $attributeValue = $attributeValue->format(\DateTime::ATOM); } - $data[$attribute] = $this->serializer->normalize($attributeValue, 'json', $context); + $data[$attributeName] = $this->serializer->normalize($attributeValue, 'json', $context); } } @@ -211,13 +217,24 @@ public function supportsDenormalization($data, $type, $format = null) public function denormalize($data, $class, $format = null, array $context = []) { $resource = $this->guessResource($data, $context); - - $allowedAttributes = $this->getAllowedAttributes($class, $context, true); $normalizedData = $this->prepareForDenormalization($data); + $attributes = $this->jsonLdClassMetadataFactory->getMetadataFor( + $class, + $resource->getNormalizationGroups(), + $resource->getDenormalizationGroups(), + $resource->getValidationGroups() + )->getAttributes(); + + $allowedAttributes = []; + foreach ($attributes as $attributeName => $attribute) { + if ($attribute->isReadable()) { + $allowedAttributes[] = $attributeName; + } + } + $reflectionClass = new \ReflectionClass($class); $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes); - $attributes = $this->getAttributes($resource, $object); foreach ($normalizedData as $attribute => $value) { // Ignore JSON-LD special attributes @@ -233,8 +250,12 @@ public function denormalize($data, $class, $format = null, array $context = []) $attribute = $this->nameConverter->denormalize($attribute); } - if (isset($attributes[$attribute]['type']) && !is_null($value)) { - if (is_array($value)) { + if ( + isset($attributes[$attribute]->getTypes()[0]) && + !is_null($value) && + 'object' === $attributes[$attribute]->getTypes()[0]->getType() + ) { + if ($attributes[$attribute]->getTypes()[0]->isCollection()) { $collection = []; foreach ($value as $uri) { $collection[] = $this->dataManipulator->getObjectFromUri($uri); @@ -289,21 +310,4 @@ private function guessResource($type, array $context = null) sprintf('Cannot find a resource object for type "%s".', $type) ); } - - /** - * Gets attributes. - * - * @param Resource $resource - * @param object $object - * - * @return array - */ - private function getAttributes(Resource $resource, $object) - { - return $this->jsonLdClassMetadataFactory->getMetadataFor($object)->getAttributes( - $resource->getNormalizationGroups(), - $resource->getDenormalizationGroups(), - $resource->getValidationGroups() - ); - } } diff --git a/composer.json b/composer.json index 12930cbbce5..e8f1290e626 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ ], "require": { "php": ">=5.5", - "symfony/symfony": "dev-jsonld_ready as 2.7.0", + "symfony/symfony": "dev-new_serializer as 2.7.0", "doctrine/orm": "~2.2,>=2.2.3", "doctrine/doctrine-bundle": "~1.2", "dunglas/php-property-info": "~0.2", diff --git a/spec/Dunglas/JsonLdApiBundle/Serializer/JsonLdNormalizerSpec.php b/spec/Dunglas/JsonLdApiBundle/Serializer/JsonLdNormalizerSpec.php index 18664c3fa3c..42824b1de2d 100644 --- a/spec/Dunglas/JsonLdApiBundle/Serializer/JsonLdNormalizerSpec.php +++ b/spec/Dunglas/JsonLdApiBundle/Serializer/JsonLdNormalizerSpec.php @@ -9,7 +9,6 @@ * file that was distributed with this source code. */ - namespace spec\Dunglas\JsonLdApiBundle\Serializer; use Dunglas\JsonLdApiBundle\Mapping\ClassMetadata; @@ -18,7 +17,6 @@ use Dunglas\JsonLdApiBundle\JsonLd\Resource; use Dunglas\JsonLdApiBundle\JsonLd\Resources; use PhpSpec\ObjectBehavior; -use Prophecy\Argument; use Symfony\Component\Routing\RouterInterface; /** @@ -26,7 +24,7 @@ */ class JsonLdNormalizerSpec extends ObjectBehavior { - function let( + public function let( Resources $resources, RouterInterface $router, DataManipulatorInterface $dataManipulator, @@ -36,7 +34,7 @@ function let( $this->beConstructedWith($resources, $router, $dataManipulator, $classMetadataFactory); } - function it_is_initializable() + public function it_is_initializable() { $this->shouldHaveType('Dunglas\JsonLdApiBundle\Serializer\JsonLdNormalizer'); $this->shouldImplement('Symfony\Component\Serializer\Normalizer\NormalizerInterface');