Bind a form
Keep calculated fields current and recalculate them before saving.
Calculate anywhere
Reuse the same rules in services, actions and form workflows.
Validated calculation plans
Declare the inputs a calculation accepts and the outputs it produces. Every input is validated before any result is returned. A failed calculation returns errors and no partial output.validated() returns the calculated values or throws Laravel’s ValidationException. Submitted values such as total => 999999 are ignored: outputs are always calculated from the declared inputs.
Input options
Function policies
OmitallowedFunctions to enable the supported whitelist. Supply a subset to restrict a particular plan, or [] to allow only arithmetic and comparisons. Unknown functions and undeclared input references are rejected when constructing the plan.
Live calculated fields
Bind a plan to your own FLEX fields. Configure labels, appearance, sizes, defaults and field-specific behavior on those fields as usual.1
Fill
Calculated fields are recomputed from current inputs when the form is filled, including when editing a record.
2
Change
Inputs update live with a 250 ms debounce. Invalid input clears calculated previews until the input becomes valid.
3
Save
Input rules run on the server. During form dehydration, outputs are calculated again; a manipulated preview cannot become the saved result.
bind() owns input hydration and calculated-field hydration, validation, save hooks and disabled state. Apply it after configuring your fields. Additional input update callbacks and validation rules are preserved. Do not replace the calculated-field hooks or enable the calculated fields afterward.
Keep your own input callbacks
Useschema() when your inputs already have afterStateHydrated() callbacks. It returns a layout group that refreshes calculated previews after every input has hydrated. The input callbacks remain intact, including when outputs appear before their inputs.
Prefer schema() for large calculations: it refreshes the complete group once after initialization rather than refreshing after each input. bind() remains useful when you need a flat list of fields.
Save through the normal Filament form lifecycle (
getState()). Directly saving raw Livewire state bypasses form validation and dehydration. In a custom save action, call the plan’s evaluate(...)->validated() before persisting results.Repeater rows and relationships
Every row calculates its own inputs; changing, reordering or removing one row does not reuse another row’s result. The same binding can be used in a JSON repeater or aHasMany relationship repeater:
Multiple and nested calculations
A flat name prefix separates independent bindings in one schema. Array keys remain the plan’s input/output names; field names include the prefix.statePath() and bind locally named fields inside it. The parent schema owns the nesting; do not put a dotted state path in the binding’s prefix. Each group reads its own inputs.
Money and percentages
CurrencyField stores amounts in minor units: €100.00 is 10000 cents. Keep inputs and outputs in the same unit.
fractionDigits from 0 to 6. Minor-unit inputs and rounded minor-unit outputs beyond ±9,007,199,254,740,991 are rejected to prevent silent integer precision loss. Calculations use PHP floating-point arithmetic: decimal values such as 0.1 + 0.2 can carry a rounding difference. Choose explicit rounding and bounded inputs for your application; this is not an arbitrary-precision accounting engine.
Common calculations
- Progress
- Margin
- Stock replenishment
- Volume discount
NumericInput contracts in a plan before using these expressions. Direct evaluate() and evaluateMap() are lower-level APIs for values whose application rules you already validate.
Formula language
References use{field_name}. Names begin with a letter or underscore and contain only letters, digits and underscores. Calculation input/output names have a 128-character limit. Use decimal literals, parentheses and these operators:
Comparisons return
1 or 0; zero is false and nonzero is true. Division and modulo by zero are errors. Strings, arbitrary PHP, property access and function calls outside the whitelist are not supported.
Supported functions
Argument counts are checked even inside branches that will not execute. Valid unused branches remain lazy:
if(1,20,10/0) returns 20.
Dependencies, cycles and incremental updates
A plan orders outputs by dependency. A cyclic graph is rejected before evaluation.Inspect expressions and handle errors
InvalidArgumentException. tryEvaluate() returns null if evaluation fails. Use it for optional display values; do not replace required validation with a silent fallback.
Expressions are bounded to 8192 bytes, 2048 syntax nodes and 128 levels of syntax depth. A plan accepts at most 512 inputs and 512 calculated outputs. Non-finite intermediate results are rejected. Compiled syntax is reused in a bounded cache; submitted input values and results are not stored in that cache.
Explore in the playground
Open Calculated formulas to try five business scenarios, write expressions, inspect dependencies, compare incremental updates, test malformed inputs and use real bound fields. The code examples include complete input contracts and form bindings.Currency field
Format amounts stored in minor units.
Flex Field Groups
Configure calculated values in dynamic form schemas.