Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Decimal Input Laravel Package

francescobruno-cmv/decimal-input

Decimal Input for Laravel provides a dedicated form field for decimal numbers, validating, formatting, and safely casting user input to and from PHP values so money and precision-sensitive fields stay consistent across Blade forms, requests, and models.

View on GitHub
Deep Wiki
Context7

Getting Started

Read the source before you write a line of Blade. At 0.0.1 the release notes are an auto-generated changelog link and there is no README worth the name, so the package's actual contract lives in vendor/francescobruno-cmv/decimal-input/src. Install it pinned, then read three files in this order:

composer require francescobruno-cmv/decimal-input:0.0.1 --prefer-dist
ls vendor/francescobruno-cmv/decimal-input/src
  1. The service provider — this is the map. It tells you the config key, whether a config file is publishable, what Blade component alias is registered (<x-decimal-input>, <x-decimal>, something else), and the FQCN of any validation rule.
  2. The Blade component / view — confirms whether it renders <input type="text" inputmode="decimal"> (locale-safe, the whole point) or falls back to type="number" (which most browsers refuse to accept a comma in).
  3. The parser/normaliser — the single most important class in the package. Answer one question: does the value pass through (float) or does it stay a string/bcmath? If it's a float anywhere on the write path, stop and keep it out of money fields.

If the provider declares publishes(), publish and commit the config:

php artisan vendor:publish --tag=decimal-input-config

First use case: one price field on an admin create/edit form. Not invoice line items, not a bulk CSV importer — a single bounded form with a DECIMAL(12,2) column behind it, so you can round-trip the value and prove nothing is being silently truncated before you widen adoption.

<x-decimal-input
    name="unit_price"
    :value="old('unit_price', $product->unit_price)"
    :precision="2"
    :min="0"
    step="0.01"
/>

Attribute and alias names above are the conventional shape — confirm them against the component class. Then pair it with a request-level guard so the field is validated server-side even if someone posts to the endpoint directly.

Implementation Patterns

Normalise in prepareForValidation, validate with the rule, persist via a cast. Three separate layers, and the value crosses the string → number boundary exactly once:

class StoreProductRequest extends FormRequest
{
    protected function prepareForValidation(): void
    {
        $this->merge([
            'unit_price' => $this->normaliseDecimal($this->input('unit_price'), 2),
        ]);
    }

    public function rules(): array
    {
        return [
            'unit_price' => ['required', 'numeric', 'min:0', new Decimal(2)],
        ];
    }
}

Keep the normalisation helper on your own base FormRequest (or a small App\Support\DecimalInput adapter) rather than calling the package class from every controller. That adapter is your escape hatch: if the package stalls at 0.0.1, you reimplement ~100 lines behind the same method signature and nothing else moves.

Put display formatting in the model, not the Blade file. The DB column stores a canonical DECIMAL(12,2); the component renders a locale-formatted string; the model is the only place that knows how to cross between them:

protected $casts = ['unit_price' => 'decimal:2'];

public function unitPriceForInput(): string
{
    return number_format((float) $this->unit_price, 2, $this->decimalSeparator(), '');
}

Old-input round-trip. The most common day-one bug is a failed validation re-rendering a different value than the user typed, because the component formats on the way back in. Decide explicitly: either pass old('unit_price') raw and let the component parse it, or normalise it the same way prepareForValidation does. Never mix the two paths.

Locale is a per-request decision, not a config constant. If the app serves both en-US and de-DE, resolve the separator from the authenticated user's locale (or the session) and feed it to both the component and the normaliser. A config-level default is a fallback, not the answer.

Wrap it once. Register your own <x-money-input> that internally delegates to the package component with your house defaults (precision 2, min=0, step=0.01, inputmode="decimal"). Every form in the codebase then targets your alias, and swapping the underlying package later is a one-file change.

Where not to use it: API endpoints receiving strict JSON decimals, console commands, imports. Those are machine inputs with no separator ambiguity — routing them through a human-input formatter adds a failure mode for zero benefit.

Gotchas and Tips

  • Pin exactly, never with a caret. ^0.0.1 in a 0.0.x line effectively means 0.0.1 today but no stability promise tomorrow. Use "francescobruno-cmv/decimal-input": "0.0.1" and treat every bump as a review-required change.
  • Confirm the internal representation before trusting money. If values are held as PHP floats anywhere between parse and persist, the package is fine for quantities and display and disqualifying for currency. brick/math, bcmath strings, or integer minor units are safe; float is not.
  • step and precision must agree. precision=2 with step="0.1" makes browsers with native constraint validation reject 12.34 with a vague "Please enter a valid value" — and if your form carries novalidate, the same mismatch is silently ignored instead. Keep them derived from one source of truth.
  • Test the ambiguous strings explicitly, not just the happy path: "1.234,56", "1,234.56", "1 234,56", "1\u{00A0}234,56" (non-breaking space), ".5", "1e3", "-0", empty string, and null. What comes out the other side is the package's real spec, and 0.0.1 almost certainly doesn't document it.
  • Empty string vs 0. For a nullable DECIMAL column, an empty input must become null, not 0.00. Check what the normaliser returns for "" — this bug shows up months later as zero-priced line items.
  • Precision overflow behaviour is undocumented. 12.345 against precision=2 may round half-up, truncate, or throw. Test it, and decide which you actually want; round-half-up at the boundary is usually correct for money, but truncation is common in naive implementations and it corrupts totals quietly.
  • The browser's type="number" ignores commas in nearly every locale. If the component ever renders type="number", users typing 1,50 get an empty field posted and your required rule fires on a value they can see on screen. Verify the rendered markup; prefer type="text" inputmode="decimal".
  • Guard the bypass paths. The Blade component protects the form; it protects nothing else. Anything that writes the same column — an API controller, an artisan importer, a Nova/Filament resource — needs the same normalisation, or you get a column with two dialects in it.
  • Round-trip through the actual column in a test. Write "1.234,56" through the form path, re-read from the database, re-render into the input, and assert you get "1.234,56" back and a DECIMAL value of 1234.56. Unit tests on the parser alone will not catch a driver-level truncation.
  • php artisan config:cache after publishing config, and remember that a cached config masks a missing .env key — the component will render with the package default precision in production and your locally-tested formatting will look different.
  • Accessibility and asset pipeline are unverified. If the component ships JS or a CSS asset, check whether it needs a build step and whether it sets aria-*/label association. At this maturity, budget for adding your own <label for> and error-text wiring rather than assuming it's there.
  • Watch the repo, not just the changelog. Releases have no narrative, so triage means diffing tags yourself. Subscribe to notifications on the repository for one named owner and re-evaluate at 0.1.0 — or immediately if you see a commit touching the parser.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core