Skip to content
Viames Marino edited this page Mar 26, 2026 · 3 revisions

Pair framework: Number

Pair\Html\FormControls\Number renders numeric inputs with support for min, max, and step. It is the reference control for quantities, amounts, percentages, and any CRUD field that should produce <input type="number">.

Rendered HTML:

<input type="number" ... />

Main methods

Number adds these methods on top of the base FormControl API:

  • step(int|float $value): self
  • min(int|float $minValue): self
  • max(int|float $maxValue): self
  • render(): string
  • validate(): bool

Inherited methods such as required(), value(), label(), class(), minLength(), and maxLength() remain available.

Range methods

min(...)

Sets the HTML min attribute and participates in server-side validation.

$quantity = (new \Pair\Html\FormControls\Number('quantity'))
    ->label('Quantity')
    // Do not allow negative quantities.
    ->min(1);

max(...)

Sets the HTML max attribute and participates in server-side validation.

$quantity = (new \Pair\Html\FormControls\Number('quantity'))
    ->label('Quantity')
    // Cap the field to a known business limit.
    ->max(999);

step(...)

Controls the increment used by the browser widget.

$amount = (new \Pair\Html\FormControls\Number('amount'))
    ->label('Amount')
    // Useful for currency-like values.
    ->step(0.01);

Rendering behavior

render() has one detail that makes Number slightly different from the simpler text controls:

  • Pair temporarily switches LC_NUMERIC to en_US
  • it prints type="number"
  • it adds min, max, step, minlength, and maxlength when present
  • it restores the previous locale at the end

Practical example:

$amount = (new \Pair\Html\FormControls\Number('amount'))
    ->label('Amount')
    // Use a decimal step when the field models money-like values.
    ->min(0.01)
    ->max(10000)
    ->step(0.01)
    ->value(15.75)
    ->class('form-control');

echo $amount->renderLabel();
echo $amount->render();

Validation behavior

Number::validate() adds numeric-specific checks before finishing the rest of the shared validation.

Current behavior:

  • if the field is required, the submitted value must be numeric
  • if min(...) was set, Pair checks the lower bound
  • if max(...) was set, Pair checks the upper bound
  • if minLength() or maxLength() were set, Pair checks the string length of the submitted value

Example:

$seats = (new \Pair\Html\FormControls\Number('seats'))
    // Combine required() with a real range to get stronger numeric validation.
    ->required()
    ->min(1)
    ->max(50);

// validate() reads Post::get('seats') and applies numeric checks.
$isValid = $seats->validate();

Practical examples

1. Integer quantity field

$qty = (new \Pair\Html\FormControls\Number('quantity'))
    ->label('Quantity')
    // Integer counters typically combine min/max and step(1).
    ->required()
    ->min(1)
    ->max(999)
    ->step(1)
    ->value(2);

2. Decimal amount field

$amount = (new \Pair\Html\FormControls\Number('amount'))
    ->label('Amount')
    // Allow positive decimals with cent precision.
    ->min(0.01)
    ->step(0.01)
    ->value(15.75);

3. Percentage-like field

$discount = (new \Pair\Html\FormControls\Number('discount'))
    ->label('Discount %')
    // Percentage fields are a typical use case for bounded numeric input.
    ->min(0)
    ->max(100)
    ->step(0.5)
    ->value(10);

4. Optional numeric field with frontend hooks

$threshold = (new \Pair\Html\FormControls\Number('threshold'))
    ->label('Threshold')
    // data-* keeps unit metadata close to the control.
    ->data('unit', 'ms')
    ->class(['form-control', 'js-threshold-input']);

Secondary methods worth knowing

  • minLength() and maxLength() Still available from FormControl, even if they are less common on numeric inputs.
  • placeholder(...) Allowed here because Number is not blocked by the base class.
  • renderLabel() Useful in the same way as for the other controls.

Notes and caveats

  • render() temporarily switches the numeric locale to en_US and then restores the original locale.
  • validate() uses truthy checks for min, max, and step-style conditions in the implementation. Values such as min(0) or max(0) deserve extra care because those checks may not run exactly as you expect.
  • Number::validate() is stricter than Text, but it is still not a full business validation layer.

See also: FormControl, Text, Meter, Progress.

Clone this wiki locally