Overview
An Object Schema is a collection of Properties attached to a model. It is the single source of truth for what a model’s data looks like — its labels, types, validation rules, relation structure, and how values are formatted. Schema support is built intoHasDomainObject. Any model implementing DomainObjectContract can define a schema() method, and the schema drives everything else: validation in actions, columns in tables, label formatting in activity logs, and inline edit in the frontend.
Defining a Schema
Implement the protectedschema() method on your domain object — similar to how Laravel’s casts() works:
app/Models/Order.php
SchemaDefinition::make() accepts an array of property instances. The schema is cached via once() per model class.
Accessing the Schema
Static Access (No Values)
UseschemaDefinition() when you need the structure without a model instance — for validation rules, column definitions, and TypeScript generation:
Instance Access (With Values)
Use$model->properties() to get a SchemaPropertySet — all properties filled with the model’s current values:
$order->property('name') returns a single bound Property:
Validation
SchemaDefinition::rules() returns validation rules for all writable properties. Computed properties, from() traversals, and aggregates are automatically excluded.
app/Http/Controllers/Actions/UpdateOrderAction.php
Extending Rules
UsemergeRules() to add extra rules on top of the schema defaults:
mergeRules() safely merges rules — it does not overwrite existing rules on a field, it appends them.Excluding Properties
only() and except() return new SchemaDefinition instances — the cached original is never mutated.
Query Scoping
applyToQuery() inspects all properties and adds the necessary with(), withSum(), withCount(), etc. calls in one step:
ObjectProperty→ adds relation towith()from('customer.name')→ addscustomertowith()sumOf('orderRows', 'amount')→ addswithSum('orderRows', 'amount')needsRelations(['orderRows'])→ addsorderRowstowith()
Tables
Schema-Bound Columns
UsePropertyColumn::fromSchema() to create a column from a schema property. The column automatically:
- Uses the property’s label
- Renders with
<PropertyValue>in the cell - Marks direct-column, aggregate, and
from()traversal properties as sortable - Sorts
ObjectPropertycolumns when->sortBy()is declared on the property - Maps
toFormatted()to the export value
app/Tables/OrderTable.php
Inline Edit in Tables
Add->editable() to a column to enable inline editing. The cell renders <PropertyValue> with an action prop, which opens an edit popover using the property’s input component on click:
paramName defaults to the property’s name (status). Override it if the action expects a different parameter:
Standalone Columns
You can also usePropertyColumn::make() with a property instance when you don’t have a schema — useful for ad-hoc columns:
Filters
PropertyFilter::fromSchema() picks the right filter type based on the property’s semantic metadata:
- Properties with
options()→CoreSetFilter(checkbox list) boolean→ toggle filterdate→ date range filternumber/money→ numeric range filter- Everything else → text filter
Activity Log
WhenHasDomainObject formats activity log entries, it checks whether the model defines a schema(). If it does, it uses the schema for labels and formatted values automatically — no extra configuration needed.
status on an Order will show “Status” (from the property label) and the formatted enum label (from toFormatted()), rather than the raw attribute name and value.
See: Activity Log (coming soon) for full documentation on the activity log system.
Frontend Usage
Passing Schema Data to Inertia
For standard show pages, prefer passing the full object viatoObjectData(). That gives the frontend both the object’s identity data and its schema-backed properties in one payload:
app/Http/Controllers/OrderController.php
toObjectData() includes schema by default. If the page should expose only a subset of fields, use schemaOnly:
Rendering on a Detail Page
Default detail pages should render the schema directly with<PropertyDetailCard>:
resources/js/pages/orders/order-show.tsx
PropertyDetailCard filters hidden properties automatically, and Object.values(order.schema) preserves the backend schema order.
For custom layouts, you can still render individual properties manually:
Inline Edit on a Detail Page
Pass an action to make a field inline-editable:EnumInput component automatically.
Form with Schema Fields
Use<PropertyCoreFormInput> inside a CoreForm. It reads name, label, and input type from the property data and binds to the form context automatically:
resources/js/pages/orders/order-edit.tsx
Reference
SchemaDefinition
| Method | Description |
|---|---|
make(array $properties): static | Static factory. Accepts an array of Property instances. |
get(string $name): Property | Get a property by name. |
has(string $name): bool | Check if a property exists. |
all(): array | All properties as an array. |
only(array $names): static | Returns a new schema with only the specified properties. |
except(array $names): static | Returns a new schema excluding the specified properties. |
writable(): array | Writable properties only (excludes from(), computed, aggregates). |
rules(?array $only = null): array | Validation rules for all writable properties. |
rulesFor(array $names): array | Validation rules for specific properties. |
mergeRules(array $extra): static | Fluent. Appends extra rules onto the existing rule set. |
fill(Model $model): SchemaPropertySet | Fills all properties from a model instance. |
applyToQuery(Builder $query, ?array $only = null): Builder | Applies eager loads and aggregates to a query. |
eagerLoads(?array $only = null): array | Relations to eager load from this schema. |
aggregates(?array $only = null): array | Aggregate definitions from this schema. |
propertiesWithOptions(): array | Properties that have declared options(). |
SchemaPropertySet
| Method | Description |
|---|---|
get(string $name): Property | Returns a cloned property with its value resolved from the model. |
all(): array | All bound properties. |
toFormatted(): array | ['name' => property->toFormatted(), ...] for all properties. |
toData(): array | ['name' => property->toData(), ...] for all properties. |
Schema Methods on HasDomainObject
| Method | Description |
|---|---|
schema(): SchemaDefinition (protected static) | Define the schema. Implement this in your model. |
schemaDefinition(): SchemaDefinition (static) | Public cached accessor. Calls schema()->forModel(static::class). |
properties(): SchemaPropertySet | Instance accessor. Returns the schema filled with this model’s values. |
property(string $name): Property | Shorthand for $this->properties()->get($name). |
PropertyColumn
| Method | Description |
|---|---|
make(Property $property): static | Standalone column from a property instance. |
fromSchema(string $schemaClass, string $propertyName): static | Schema-bound column. Auto-derives sortability and export format. |
sortBy(string $column): static | Enable sorting by a specific column. Accepts a plain column name or a dot-notation path for a relationship join (e.g. 'customer.name'). Overrides any sortBy declared on the property itself. |
editable(string $actionClass, ?Closure $params, ?string $paramName = null): static | Enable inline edit via <PropertyValue> action props. |
PropertyFilter
| Method | Description |
|---|---|
make(Property $property): mixed | Standalone filter from a property instance. |
fromSchema(string $schemaClass, string $propertyName): mixed | Schema-bound filter. Auto-selects filter type from property. |