Hi,
Not a bug probably but I just can't wrap my head around this one. I've read like 50 issues about embedded objects but nothing seems to solve my issue.
I have an @ORM\Embeddable that's used quite a lot in other real resources. Serialization/deserialization seem to work just fine. But the swagger documentation is giving me headache.
I can of course do something like:
/*
* @ORM\Embedded(class="App\Entity\Id")
* @ApiProperty(
* attributes={
* "swagger_context"={
* "type"="object",
* "properties"= {
* "type" = "int",
* "code" = "string",
* },
* "example"={"type":1, "code":"1234567-1"}
* }
* }
* )
*
* /
private $embeddedThing;
But that seems really counterproductive when I have multiple users for the embeddable. I just wouldn't like to maintain it like this.
Couple of solution possibilities comes to mind. The optimal solution would be to pull the embedded entity as a swagger schema automatically and referencing it in the parent.
I could live with something like this:
* @ApiProperty(
* attributes={
* "swagger_context"={
* "$ref"="#/definitions/Id"
* }
* }
* )
But I just can't seem to figure out how to make API Platform create that model for my Id embeddable.
Any ideas how to approach this? Can I somehow force the model creation from Embeddables? Any workaround other than maintaining the embeddable documentation manually?
Just an additional note. I tried also to define the Embeddable as ApiResources. If I give even one dummy operation the model gets generated on the documentation properly. If I remove all the operations it won't include the model.
Embedding the element gives:
Nested documents for attribute "xyz" are not allowed. Use IRIs instead.
I think there was a workaround using serialization groups but I'm afraid this road is doomed to fail in the end.
Update: Using ApiResource annotation in embeddables is definitely not the way to go. Gives all sort of problems. So I'll return to my original question.
I could define the reference on embedding side with swagger_context and using "$ref"="#/definitions/MyEmbeddable" directly. That's not too much trouble. Although it could be automated if ever implemented in the documentation generator.
But is there any way to force the generation of models from Embeddables? Everything else works just fine but the documentation is off and I don't want to manage it manually using swagger_context everywhere on the embedding resource.
Any ideas on how to approach this?
I stumbled on the same problem.
Do you know how to put custom definitions into the swagger doc ?
For the autogen feature :
I narrowed it down to DocumentationNormalizer::getType which will fallback to "string" if the classname is not recognized as a resource.
My idea :
ApiProperty annotation to mark embeddable objects for the doc generator ;DocumentationNormalizer to handle it (?)Any progress on this?
I ended up with an internal solution, I can't share the code for now, but here is the idea :
@ApiEmbeddedResource annotation which mirror the @ApiResource annotation ;ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface which decorate the original api_platform.metadata.resource.metadata_factory service & concat the ApiEmbeddedResource tagged entities to the resourceNameCollection.resourceNameCollection.Hope it helps.
FYI, v2.4.* breaks everything I done. And I don't see a way to work around it anymore.
My solution is for those who dare stuck to v2.3.6 ^^'
Why did it break @Cethy ?
@soyuka the changes made in Swagger/Serializer/DocumentationNormalizer::getDefinition seem to be the culprits. I will try to investigate more or post something you could check next week.
But I was led to believe the Embeddables were now supported, am I wrong ?
@paali
Hi, I guess I found one way to walk it through, but first we must know why$ref wasn't working.The $ref directive is used to refer to an existing object definition in swagger objects definitions. api-platform generates an object definition (for swagger) for each resource (annotated with @ApiResource) but not for you embeddable entity. By typing $ docker-compose exec php bin/console api:openapi:export --spec-version=3 you obtain a dump of all the definitions used by swagger in your application and you will see no definition for your embeddable entity.
So what should be done is to create an object definition for you embeddable entity first, and then refer to it from the embedding entities.
Let say:
First the embedded entity
class Embedded
{
private $field;
}
The Swagger decorator service
Then you create a swagger decorator to add the definition of that object thanks to
<?php
// api/src/Swagger/SwaggerDecorator.php
namespace App\Swagger;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
final class SwaggerDecorator implements NormalizerInterface
{
private $decorated;
public function __construct(NormalizerInterface $decorated)
{
$this->decorated = $decorated;
}
public function normalize($object, $format = null, array $context = [])
{
$docs = $this->decorated->normalize($object, $format, $context);
$embeddedDefinition = [
"type" => "object",
"description" => "This is an Embedded entiy",
"properties" =>[
"field" => [
"type" => "string",
"description" => "The best field in the world",
"example" => "foo"
]
]
];
// add the emmbedDefinition under the key 'Embedded'
$docs['definitions']['Embedded'] = $embeddedDefinition;
return $docs;
}
public function supportsNormalization($data, $format = null)
{
return $this->decorated->supportsNormalization($data, $format);
}
}
Registering the service
# api/config/services.yaml
services:
App\Swagger\SwaggerDecorator:
decorates: 'api_platform.swagger.normalizer.documentation'
arguments: [ '@App\Swagger\SwaggerDecorator.inner' ]
autoconfigure: false
Finally refering to the embeddable definition from the embedding resource
@ApiResource
class Embedding
{
* @ApiProperty(
* attributes={
* "swagger_context"={
* "$ref": "#/definitions/Embedded"
* }
* }
* )
private $embedded
}
Done
@sorel-kui , while being documented and reasonable solution, it seems requiring more work to do than simply define all properties straight inside "swagger_context".
Hi,
for Information, I have the same problem than @paali with MongoDB and I've applied the solution given by @sorel-kui without defining Embedding as a ApiResource.
example for MongoDB :
class Embedding
{
/**
* @ODM\EmbedMany(targetDocument="Document")
* @ApiProperty(swaggerContext={
* "type": "array",
* "items": {
* "$ref": "#/definitions/Document-write"
* }
* })
*/
private $embedMany;
/**
* @ODM\EmbedOne(targetDocument="Document")
* @ApiProperty(swaggerContext={
* "type": "object",
* "$ref": "#/definitions/Document-write"
* })
*/
private $embedOne;
}
Could you give https://github.com/api-platform/core/pull/3309 a try? And let me know if you encounter some issues with that and/or what's still missing.
Most helpful comment
Update: Using ApiResource annotation in embeddables is definitely not the way to go. Gives all sort of problems. So I'll return to my original question.
I could define the reference on embedding side with swagger_context and using
"$ref"="#/definitions/MyEmbeddable"directly. That's not too much trouble. Although it could be automated if ever implemented in the documentation generator.But is there any way to force the generation of models from Embeddables? Everything else works just fine but the documentation is off and I don't want to manage it manually using swagger_context everywhere on the embedding resource.
Any ideas on how to approach this?