laravelwebdev/numeric
LaravelWebDev Numeric adds convenient number helpers to Laravel: format, parse, and validate numeric values with a clean, framework-friendly API. Lightweight and easy to install, it keeps common numeric tasks readable and consistent across your app.
composer require laravelwebdev/numeric
FormRequest, since packages of this size occasionally move the class between minor releases.tinker before adopting it, because the rule intentionally deviates from Laravel's built-in numeric:
Validator::make(['n' => '-12.5'], ['n' => [new \LaravelWebDev\Numeric\Rules\Numeric]])->passes();
"1.2.3"), for example an integer quantity, an account balance, or an adjustment/delta value. Prior to 1.0.1 that last case — negative deltas, refunds, temperature, offsets — was rejected; now it validates.As a rule object in a FormRequest (the idiomatic path):
public function rules(): array
{
return [
'adjustment' => ['required', new Numeric()],
'quantity' => ['required', 'integer', 'min:0', new Numeric()],
];
}
Composed with range rules. The rule answers "is this a number?", not "is this an acceptable number?". Always pair it with min/max/between when the domain is non-negative — the 1.0.1 change means -5 now passes a rule that previously blocked it:
'price' => ['required', new Numeric(), 'min:0', 'decimal:0,2'],
String-syntax registration. If the package exposes a rule alias (check the README / service provider), register it once in a ServiceProvider::boot() so 'amount' => ['numeric_strict'] works everywhere and stays greppable:
Validator::extend('numeric_strict', fn ($attribute, $value, $parameters, $validator) => /* ... */);
Normalising before validating. Form input arrives as a string; a leading +, a non-breaking space, or a thin space from a copy-paste will not validate. Normalise in prepareForValidation() rather than loosening the rule:
protected function prepareForValidation(): void
{
$this->merge([
'adjustment' => is_string($this->adjustment)
? trim(str_replace([' ', "\u{00A0}"], '', $this->adjustment))
: $this->adjustment,
]);
}
Testing as a matrix. Because the accepted grammar is the entire point of the package, pin it down with a data provider that includes the negative edge cases introduced in 1.0.1:
yield 'negative int' => ['-42', true];
yield 'negative float' => ['-0.5', true];
yield 'negative zero' => ['-0', true];
yield 'lone minus' => ['-', false];
yield 'minus with gap' => ['- 5', false];
yield 'thousands sep' => ['1,000', false];
yield 'exponent' => ['1e5', false];
yield 'comma decimal' => ['1,5', false];
API/DTO layer. When the same rule guards both an HTTP endpoint and a queued job payload, extract it to a named constant or a small factory (e.g. Numeric::forSignedAmount()) so the 1.0.1 semantics are applied consistently instead of being re-declared per controller.
1.0.0 → 1.0.1 widens the accepted language of the rule. Any test or downstream assumption written against 1.0.0 that asserted -1 is invalid will now fail — that is expected, but audit for it before upgrading.min:0. This is the single most likely production surprise from 1.0.1.-0 and -0.0 now validate. If you (int)/(float) the input afterwards, -0 collapses to 0; if you persist the raw string to a decimal column, check how your database normalises it, because -0.00 and 0.00 may compare unequal in later reporting queries.- or a sign separated by whitespace must still fail. Verify this in your own suite — it is exactly the kind of edge case that a permissive regex introduced alongside negative support can accidentally allow. "- 5", "--5", "5-", and "−5" (U+2212 minus) are all worth an assertion.− (U+2212) or an en dash. Decide explicitly whether to normalise in prepareForValidation() or reject; the rule will almost certainly reject, so surface a clear message rather than the default one.nullable vs. empty string. Laravel's ConvertEmptyStringsToNull middleware handles '' before validation, but JSON APIs bypassing middleware can still send "". Combine with nullable and test the raw JSON path separately.int/float in a FormRequest::passedValidation() or a DTO constructor before validation, you change both the type and the value the rule sees. Keep the rule on the raw input and cast afterwards.max: / digits_between: and store as strings, since float casting loses precision long before the rule complains.message(). If your rule class supports a custom message (or you add a :attribute translation key), do it — otherwise support tickets for - vs − are indistinguishable at the UI level.composer.json ("laravelwebdev/numeric": "^1.0") and re-run your validation matrix on any future patch, given that 1.0.1 showed the package is willing to change accepted input in a patch release.How can I help you explore Laravel packages today?