Generation doc for nested DTO objects
See original GitHub issueHello. I am using DTO for GET methods. All work fine except documentation. I have 3 levels of nested objects (relations). And for nested objects in swagger describe like simple string. For example I have 3 DTO:
declare(strict_types=1);
namespace App\API\Resource\DTO\Quiz;
use ApiPlatform\Core\Annotation\ApiProperty;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
final class QuizStepDTO
{
/**
* @Groups({"quizStep:read"})
* @Assert\NotNull()
*
* @var int
*/
private $id;
/**
* @Groups({"quizStep:read"})
* @Assert\NotNull()
*
* @var string
*/
private $title;
/**
* @Groups({"quizStep:read"})
* @Assert\NotNull()
*
* @var QuizStepChoiceDTO[]
*/
private $quizStepChoices;
public function getId(): int
{
return $this->id;
}
/**
* @param int $id
* @return QuizStepDTO
*/
public function setId(int $id): QuizStepDTO
{
$this->id = $id;
return $this;
}
public function getTitle(): string
{
return $this->title;
}
/**
* @param string $title
* @return QuizStepDTO
*/
public function setTitle(string $title): QuizStepDTO
{
$this->title = $title;
return $this;
}
public function getQuizStepChoices(): array
{
return $this->quizStepChoices;
}
/**
* @param QuizStepChoiceDTO[] $quizStepChoices
* @return QuizStepDTO
*/
public function setQuizStepChoices(array $quizStepChoices): QuizStepDTO
{
$this->quizStepChoices = $quizStepChoices;
return $this;
}
}```
QuizStepChoiceDTO definition:
```php
namespace App\API\Resource\DTO\Quiz;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
final class QuizStepChoiceDTO
{
/**
* @Groups({"quizStep:read"})
* @Assert\NotNull()
*
* @var int
*/
private $position;
/**
* @Groups({"quizStep:read"})
* @Assert\NotNull()
*
* @var ChoiceDTO
*/
private $choice;
public function getPosition(): int
{
return $this->position;
}
/**
* @param int $position
* @return QuizStepChoiceDTO
*/
public function setPosition(int $position): QuizStepChoiceDTO
{
$this->position = $position;
return $this;
}
public function getChoice(): ChoiceDTO
{
return $this->choice;
}
/**
* @param ChoiceDTO $choice
* @return QuizStepChoiceDTO
*/
public function setChoice(ChoiceDTO $choice): QuizStepChoiceDTO
{
$this->choice = $choice;
return $this;
}
and ChoiceDTO
namespace App\API\Resource\DTO\Quiz;
use ApiPlatform\Core\Annotation\ApiProperty;
use App\API\Resource\DTO\Meal\IngredientAliasDTO;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
final class ChoiceDTO
{
/**
* @Groups({"choice:read", "quizStep:read"})
* @Assert\NotNull()
*
* @param int
*/
private $id;
/**
* @Groups({"choice:read", "quizStep:read"})
* @Assert\NotNull()
*
* @param string
*/
private $title;
/**
*
* @Groups({"choice:read", "quizStep:read"})
*
* @ApiProperty(
* attributes={
* "swagger_context"={
* "type"="integer",
* "enum" = {1,2},
* }
* }
* )
*
* @var int|null
*/
private $measurementUnit;
/**
* @Groups({"choice:read", "quizStep:read"})
*
* @ApiProperty(
* attributes={
* "swagger_context"={
* "type"="object",
* "required" = {"id","name"},
* "properties"={
* "id"={"type":"integer"},
* "name"={"type":"string"}
* }
* }
* }
* )
*
*
* @var IngredientAliasDTO|null
*/
private $ingredientAlias;
/**
* @Groups({"choice:read", "quizStep:read"})
*
* @var string|null
*/
private $value;
/**
* @Groups({"choice:read", "quizStep:read"})
*
* @var int|null
*/
private $rangeStart;
/**
* @Groups({"choice:read", "quizStep:read"})
*
* @var int|null
*/
private $rangeEnd;
public function getId(): int
{
return $this->id;
}
public function setId(int $id): ChoiceDTO
{
$this->id = $id;
return $this;
}
public function getTitle(): string
{
return $this->title;
}
public function setTitle(string $title): ChoiceDTO
{
$this->title = $title;
return $this;
}
public function getMeasurementUnit(): ?int
{
return $this->measurementUnit;
}
public function setMeasurementUnit(?int $measurementUnit): ChoiceDTO
{
$this->measurementUnit = $measurementUnit;
return $this;
}
public function getIngredientAlias(): ?IngredientAliasDTO
{
return $this->ingredientAlias;
}
public function setIngredientAlias(?IngredientAliasDTO $ingredientAlias): ChoiceDTO
{
$this->ingredientAlias = $ingredientAlias;
return $this;
}
public function getValue(): ?string
{
return $this->value;
}
public function setValue(?string $value): ChoiceDTO
{
$this->value = $value;
return $this;
}
public function getRangeStart(): ?int
{
return $this->rangeStart;
}
public function setRangeStart(?int $rangeStart): ChoiceDTO
{
$this->rangeStart = $rangeStart;
return $this;
}
public function getRangeEnd(): ?int
{
return $this->rangeEnd;
}
public function setRangeEnd(?int $rangeEnd): ChoiceDTO
{
$this->rangeEnd = $rangeEnd;
return $this;
}
How I can describe nested DTO ?. I have tried to use a swagger_context but it not useful for those kinds of nested DTO. Are any possibility to use references $ref like in Swagger or something similar ?
Issue Analytics
- State:
- Created 4 years ago
- Comments:20 (2 by maintainers)
Top Results From Across the Web
java - How to convert nested objects into nested DTOs using ...
I am facing issue when I am trying to use ModelMapper to convert nested java objects into nested DTO's. Getting null for child...
Read more >DTO Generator - JPA Buddy
DTO (data transfer object) is an object that carries data between processes. ... JPA Buddy offers DTO generation from JPA entities via visual...
Read more >Create Data Transfer Objects (DTOs) - Microsoft Learn
Describes how to create data transfer objects (DTOs) manually using code to change ... Flatten object graphs that contain nested objects, ...
Read more >Customization - OpenAPI Generator
The empty object definition following AUTHORS.md allows the tool to infer the target output filename in the root of the output directory.
Read more >Automatically Mapping DTO to Entity on Spring Boot APIs
DTO, which stands for Data Transfer Object, is a design pattern conceived to reduce the number of calls when working with remote interfaces....
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found

Yes! You just need to dive into the source itself: at
api-platform/core/src/Swagger/Serializer/DocumentationNormalizer.php:685you’ll find that iftrue === $readableLink, a $ref will be generated.$typecomes from@varannotation. So to get it into documentation you need to:and you should be good.
It is definitely a sign of good documentation is when someone has to go and read the source code in order to do somewhat trivial things. /s
Sorry about that last snark, but I just spent 4 hours trying to figure out the solution to the same problem.
@teohhanhui Hi. I have the same issue with api-platform/core v2.5.3 and v2.5.4.
In my case:
OfferSearchOutput(DTO, not resource) defined as output for custom operation. I have some nested DTO’s.OfferSearchOutputhas field like this with get/set methods:and
OfferOutputwith some not important fields (in fact with another compex DTO’s).As result i have this generated docs, field
offersas array of strings instead of array of my DTO:But when i tested API - all denormalization process works fine.
Can you help me?
UPDATED
I found a problem in
ApiPlatform\Core\JsonSchema\TypeFactoryline 102.if ($this->isResourceClass($className) && true !== $readableLink)always true for non-resource DTO.Next to
ApiPlatform\Core\Util\ResourceClassInfoTraitline 69 -// assume that it's a resource classbut it doesn’t becauseTypeFactoryhas$resourceClassResolver = nullin constructor.See
ApiPlatform\Core\Swagger\Serializer\DocumentationNormalizerconstructor withTypeFactorycreation without params.