OpenAPITools / OpenAPITools/openapi-generator

[REQ][PHP] Discriminator does not actually work when using ObjectSerializer::deserialize()

Open
#11,432 4 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Client: PHP Enhancement: Feature
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Is your feature request related to a problem? Please describe.

I don't think the current implementation of discriminator actually works as expected. Passing an array of data to ObjectSerializer::deserialize() does not instantiate the correct child class.

openapi: 3.0.3
info:
  title: 'API'
  version: 1.0.0
servers:
  -
    url: 'https://example.com'
paths:
  /foo:
    post:
      tags:
        - Bar
      summary: 'Summary'
      operationId: foo
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PetBaseObject'
      responses:
        '200':
          description: 'successful operation'

components:
  schemas:
    PetBaseObject:
      type: object
      required:
        - pet_type
      properties:
        pet_type:
          type: string
      discriminator:
        propertyName: pet_type
        mapping:
          dog: '#/components/schemas/PetDog'
          cat: '#/components/schemas/PetCat'
          fish: '#/components/schemas/PetFish'
    PetDog:
      type: object
      allOf:
        - $ref: '#/components/schemas/PetBaseObject'
        -
          required:
            - pet_type
          properties:
            pet_type:
              type: string
              default: dog
            likes_fetch:
              type: boolean
    PetCat:
      type: object
      allOf:
        - $ref: '#/components/schemas/PetBaseObject'
        -
          required:
            - pet_type
          properties:
            pet_type:
              type: string
              default: cat
            likes_catnip:
              type: boolean
    PetFish:
      type: object
      allOf:
        - $ref: '#/components/schemas/PetBaseObject'
        -
          required:
            - pet_type
          properties:
            pet_type:
              type: string
              default: fish
            water_type:
              type: string
<?php

require_once __DIR__ . '/vendor/autoload.php';

$pet = OpenAPI\Client\ObjectSerializer::deserialize([
    'pet_type' => 'cat',
    'likes_catnip' => false,
], OpenAPI\Client\Model\PetBaseObject::class);

var_dump($pet);
$ php test.php
object(OpenAPI\Client\Model\PetBaseObject)#3 (1) {
  ["container":protected]=>
  array(1) {
    ["pet_type"]=>
    string(13) "PetBaseObject"
  }
}

Describe the solution you'd like

I would expect discriminator instantiation to work similarly to the typescript-fetch generator. It reads the discriminator field value and does a simple comparison and returns the correct class type.

In the PHP generator it would look like this:

class PetBaseObject implements ModelInterface, ArrayAccess, \JsonSerializable
{
    // ...

    public static function discriminatorClassName(array $data): ?string
    {
        if (!array_key_exists('pet_type', $data)) {
            return null;
        }

        if ($data['pet_type'] === 'cat') {
            return PetCat::class;
        }
        if ($data['pet_type'] === 'dog') {
            return PetDog::class;
        }
        if ($data['pet_type'] === 'fish') {
            return PetFish::class;
        }

        return null;
    }

In my mind the whole point of passing an array of data to ObjectSerializer::deserialize() is for it to do all instantiation for the user. Otherwise the user would simply do

$cat = new OpenAPI\Client\Model\PetCat();
$cat->setLikesCatnip(true);

Was this choice done on purpose? I have code ready for a PR if it is something you think would be a good addition to the generator:

$ php test.php
object(OpenAPI\Client\Model\PetCat)#3 (1) {
  ["container":protected]=>
  array(2) {
    ["pet_type"]=>
    string(3) "cat"
    ["likes_catnip"]=>
    bool(false)
  }
}

@jebentier @dkarlovi @mandrean @jfastnacht @ackintosh @ybelenko @renepardon

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the PHP generator's ObjectSerializer::deserialize() entry point and compare how the typescript-fetch generator handles discriminator values. Use the supplied PetBaseObject, PetCat, PetDog, and PetFish example to verify the expected generated class, then check that the generated PHP models and serializer produce the demonstrated result.

Written by the indexing model from the issue text.

Assessment

Tech stack
openapi, php
Domain
backend-api-design, devtools
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.