From f0eb85584b41e6a75b7e9c7743e0722022563fa4 Mon Sep 17 00:00:00 2001 From: Dan McNulty Date: Thu, 14 May 2026 11:28:36 -0500 Subject: [PATCH 1/6] chore: add support for non-discriminator, oneOf request parameters - Update `api` template to use union types for request parameters that use the `oneOf` construct without a discriminator rather than a generated wrapper model. This ensures that `oneOf` can be used to expand a parameters types without breaking backward compatibility. - Update `ObjectSerializer` template to support the `int|\DateTime` type, which is will be used to by the pending `start` and `end` types. New combinations of `oneOf` schemas will require similar changes in the future. - Update `README.mustache` to skip references to models without variables. The referenced models will be ignored in .openapi-generator-ignore moving forward. - Update `api_doc` template to generate correct examples and type references for `oneOf` request parameters. --- template/ObjectSerializer.mustache | 7 +++++++ template/README.mustache | 4 ++-- template/api.mustache | 20 ++++++++++---------- template/api_doc.mustache | 4 ++-- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/template/ObjectSerializer.mustache b/template/ObjectSerializer.mustache index b2a28bec..91ca2a20 100644 --- a/template/ObjectSerializer.mustache +++ b/template/ObjectSerializer.mustache @@ -211,6 +211,13 @@ class ObjectSerializer } } + # Handle int and DateTime union in query + if ('int|\DateTime' === $openApiType || '\DateTime|int' === $openApiType) { + if ($value instanceof \DateTime) { + return ["$paramName" => $value->format(self::$dateTimeFormat)]; + } + } + # Handle DateTime objects in query if ('\DateTime' === $openApiType && $value instanceof \DateTime) { return ["$paramName" => $value->format(self::$dateTimeFormat)]; diff --git a/template/README.mustache b/template/README.mustache index 8a2ee436..f1d5b706 100644 --- a/template/README.mustache +++ b/template/README.mustache @@ -235,8 +235,8 @@ Class | Method | HTTP request | Description ## Documentation for Models -{{#models}}{{#model}} - [{{{classname}}}]({{modelDocPath}}/{{{classname}}}.md) -{{/model}}{{/models}} +{{#models}}{{#model}}{{^emptyVars}} - [{{{classname}}}]({{modelDocPath}}/{{{classname}}}.md) +{{/emptyVars}}{{/model}}{{/models}} ## Documentation for Authorization diff --git a/template/api.mustache b/template/api.mustache index 79f8bb9f..0fa84b89 100644 --- a/template/api.mustache +++ b/template/api.mustache @@ -95,7 +95,7 @@ use {{invokerPackage}}\ObjectSerializer; * {{/description}} {{#allParams}} - * @param {{{dataType}}}{{^required}}|null{{/required}} ${{paramName}}{{#description}} {{{.}}}{{/description}}{{^description}} {{paramName}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} + * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{^required}}|null{{/required}} ${{paramName}}{{#description}} {{{.}}}{{/description}}{{^description}} {{paramName}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} {{/allParams}} * * @noinspection GrazieInspection @@ -108,7 +108,7 @@ use {{invokerPackage}}\ObjectSerializer; * @deprecated {{/isDeprecated}} */ - public function {{operationId}}({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{{dataType}}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} + public function {{operationId}}({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} { {{#returnType}}list($response) = {{/returnType}}$this->{{operationId}}WithHttpInfo({{#allParams}}${{paramName}}, {{/allParams}});{{#returnType}} return $response;{{/returnType}} @@ -126,7 +126,7 @@ use {{invokerPackage}}\ObjectSerializer; * {{/description}} {{#allParams}} - * @param {{{dataType}}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} + * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} {{/allParams}} * * @noinspection GrazieInspection @@ -141,7 +141,7 @@ use {{invokerPackage}}\ObjectSerializer; * @deprecated {{/isDeprecated}} */ - public function {{operationId}}WithHttpInfo({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{{dataType}}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): array + public function {{operationId}}WithHttpInfo({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): array { $request = $this->{{operationId}}Request({{#allParams}}${{paramName}}, {{/allParams}}); @@ -206,7 +206,7 @@ use {{invokerPackage}}\ObjectSerializer; * {{/description}} {{#allParams}} - * @param {{{dataType}}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} + * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} {{/allParams}} * * @noinspection GrazieInspection @@ -218,7 +218,7 @@ use {{invokerPackage}}\ObjectSerializer; * @deprecated {{/isDeprecated}} */ - public function {{operationId}}Async({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{{dataType}}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): PromiseInterface + public function {{operationId}}Async({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): PromiseInterface { return $this->{{operationId}}AsyncWithHttpInfo({{#allParams}}${{paramName}}, {{/allParams}}) ->then( @@ -240,7 +240,7 @@ use {{invokerPackage}}\ObjectSerializer; * {{/description}} {{#allParams}} - * @param {{{dataType}}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} + * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} {{/allParams}} * * @noinspection GrazieInspection @@ -252,7 +252,7 @@ use {{invokerPackage}}\ObjectSerializer; * @deprecated {{/isDeprecated}} */ - public function {{operationId}}AsyncWithHttpInfo({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{{dataType}}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): PromiseInterface + public function {{operationId}}AsyncWithHttpInfo({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): PromiseInterface { $request = $this->{{operationId}}Request({{#allParams}}${{paramName}}, {{/allParams}}); @@ -283,7 +283,7 @@ use {{invokerPackage}}\ObjectSerializer; * Create request for operation '{{{operationId}}}' * {{#allParams}} - * @param {{{dataType}}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} + * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} {{/allParams}} * * @noinspection GrazieInspection @@ -295,7 +295,7 @@ use {{invokerPackage}}\ObjectSerializer; * @deprecated {{/isDeprecated}} */ - public function {{operationId}}Request({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{{dataType}}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): Request + public function {{operationId}}Request({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): Request { {{#allParams}} {{#hasValidation}} diff --git a/template/api_doc.mustache b/template/api_doc.mustache index e0ea771a..a1d11bb6 100644 --- a/template/api_doc.mustache +++ b/template/api_doc.mustache @@ -35,7 +35,7 @@ $apiInstance = new {{classname}}( new GuzzleHttp\Client() ); -{{#allParams}}${{paramName}} = {{{example}}}; // {{{dataType}}}{{#description}} | {{{.}}}{{/description}} +{{#allParams}}${{paramName}} = {{#composedSchemas.oneOf}}{{#-first}}{{{example}}}{{/-first}}{{/composedSchemas.oneOf}}{{^composedSchemas.oneOf}}{{{example}}}{{/composedSchemas.oneOf}}; // {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{#description}} | {{{.}}}{{/description}} {{/allParams}} try { @@ -53,7 +53,7 @@ try { {{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}}| Name | Type | Description | Notes | | ------------- | ------------- | ------------- | ------------- |{{/-last}}{{/allParams}} -{{#allParams}}| **{{paramName}}** | {{#isFile}}**{{{dataType}}}**{{/isFile}}{{#isPrimitiveType}}**{{{dataType}}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{{dataType}}}**](../Model/{{baseType}}.md){{/isFile}}{{/isPrimitiveType}} | {{{description}}} |{{^required}} [optional]{{/required}}{{#defaultValue}} [default to {{.}}]{{/defaultValue}} | +{{#allParams}}| **{{paramName}}** | {{#isFile}}**{{{dataType}}}**{{/isFile}}{{#isPrimitiveType}}**{{{dataType}}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}{{^composedSchemas.oneOf}}[**{{{dataType}}}**](../Model/{{baseType}}.md){{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{^hasDiscriminatorWithNonEmptyMapping}}{{#-first}}**{{/-first}}{{{dataType}}}{{^-last}}\|{{/-last}}{{#-last}}**{{/-last}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{#-first}}[**{{{dataType}}}**](../Model/{{baseType}}.md){{/-first}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/composedSchemas.oneOf}}{{/isFile}}{{/isPrimitiveType}} | {{{description}}} |{{^required}} [optional]{{/required}}{{#defaultValue}} [default to {{.}}]{{/defaultValue}} | {{/allParams}} ### Return type From 14c2e5b1816c563f36fd2125879699089b44b8af Mon Sep 17 00:00:00 2001 From: Dan McNulty Date: Thu, 14 May 2026 12:06:03 -0500 Subject: [PATCH 2/6] chore: generated updates to ObjectSerializer --- src/ObjectSerializer.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ObjectSerializer.php b/src/ObjectSerializer.php index 3397cf12..221762e4 100644 --- a/src/ObjectSerializer.php +++ b/src/ObjectSerializer.php @@ -191,6 +191,13 @@ public static function toQueryValue( return []; } + # Handle int and DateTime union in query + if ('int|\DateTime' === $openApiType || '\DateTime|int' === $openApiType) { + if ($value instanceof \DateTime) { + return ["$paramName" => $value->format(self::$dateTimeFormat)]; + } + } + # Handle DateTime objects in query if ('\DateTime' === $openApiType && $value instanceof \DateTime) { return ["$paramName" => $value->format(self::$dateTimeFormat)]; From d27e5c4d158fe3da4bf29058a50bf3f6888fc0a7 Mon Sep 17 00:00:00 2001 From: Dan McNulty Date: Thu, 14 May 2026 12:21:38 -0500 Subject: [PATCH 3/6] chore: fix reference for discriminator oneOf parameters --- template/api.mustache | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/template/api.mustache b/template/api.mustache index 0fa84b89..8bbaf17f 100644 --- a/template/api.mustache +++ b/template/api.mustache @@ -95,7 +95,7 @@ use {{invokerPackage}}\ObjectSerializer; * {{/description}} {{#allParams}} - * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{^required}}|null{{/required}} ${{paramName}}{{#description}} {{{.}}}{{/description}}{{^description}} {{paramName}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} + * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{schema.complexType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{^required}}|null{{/required}} ${{paramName}}{{#description}} {{{.}}}{{/description}}{{^description}} {{paramName}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} {{/allParams}} * * @noinspection GrazieInspection @@ -108,7 +108,7 @@ use {{invokerPackage}}\ObjectSerializer; * @deprecated {{/isDeprecated}} */ - public function {{operationId}}({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} + public function {{operationId}}({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{schema.complexType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} { {{#returnType}}list($response) = {{/returnType}}$this->{{operationId}}WithHttpInfo({{#allParams}}${{paramName}}, {{/allParams}});{{#returnType}} return $response;{{/returnType}} @@ -126,7 +126,7 @@ use {{invokerPackage}}\ObjectSerializer; * {{/description}} {{#allParams}} - * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} + * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{schema.complexType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} {{/allParams}} * * @noinspection GrazieInspection @@ -141,7 +141,7 @@ use {{invokerPackage}}\ObjectSerializer; * @deprecated {{/isDeprecated}} */ - public function {{operationId}}WithHttpInfo({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): array + public function {{operationId}}WithHttpInfo({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{schema.complexType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): array { $request = $this->{{operationId}}Request({{#allParams}}${{paramName}}, {{/allParams}}); @@ -206,7 +206,7 @@ use {{invokerPackage}}\ObjectSerializer; * {{/description}} {{#allParams}} - * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} + * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{schema.complexType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} {{/allParams}} * * @noinspection GrazieInspection @@ -218,7 +218,7 @@ use {{invokerPackage}}\ObjectSerializer; * @deprecated {{/isDeprecated}} */ - public function {{operationId}}Async({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): PromiseInterface + public function {{operationId}}Async({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{schema.complexType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): PromiseInterface { return $this->{{operationId}}AsyncWithHttpInfo({{#allParams}}${{paramName}}, {{/allParams}}) ->then( @@ -240,7 +240,7 @@ use {{invokerPackage}}\ObjectSerializer; * {{/description}} {{#allParams}} - * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} + * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{schema.complexType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} {{/allParams}} * * @noinspection GrazieInspection @@ -252,7 +252,7 @@ use {{invokerPackage}}\ObjectSerializer; * @deprecated {{/isDeprecated}} */ - public function {{operationId}}AsyncWithHttpInfo({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): PromiseInterface + public function {{operationId}}AsyncWithHttpInfo({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{schema.complexType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): PromiseInterface { $request = $this->{{operationId}}Request({{#allParams}}${{paramName}}, {{/allParams}}); @@ -283,7 +283,7 @@ use {{invokerPackage}}\ObjectSerializer; * Create request for operation '{{{operationId}}}' * {{#allParams}} - * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} + * @param {{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{schema.complexType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{{.}}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}} {{/allParams}} * * @noinspection GrazieInspection @@ -295,7 +295,7 @@ use {{invokerPackage}}\ObjectSerializer; * @deprecated {{/isDeprecated}} */ - public function {{operationId}}Request({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): Request + public function {{operationId}}Request({{#allParams}}{{#isArray}}array{{/isArray}}{{#isMap}}array{{/isMap}}{{^isArray}}{{^isMap}}{{^composedSchemas.oneOf}}{{{dataType}}}{{/composedSchemas.oneOf}}{{#composedSchemas.oneOf}}{{#-first}}{{^hasDiscriminatorWithNonEmptyMapping}}{{{schema.dataType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{#hasDiscriminatorWithNonEmptyMapping}}{{{schema.complexType}}}{{/hasDiscriminatorWithNonEmptyMapping}}{{/-first}}{{/composedSchemas.oneOf}}{{/isMap}}{{/isArray}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): Request { {{#allParams}} {{#hasValidation}} From 0d8a33c7d7f3bb759bf1a66ef07e5d252c0ce3e6 Mon Sep 17 00:00:00 2001 From: Dan McNulty Date: Thu, 14 May 2026 12:47:47 -0500 Subject: [PATCH 4/6] chore: fix issue with 0 value for `int|\DateTime` parameter --- src/ObjectSerializer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ObjectSerializer.php b/src/ObjectSerializer.php index 221762e4..701dcfa4 100644 --- a/src/ObjectSerializer.php +++ b/src/ObjectSerializer.php @@ -553,7 +553,7 @@ private static function isEmptyValue(mixed $value, string $openApiType): bool # For numeric values, false and '' are considered empty. # This comparison is safe for floating point values, since the previous call to empty() will # filter out values that don't match 0. - 'int', 'integer' => 0 !== $value, + 'int', 'integer', 'int|\DateTime', '\DateTime|int' => 0 !== $value, 'number', 'float' => 0 !== $value && 0.0 !== $value, # For boolean values, '' is considered empty 'bool', 'boolean' => !in_array($value, [false, 0], true), From cccd92cf1a9848b6392a545c530220d17a140b07 Mon Sep 17 00:00:00 2001 From: Dan McNulty Date: Thu, 14 May 2026 12:48:16 -0500 Subject: [PATCH 5/6] test: add unit tests for toQueryValue updates --- test/ObjectSerializerTest.php | 68 +++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/test/ObjectSerializerTest.php b/test/ObjectSerializerTest.php index 813bfb15..62df7b64 100644 --- a/test/ObjectSerializerTest.php +++ b/test/ObjectSerializerTest.php @@ -196,4 +196,72 @@ public function testBuildQueryInvalidEncoding(): void $this->expectException(\InvalidArgumentException::class); ObjectSerializer::buildQuery(['key' => 'value'], 'invalid'); } + + /** + * toQueryValue with int|\DateTime type should format a DateTime using the current dateTimeFormat. + */ + public function testToQueryValueDateTimeUnionWithDateTime(): void + { + $date = new \DateTime('2024-03-15T12:00:00+00:00'); + $result = ObjectSerializer::toQueryValue($date, 'before', 'int|\DateTime'); + $this->assertSame(['before' => $date->format(\DateTimeInterface::ATOM)], $result); + } + + /** + * toQueryValue with \DateTime|int (reversed) should also format a DateTime correctly. + */ + public function testToQueryValueDateTimeUnionReversedWithDateTime(): void + { + $date = new \DateTime('2024-03-15T12:00:00+00:00'); + $result = ObjectSerializer::toQueryValue($date, 'after', '\DateTime|int'); + $this->assertSame(['after' => $date->format(\DateTimeInterface::ATOM)], $result); + } + + /** + * toQueryValue with int|\DateTime type should pass an integer through as-is. + */ + public function testToQueryValueDateTimeUnionWithInt(): void + { + $result = ObjectSerializer::toQueryValue(1700000000, 'before', 'int|\DateTime'); + $this->assertSame(['before' => 1700000000], $result); + } + + /** + * toQueryValue with int|\DateTime type should omit a null value when the parameter is not required. + */ + public function testToQueryValueDateTimeUnionNullOptional(): void + { + $result = ObjectSerializer::toQueryValue(null, 'before', 'int|\DateTime', 'form', true, false); + $this->assertSame([], $result); + } + + /** + * toQueryValue with int|\DateTime type should return an empty string for a null value when the parameter is required. + */ + public function testToQueryValueDateTimeUnionNullRequired(): void + { + $result = ObjectSerializer::toQueryValue(null, 'before', 'int|\DateTime', 'form', true, true); + $this->assertSame(['before' => ''], $result); + } + + /** + * toQueryValue with int|\DateTime type should pass zero through because it should be treated as an integer. + */ + public function testToQueryValueDateTimeUnionZeroOptional(): void + { + $result = ObjectSerializer::toQueryValue(0, 'before', 'int|\DateTime', 'form', true, false); + $this->assertSame(['before' => 0], $result); + } + + /** + * toQueryValue with int|\DateTime type should respect a custom dateTimeFormat when formatting a DateTime. + */ + public function testToQueryValueDateTimeUnionCustomFormat(): void + { + $date = new \DateTime('2024-03-15T12:00:00+00:00'); + ObjectSerializer::setDateTimeFormat('Y-m-d'); + $result = ObjectSerializer::toQueryValue($date, 'before', 'int|\DateTime'); + ObjectSerializer::setDateTimeFormat(\DateTimeInterface::ATOM); + $this->assertSame(['before' => '2024-03-15'], $result); + } } From ff60c41dd6659890913273f58a775115fa86e3d6 Mon Sep 17 00:00:00 2001 From: Dan McNulty Date: Thu, 14 May 2026 12:51:19 -0500 Subject: [PATCH 6/6] chore: update ObjectSerializer template The last commit just updated the generated code, not the template. --- template/ObjectSerializer.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/ObjectSerializer.mustache b/template/ObjectSerializer.mustache index 91ca2a20..ec8b362c 100644 --- a/template/ObjectSerializer.mustache +++ b/template/ObjectSerializer.mustache @@ -166,7 +166,7 @@ class ObjectSerializer # For numeric values, false and '' are considered empty. # This comparison is safe for floating point values, since the previous call to empty() will # filter out values that don't match 0. - 'int', 'integer' => 0 !== $value, + 'int', 'integer', 'int|\DateTime', '\DateTime|int' => 0 !== $value, 'number', 'float' => 0 !== $value && 0.0 !== $value, # For boolean values, '' is considered empty 'bool', 'boolean' => !in_array($value, [false, 0], true),