nelmio / NelmioApiDocBundle

Generates documentation for your REST API from annotations
MIT License
2.21k stars 828 forks source link

[Question]: Discriminator schema with Doctrine ORM Discriminator map #2262

Closed deluxetom closed 2 months ago

deluxetom commented 2 months ago

Version

4.25.2

Question

I was trying to define the discriminator manually but I cant get it to work:

#[ApiDoc\Schema(
    title: 'Unlock',
    type: 'object',
    discriminator: new ApiDoc\Discriminator(propertyName: 'discriminator', mapping: [
        self::DISCRIMINATOR_FREE_TRIAL_LOW_LATENCY => UnlockFreeTrialLowLatency::class,
        self::DISCRIMINATOR_FREE_TRIAL_SOURCE      => UnlockFreeTrialSource::class,
        self::DISCRIMINATOR_PM_MEDIA               => UnlockPmMedia::class,
        self::DISCRIMINATOR_POST                   => UnlockPost::class,
        self::DISCRIMINATOR_SOCIAL                 => UnlockSocial::class,
        self::DISCRIMINATOR_SUB_CLUB               => UnlockSubClub::class,
        self::DISCRIMINATOR_GIFT                   => UnlockGift::class,
        self::DISCRIMINATOR_CLIP                   => UnlockClip::class,
    ]),
    oneOf: [
        new ApiDoc\Schema(ref: new Model(type: UnlockFreeTrialLowLatency::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockFreeTrialSource::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockPmMedia::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockPost::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockSocial::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockSubClub::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockGift::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockClip::class)),
    ],
)]

I end up with this:

                "discriminator": {
                    "propertyName": "discriminator",
                    "mapping": {
                        "free_trial_low_latency": "App\\Entity\\Unlock\\UnlockFreeTrialLowLatency",
                        "free_trial_source": "App\\Entity\\Unlock\\UnlockFreeTrialSource",
                        "pm_media": "App\\Entity\\Unlock\\UnlockPmMedia",
                        "post": "App\\Entity\\Unlock\\UnlockPost",
                        "social": "App\\Entity\\Unlock\\UnlockSocial",
                        "sub_club": "App\\Entity\\Unlock\\UnlockSubClub",
                        "gift": "App\\Entity\\Unlock\\UnlockGift",
                        "clip": "App\\Entity\\Unlock\\UnlockClip"
                    }
                },

and when I try this one:

#[ApiDoc\Schema(
    title: 'Unlock',
    type: 'object',
    discriminator: new ApiDoc\Discriminator(propertyName: 'discriminator', mapping: [
        self::DISCRIMINATOR_FREE_TRIAL_LOW_LATENCY => new ApiDoc\Schema(ref: new Model(type: UnlockFreeTrialLowLatency::class)),
        self::DISCRIMINATOR_FREE_TRIAL_SOURCE      => new ApiDoc\Schema(ref: new Model(type: UnlockFreeTrialSource::class)),
        self::DISCRIMINATOR_PM_MEDIA               => new ApiDoc\Schema(ref: new Model(type: UnlockPmMedia::class)),
        self::DISCRIMINATOR_POST                   => new ApiDoc\Schema(ref: new Model(type: UnlockPost::class)),
        self::DISCRIMINATOR_SOCIAL                 => new ApiDoc\Schema(ref: new Model(type: UnlockSocial::class)),
        self::DISCRIMINATOR_SUB_CLUB               => new ApiDoc\Schema(ref: new Model(type: UnlockSubClub::class)),
        self::DISCRIMINATOR_GIFT                   => new ApiDoc\Schema(ref: new Model(type: UnlockGift::class)),
        self::DISCRIMINATOR_CLIP                   => new ApiDoc\Schema(ref: new Model(type: UnlockClip::class)),
    ]),
    oneOf: [
        new ApiDoc\Schema(ref: new Model(type: UnlockFreeTrialLowLatency::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockFreeTrialSource::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockPmMedia::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockPost::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockSocial::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockSubClub::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockGift::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockClip::class)),
    ],
)]

the result is:

                "discriminator": {
                    "propertyName": "discriminator",
                    "mapping": {
                        "free_trial_low_latency": {
                            "$ref": "#/components/schemas/UnlockFreeTrialLowLatency"
                        },
                        "free_trial_source": {
                            "$ref": "#/components/schemas/UnlockFreeTrialSource"
                        },
                        "pm_media": {
                            "$ref": "#/components/schemas/UnlockPmMedia"
                        },
                        "post": {
                            "$ref": "#/components/schemas/UnlockPost"
                        },
                        "social": {
                            "$ref": "#/components/schemas/UnlockSocial"
                        },
                        "sub_club": {
                            "$ref": "#/components/schemas/UnlockSubClub"
                        },
                        "gift": {
                            "$ref": "#/components/schemas/UnlockGift"
                        },
                        "clip": {
                            "$ref": "#/components/schemas/UnlockClip"
                        }
                    }
                },

I'm trying to generate this:

                "discriminator": {
                    "propertyName": "discriminator",
                    "mapping": {
                        "free_trial_low_latency": "#/components/schemas/UnlockFreeTrialLowLatency",
                        "free_trial_source": "#/components/schemas/UnlockFreeTrialSource",
                        "pm_media": "#/components/schemas/UnlockPmMedia",
                        "post": "#/components/schemas/UnlockPost",
                        "social": "#/components/schemas/UnlockSocial",
                        "sub_club": "#/components/schemas/UnlockSubClub",
                        "gift": "#/components/schemas/UnlockGift",
                        "clip": "#/components/schemas/UnlockClip"
                    }
                },

any idea how I can achieve this?

Additional context

I tried using the JMS DiscriminatorMap but when I set the field name to the virtual property returning the discriminator, I get an error that it's a duplicate (the DB field name is different)

#[ORM\InheritanceType('SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'discr', type: 'string')]
#[ORM\DiscriminatorMap([
    self::DISCRIMINATOR_FREE_TRIAL_LOW_LATENCY => UnlockFreeTrialLowLatency::class,
    self::DISCRIMINATOR_FREE_TRIAL_SOURCE      => UnlockFreeTrialSource::class,
    self::DISCRIMINATOR_PM_MEDIA               => UnlockPmMedia::class,
    self::DISCRIMINATOR_POST                   => UnlockPost::class,
    self::DISCRIMINATOR_SOCIAL                 => UnlockSocial::class,
    self::DISCRIMINATOR_SUB_CLUB               => UnlockSubClub::class,
    self::DISCRIMINATOR_GIFT                   => UnlockGift::class,
    self::DISCRIMINATOR_CLIP                   => UnlockClip::class,
])]

any help would be appreciated, thank you!

DjordyKoert commented 2 months ago

There is currently no real way to "translate" the Model attribute to a reference string for your example. In the meantime you should be able to do this by manually defining reference like so:


#[ApiDoc\Schema(
    title: 'Unlock',
    type: 'object',
    discriminator: new ApiDoc\Discriminator(propertyName: 'discriminator', mapping: [
        self::DISCRIMINATOR_FREE_TRIAL_LOW_LATENCY => '#/components/schemas/UnlockFreeTrialLowLatency',
        self::DISCRIMINATOR_FREE_TRIAL_SOURCE      => '#/components/schemas/UnlockFreeTrialSource',
        self::DISCRIMINATOR_PM_MEDIA               => '#/components/schemas/UnlockPmMedia',
        self::DISCRIMINATOR_POST                   => '#/components/schemas/UnlockPost',
        self::DISCRIMINATOR_SOCIAL                 => '#/components/schemas/UnlockSocial',
        self::DISCRIMINATOR_SUB_CLUB               => '#/components/schemas/UnlockSubClub',
        self::DISCRIMINATOR_GIFT                   => '#/components/schemas/UnlockGift',
        self::DISCRIMINATOR_CLIP                   => '#/components/schemas/UnlockClip',
    ]),
    oneOf: [
        new ApiDoc\Schema(ref: new Model(type: UnlockFreeTrialLowLatency::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockFreeTrialSource::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockPmMedia::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockPost::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockSocial::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockSubClub::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockGift::class)),
        new ApiDoc\Schema(ref: new Model(type: UnlockClip::class)),
    ],
)]
deluxetom commented 2 months ago

Thanks @DjordyKoert, that's actually what I ended up doing for now