Skip to content

Latest commit

 

History

History
258 lines (193 loc) · 6.81 KB

ObjectSchema.md

File metadata and controls

258 lines (193 loc) · 6.81 KB

ObjectSchema

The ObjectSchema validates objects/DTOs with named fields. It can parse input data into stdClass objects or custom class instances.

Note: If you need an associative array as output instead of an object, use AssocSchema.

Basic Usage

use Chubbyphp\Parsing\Parser;

$p = new Parser();

// Parse to stdClass
$schema = $p->object([
    'name' => $p->string(),
    'age' => $p->int(),
]);

$object = $schema->parse(['name' => 'John', 'age' => 30]);
// Returns: stdClass { name: 'John', age: 30 }

Parsing to Custom Classes

class User
{
    public string $name;
    public int $age;
}

$schema = $p->object([
    'name' => $p->string(),
    'age' => $p->int(),
], User::class);

$user = $schema->parse(['name' => 'John', 'age' => 30]);
// Returns: User instance with populated properties
readonly class User
{
    public function __construct(public string $name, public int $age)
    {}
}

$schema = $p->object([
    'name' => $p->string(),
    'age' => $p->int(),
], User::class, true);

$user = $schema->parse(['name' => 'John', 'age' => 30]);
// Returns: User instance with populated properties

Supported Input Types

The ObjectSchema accepts multiple input formats:

  • Arrays - Standard associative arrays
  • stdClass - Anonymous objects
  • Traversable - Objects implementing \Traversable
  • JsonSerializable - Objects implementing \JsonSerializable

Validations

Strict Mode

By default, unknown fields are silently ignored. Use strict() to reject unknown fields:

$schema = $p->object(['name' => $p->string()])->strict();

$schema->parse(['name' => 'John']);              // OK
$schema->parse(['name' => 'John', 'extra' => 1]); // Throws error

Strict with Exceptions

Allow specific unknown fields to be stripped while rejecting others:

$schema = $p->object(['name' => $p->string()])->strict(['_id', '_rev']);

$schema->parse(['name' => 'John', '_id' => '123']);     // OK, _id stripped
$schema->parse(['name' => 'John', 'unknown' => 'val']); // Throws error

Additional Properties

Like the JSON Schema additionalProperties keyword with a schema: fields without a field schema are validated against the given schema and kept in the output as dynamic properties. Without additionalProperties() unknown fields are silently dropped, with strict() they are rejected (the JSON Schema additionalProperties: false case) - combining the two throws an \InvalidArgumentException:

$schema = $p->object(['name' => $p->string()])->additionalProperties($p->float());

$schema->parse(['name' => 'John', 'score' => 1.5]);
// Returns: stdClass { name: 'John', score: 1.5 }

$schema->parse(['name' => 'John', 'score' => 'high']);
// Throws: score: Type should be "float", "string" given

The classname must accept dynamic properties: \stdClass (the default, including subclasses) or a class implementing __set(). It also cannot be combined with construct: true, since an extra field would be passed as an unknown named constructor argument. Violating either constraint throws an \InvalidArgumentException when additionalProperties() is called.

A class marked with #[\AllowDynamicProperties] is intentionally not accepted: the attribute only suppresses the dynamic property deprecation, it is no opt-in for input-driven property names - a colliding declared property (private or typed) would cause a fatal error instead of a parse error. For such classes compose instead:

$schema = $p->assoc(['name' => $p->string()])
    ->additionalProperties($p->float())
    ->postParse(static fn (array $data) => new TheClass($data));

Required Fields

Like the JSON Schema required keyword: fields listed in required() must be present - a missing one causes a dedicated object.missingField error. All other fields are optional and won't appear in the output if not provided:

$schema = $p->object([
    'name' => $p->string(),
    'nickname' => $p->string(),
])->required(['name']);

$schema->parse(['name' => 'John']);
// Returns: stdClass { name: 'John' } - no nickname property

$schema->parse(['nickname' => 'Johnny']);
// Throws: name: Missing field "name"

Without required() a missing field is parsed as null, so nullable() / default() apply.

Schema Utilities

Get Field Schema

Retrieve the schema for a specific field:

$schema = $p->object(['name' => $p->string(), 'age' => $p->int()]);

$nameSchema = $schema->getFieldSchema('name'); // Returns StringSchema

Extend Schema

Get all field schemas to extend or compose:

$baseSchema = $p->object([
    'id' => $p->int(),
    'createdAt' => $p->dateTime(),
]);

$userSchema = $p->object([
    ...$baseSchema->getFieldToSchema(),
    'name' => $p->string(),
    'email' => $p->string()->email(),
]);

Common Patterns

API Request Validation

$createUserSchema = $p->object([
    'name' => $p->string()->trim()->minLength(1)->maxLength(100),
    'email' => $p->string()->trim()->toLowerCase()->email(),
    'age' => $p->int()->minimum(0)->maximum(150),
])->strict();

Nested Objects

$addressSchema = $p->object([
    'street' => $p->string(),
    'city' => $p->string(),
    'zipCode' => $p->string()->pattern('/^\d{5}$/'),
]);

$personSchema = $p->object([
    'name' => $p->string(),
    'address' => $addressSchema,
]);

$personSchema->parse([
    'name' => 'John',
    'address' => [
        'street' => '123 Main St',
        'city' => 'Springfield',
        'zipCode' => '12345',
    ],
]);

With Required and Nullable

$schema = $p->object([
    'id' => $p->int(),
    'name' => $p->string(),
    'bio' => $p->string()->nullable(),      // Can be null
    'website' => $p->string()->uri(),        // Validated if present
])->required(['id', 'name', 'bio']);         // website field is optional

// Valid inputs:
$schema->parse(['id' => 1, 'name' => 'John', 'bio' => null]);
$schema->parse(['id' => 1, 'name' => 'John', 'bio' => 'Hello', 'website' => 'https://example.com']);

Complex Real-World Example

$petSchema = $p->object([
    'id' => $p->string()->uuid(),
    'name' => $p->string()->minLength(1),
    'vaccinations' => $p->array(
        $p->object([
            'name' => $p->string(),
            'date' => $p->dateTime(),
        ])
    ),
]);

$listRequestSchema = $p->object([
    'offset' => $p->int()->minimum(0),
    'limit' => $p->int()->exclusiveMinimum(0)->maximum(100),
    'sort' => $p->record($p->const('asc')),
    'items' => $p->array($petSchema),
]);

Error Codes

Code Description
object.type Value is not a valid object type
object.unknownField Unknown field found in strict mode
object.missingField Field listed in required() is missing

Field-level errors include the field name in the error path (e.g., name, address.city).