Skip to content

feat: support polymorphism#7815

Open
Maxcastel wants to merge 1 commit intoapi-platform:mainfrom
Maxcastel:feature/support-polymorphism
Open

feat: support polymorphism#7815
Maxcastel wants to merge 1 commit intoapi-platform:mainfrom
Maxcastel:feature/support-polymorphism

Conversation

@Maxcastel
Copy link
Contributor

@Maxcastel Maxcastel commented Mar 2, 2026

Q A
Branch? main
Tickets #6915 #2931
License MIT
Doc PR N/A

Exemple ApiResource / Entity

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\DiscriminatorMap;

#[DiscriminatorMap(typeProperty: 'bookType', mapping: [
    'fiction' => FictionBook::class,
    'technical' => TechnicalBook::class,
])]
#[ApiResource(
    operations: [
        new GetCollection(),
        new Get()
    ],
)]
#[ORM\Entity]
#[ORM\InheritanceType('SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'book_type', type: 'string')]
#[ORM\DiscriminatorMap([
    'fiction' => FictionBook::class,
    'technical' => TechnicalBook::class,
])]
abstract class Book
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;

    #[ORM\Column(type: 'string', length: 255)]
    private string $title;

    #[ORM\ManyToOne(targetEntity: Author::class)]
    #[ORM\JoinColumn(nullable: false)]
    private Author $author;

    #[ORM\Column(type: 'string', length: 20)]
    private string $isbn;

    public function __construct(string $title = '', Author $author = null, string $isbn = '')
    {
        $this->title = $title;
        $this->author = $author ?? new Author('Unknown');
        $this->isbn = $isbn;
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getTitle(): string
    {
        return $this->title;
    }

    public function setTitle(string $title): self
    {
        $this->title = $title;
        return $this;
    }

    public function getAuthor(): Author
    {
        return $this->author;
    }

    public function setAuthor(Author $author): self
    {
        $this->author = $author;
        return $this;
    }

    public function getIsbn(): string
    {
        return $this->isbn;
    }

    public function setIsbn(string $isbn): self
    {
        $this->isbn = $isbn;
        return $this;
    }

    abstract public function getBookType(): string;
}
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class FictionBook extends Book
{
    public const string BOOK_TYPE = 'fiction';

    #[ORM\Column(type: 'string', length: 100, nullable: true)]
    private ?string $genre = null;

    #[ORM\Column(type: 'integer', nullable: true)]
    private ?int $pageCount = null;

    public function __construct(
        string $title = '',
        Author $author = null,
        string $isbn = '',
        ?string $genre = null,
        ?int $pageCount = null
    ) {
        parent::__construct($title, $author, $isbn);
        $this->genre = $genre;
        $this->pageCount = $pageCount;
    }

    public function getBookType(): string
    {
        return self::BOOK_TYPE;
    }

    public function getGenre(): ?string
    {
        return $this->genre;
    }

    public function setGenre(?string $genre): self
    {
        $this->genre = $genre;
        return $this;
    }

    public function getPageCount(): ?int
    {
        return $this->pageCount;
    }

    public function setPageCount(?int $pageCount): self
    {
        $this->pageCount = $pageCount;
        return $this;
    }
}
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class TechnicalBook extends Book
{
    public const string BOOK_TYPE = 'technical';

    #[ORM\Column(type: 'string', length: 100, nullable: true)]
    private ?string $programmingLanguage = null;

    #[ORM\Column(type: 'string', length: 50, nullable: true)]
    private ?string $difficultyLevel = null;

    #[ORM\Column(type: 'string', length: 255, nullable: true)]
    private ?string $topic = null;

    public function __construct(
        string $title = '',
        Author $author = null,
        string $isbn = '',
        ?string $programmingLanguage = null,
        ?string $difficultyLevel = null,
        ?string $topic = null
    ) {
        parent::__construct($title, $author, $isbn);
        $this->programmingLanguage = $programmingLanguage;
        $this->difficultyLevel = $difficultyLevel;
        $this->topic = $topic;
    }

    public function getBookType(): string
    {
        return self::BOOK_TYPE;
    }

    public function getProgrammingLanguage(): ?string
    {
        return $this->programmingLanguage;
    }

    public function setProgrammingLanguage(?string $programmingLanguage): self
    {
        $this->programmingLanguage = $programmingLanguage;
        return $this;
    }

    public function getDifficultyLevel(): ?string
    {
        return $this->difficultyLevel;
    }

    public function setDifficultyLevel(?string $difficultyLevel): self
    {
        $this->difficultyLevel = $difficultyLevel;
        return $this;
    }

    public function getTopic(): ?string
    {
        return $this->topic;
    }

    public function setTopic(?string $topic): self
    {
        $this->topic = $topic;
        return $this;
    }
}

Before

JSON Schema

"Book.jsonld": {
    "allOf": [
        {
            "$ref": "#/components/schemas/HydraItemBaseSchema"
        },
        {
            "type": "object",
            "properties": {
                "id": {
                    "readOnly": true,
                    "type": "integer"
                },
                "title": {
                    "type": "string"
                },
                "author": {
                    "type": [
                        "string",
                        "null"
                    ],
                    "format": "iri-reference",
                    "example": "https://example.com/"
                },
                "isbn": {
                    "type": "string"
                },
                "bookType": {
                    "readOnly": true,
                    "type": "string"
                }
            }
        }
    ]
}

No FictionBook schema
No TechnicalBook schema

API Response

Capture d’écran du 2026-03-09 17-20-16

API Docs

Capture d’écran du 2026-03-09 17-21-54 Capture d’écran du 2026-03-09 17-22-37

After

JSON Schema

"Book.jsonld": {
    "allOf": [
        {
            "$ref": "#/components/schemas/HydraItemBaseSchema"
        },
        {
            "type": "object",
            "properties": {
                "id": {
                    "readOnly": true,
                    "type": "integer"
                },
                "title": {
                    "type": "string"
                },
                "author": {
                    "type": [
                        "string",
                        "null"
                    ],
                    "format": "iri-reference",
                    "example": "https://example.com/"
                },
                "isbn": {
                    "type": "string"
                },
                "bookType": {
                    "readOnly": true,
                    "type": "string"
                }
            },
            "oneOf": [
                {
                    "$ref": "#/components/schemas/FictionBook.jsonld"
                },
                {
                    "$ref": "#/components/schemas/TechnicalBook.jsonld"
                }
            ],
            "discriminator": {
                "propertyName": "bookType",
                "mapping": {
                    "fiction": "#/components/schemas/FictionBook.jsonld",
                    "technical": "#/components/schemas/TechnicalBook.jsonld"
                }
            }
        }
    ]
}
"FictionBook.jsonld": {
    "allOf": [
        {
            "$ref": "#/components/schemas/Book.jsonld"
        },
        {
            "type": "object",
            "properties": {
                "genre": {
                    "type": [
                        "string",
                        "null"
                    ]
                },
                "pageCount": {
                    "type": [
                        "integer",
                        "null"
                    ]
                }
            }
        }
    ]
}
"TechnicalBook.jsonld": {
    "allOf": [
        {
            "$ref": "#/components/schemas/Book.jsonld"
        },
        {
            "type": "object",
            "properties": {
                "programmingLanguage": {
                    "type": [
                        "string",
                        "null"
                    ]
                },
                "difficultyLevel": {
                    "type": [
                        "string",
                        "null"
                    ]
                },
                "topic": {
                    "type": [
                        "string",
                        "null"
                    ]
                }
            }
        }
    ]
}

API Response

Capture d’écran du 2026-03-10 10-09-28

API Docs

Capture d’écran du 2026-03-10 10-13-17 Capture d’écran du 2026-03-10 10-17-06

@Maxcastel Maxcastel force-pushed the feature/support-polymorphism branch 2 times, most recently from 9172fa8 to f19b7e6 Compare March 9, 2026 15:59
@Maxcastel Maxcastel marked this pull request as ready for review March 9, 2026 15:59
@Maxcastel Maxcastel force-pushed the feature/support-polymorphism branch from f19b7e6 to e6d743b Compare March 9, 2026 16:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant