021/numeric
021/numeric is a lightweight Laravel/PHP package offering straightforward helpers for working with numeric values. It simplifies common number handling and formatting tasks, making it easy to add reliable numeric utilities to your application without extra dependencies.
Install the package with Composer; Laravel's package discovery should register the service provider automatically:
composer require 021/numeric
If you're on an older Laravel version or discovery is disabled, add the provider manually in config/app.php:
'providers' => [
// ...
Numeric\NumericServiceProvider::class,
],
Publish the configuration file so you can inspect the defaults before you build on top of them:
php artisan vendor:publish --tag=numeric-config
Where to look first:
README.md — the authoritative API surface; with a package this small, the README is the documentation.config/numeric.php — locale, default precision, rounding mode, currency, and separator settings.src/NumericServiceProvider.php — confirms whether a facade alias, Blade directive, or global helper is registered.src/ — check whether the entry point is a static utility class, an injectable service, or a Numeric value object; this determines your usage pattern.First use case: formatting a stored price for display. Assuming a static/facade API:
use Numeric\Numeric;
Numeric::format(1234.5); // "1,234.50"
Numeric::currency(1234.5); // "$1,234.50"
Numeric::percent(0.075); // "7.50%"
Then in Blade:
{{ Numeric::currency($order->total) }}
Do this in one place (a Blade component or API resource) rather than scattering calls through views — see the patterns below.
Wrap it, don't sprinkle it. The highest-value pattern with a single-maintainer numeric package is a thin application-level wrapper so a future swap (or a fork) is a one-file change:
// app/Support/Money.php
final class Money
{
public static function format(int|float|string $amount, ?string $currency = null): string
{
return Numeric::currency($amount, $currency ?? config('shop.currency'));
}
}
API resources for output, raw values for storage. Keep the database column numeric and only format at the serialization boundary:
class OrderResource extends JsonResource
{
public function toArray($request): array
{
return [
'total' => (float) $this->total,
'total_display' => Numeric::currency($this->total),
'total_compact' => Numeric::abbreviate($this->total), // "1.2K"
];
}
}
Locale per request. If the package reads locale from config, scope it to the request rather than mutating global config permanently:
public function handle($request, Closure $next)
{
Numeric::locale($request->user()?->locale ?? config('app.locale'));
return $next($request);
}
Admin/reporting views are the sweet spot: tables of totals, deltas, and percentages where consistent thousands separators and rounding matter more than raw precision.
Validation and input. If the package ships rules (e.g. Numeric::rule() or a numeric-style rule), prefer Laravel's built-in numeric/decimal rules for validation and reserve this package for presentation. Mixing the two is a common source of "valid but unformattable" bugs.
Testing assertions. Assert on formatted strings, not floats, in feature tests — that's what users see:
$this->assertSame('$1,234.50', Numeric::currency(1234.5));
Pin a couple of these in a NumericFormattingTest so a config change or locale default doesn't silently break invoices.
Byte sizes and durations are a likely secondary use case if the package exposes fileSize() / duration() style helpers; useful for storage dashboards.
float will round at the edges. For money, store integer minor units (cents) and format the integer, or use bcmath. Never round twice (once by the DB, once by the formatter).intl extension. Locale-aware formatting usually delegates to PHP's NumberFormatter. If ext-intl is missing in your container, you'll get either an exception or a silent fallback to a hardcoded en_US style. Check php -m | grep intl in CI and production.php artisan config:cache freezes config/numeric.php. Changing locale/currency at runtime via config([...]) will appear to work locally and fail in production. Use the package's runtime setter if one exists; otherwise re-deploy after config changes.PHP_ROUND_HALF_UP, but finance often wants half-even (banker's rounding). Set it explicitly in the published config and assert it in a test — this is the classic "one cent off on the invoice" bug.Numeric can clash with your own class or another package. Use the fully-qualified use Numeric\Numeric; import instead of relying on the global alias.precision (calculation) and decimals (display) conceptually separate. Formatting a value to 2 decimals and then doing arithmetic on the string is a real anti-pattern.int vs float vs numeric string), locale, and separators. Echo Numeric::config() (or dump the resolved config) rather than guessing which layer mangled it.Macroable, add project-specific formatters (Numeric::macro('compactCurrency', ...)) instead of forking. If it isn't, the wrapper class above is your extension point.composer.json, cover your usage with tests, and keep the wrapper thin enough that replacing it with NumberFormatter directly is a morning's work.0.x release, expect breaking changes in minor bumps; review the changelog before composer update.How can I help you explore Laravel packages today?