Skip to content

Latest commit

 

History

History
196 lines (155 loc) · 6.38 KB

File metadata and controls

196 lines (155 loc) · 6.38 KB

If

The keywords if, then and else can be used to conditionally combine multiple subschemas. Conditional compositions can be used on property level and on object level.

{
    "$id": "example",
    "type": "object",
    "properties": {
        "example": {
            "type": "number",
            "if": {
                "multipleOf": 5
            },
            "then": {
                "minimum": 100
            },
            "else": {
                "maximum": 100
            }
        }
    }
}

Valid values are eg. 100, 105, 99. Invalid values are eg. 50, 101 or any non numeric values.

Generated interface:

public function setExample(float $example): static;
public function getExample(): ?float;

Possible exception (in this case 50 was provided so the if condition succeeds but the then branch failed):

Invalid value for example declined by conditional composition constraint
  - Condition: Valid
  - Conditional branch failed:
    * Value for example must not be smaller than 100

Another example exception with 101 as value for the property:

Invalid value for example declined by conditional composition constraint
  - Condition: Failed
    * Value for example must be a multiple of 5
  - Conditional branch failed:
    * Value for example must not be larger than 100

The thrown exception will be a PHPModelGenerator\Exception\ComposedValue\ConditionalException which provides the following methods to get further error details:

// get the exception which triggered the condition to fail
// if error collection is enabled an ErrorRegistryException will be returned
public function getIfException(): ?Exception

// get the exception which triggered the conditional branch to fail
// if error collection is enabled an ErrorRegistryException will be returned
public function getThenException(): ?Exception
public function getElseException(): ?Exception

// get the name of the property which failed
public function getPropertyName(): string
// get the value provided to the property
public function getProvidedValue()

An object level composition will result in an object which contains all properties contained in the three possible blocks of the condition.

{
    "$id": "customer",
    "type": "object",
    "properties": {
        "country": {
            "enum": ["United States of America", "Canada"]
        }
    },
    "if": {
        "type": "object",
        "properties": {
            "country": {
                "const": "United States of America"
            }
        }
    },
    "then": {
        "type": "object",
        "properties": {
            "postal_code": {
                "pattern": "[0-9]{5}(-[0-9]{4})?"
            }
        }
    },
    "else": {
        "type": "object",
        "properties": {
            "postal_code": {
                "pattern": "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]"
            }
        }
    }
}

Generated interface:

public function setCountry(string $country): static;
public function getCountry(): ?string;

public function setPostalCode(string $country): static;
public function getPostalCode(): ?string;

When the then and else branches define the same property with different types, the generator produces a union type hint — consistent with the behaviour of anyOf/oneOf:

{
    "$id": "example",
    "type": "object",
    "if": {
        "properties": {
            "name": {
                "const": "Alice"
            }
        }
    },
    "then": {
        "properties": {
            "age": {
                "type": "integer"
            }
        }
    },
    "else": {
        "properties": {
            "age": {
                "type": "string"
            }
        }
    }
}

Generated interface:

public function setAge(int | string | null $age): static;
public function getAge(): int | string | null;

When only a then block is present (no else), the branch may not apply at runtime, so the property is always nullable:

public function setAge(?int $age): static;
public function getAge(): ?int;

Note

Any of the three branches (if, then, else) can be the boolean literal true or false. The generator resolves these statically at generation time:

  • if: false — condition never matches; else (if present) is applied unconditionally, then is ignored.
  • if: true — condition always matches; then (if present) is applied unconditionally, else is ignored.
  • if: false, else: false or if: true, then: false — the composition is always unsatisfiable; providing any value raises a ConditionalException at runtime. The generator also emits a warning at generation time.
  • then: false / else: false (with a real schema for if) — when the relevant branch is entered, the value would always be invalid; the generator throws a SchemaException at generation time.
  • then: true / else: true — when the relevant branch is entered, any value is accepted; treated as absent (no additional constraint).

Hint

The union-widening and nullability rules for if/then/else follow the same logic as anyOf/oneOf. See Cross-typed compositions for the full explanation.

Note

For object-level if/then/else compositions, when a property appears in the required array of both then and else, the generator promotes that property to non-nullable. Exactly one of the two branches applies at runtime, and both guarantee the property's presence. If there is no else block, the property is never promoted — the schema is silent when the condition fails, so the property may be absent. See Cross-typed compositions for the full promotion rules.