Skip to main content
For a one-dimensional list of scalars (e.g. "ids": [23, 41, 22]) you don’t need any of this — reach for SimpleArrayProperty instead. The classes below are for lists of objects.
Flat properties (StringProperty, IntegerProperty, EnumProperty, …) cover most fields. When a payload carries structured data — a nested object or a list of objects — you compose it with three classes:
  • AbstractPropertyCollection — an iterable container of properties, mode-aware so you can vary fields between CREATE, UPDATE, VIEW, LIST, DELETE.
  • AbstractObjectProperty — a property whose value is an object; it returns a collection.
  • ArrayProperty — a property whose value is an array of objects; it wraps an AbstractObjectProperty.

Scenario

We’ll model a POST /api/v1/orders request that looks like this:
Two nested shapes: a single customer object (with a nested shipping_address), and an items array of uniform objects.

1. Leaf collection: AddressPropertyCollection

Start with the innermost shape. A collection takes a mode constant in its constructor — you only need to branch when a field is present in some modes but not others.

2. Object wrapper: AddressObjectProperty

The property that lives inside another schema and plugs the collection into the framework’s object machinery:

3. Composite collection: CustomerPropertyCollection

Customer contains email plus the nested shipping_address object. Compose via addProperty(new AddressObjectProperty(...)):
And the wrapper:

4. Line items: ArrayProperty of objects

For "items": [...] you wrap an AbstractObjectProperty in an ArrayProperty.

5. Use them in a request

Reading nested data in the controller

The ParameterBag magic getter returns the raw typed value. For nested objects and arrays of objects, it returns an array:
RequestValidationMiddleware already validated every level before you got here — line1 is a non-empty string, country is one of the enum values, quantity is an integer. If anything was wrong the client got a 422 with a field path like items.0.quantity.

Mode-specific fields

Branch on the $mode you pass down from the top:
Pass AbstractPropertyCollection::MODE_VIEW when composing the response, MODE_CREATE when composing the request — same collection class, correct schema in each place.

Tips

  • Keep wrappers boring. An AbstractObjectProperty subclass usually only carries the $mode and delegates to a collection. Logic belongs in the collection (or the domain).
  • Reuse wrappers across requests and responses. CustomerObjectProperty used in CreateOrderRequest is the same class you’d use in a ViewOrderResponse — swap the mode.
  • Validators stay per-property. Call setMinLength, setMaxLength, setPattern, setIsRequired, etc. on the individual StringProperty / IntegerProperty / etc. instances inside the collection.
See also: the Property reference and Property Collection reference.