lathanhvien/nova-text-currency
A Laravel Nova field for entering and displaying monetary values as formatted text, making currency input simple in your admin panels. Add it to Nova resources for a lightweight text-based currency field.
This is the initial v1.0.0 release, adding two capabilities that matter for money and quantity fields: dynamic input with thousand separators and currency symbol support.
composer require <vendor>/<package>
php artisan vendor:publish --tag=<package>-config
, vs . vs space). vs ,)1234567 becomes 1,234,567 or $1,234,567.00 — while the value you validate and persist remains a plain numeric string.A reasonable first integration looks like:
<form method="POST" action="{{ route('products.store') }}">
@csrf
<x-dynamic-input
name="price"
currency="USD"
:decimals="2"
/>
@error('price') <span>{{ $message }}</span> @enderror
<button type="submit">Save</button>
</form>
Before wiring it into a real form, verify what the component actually submits — formatted text ($1,234.56) or a normalized value (1234.56). That single detail drives most of the implementation patterns below.
Treat the browser formatting as presentation only. Strip currency symbols, thousand separators, and non-breaking spaces before validation and before writing to the database:
$raw = $request->input('price'); // "$1,234.56"
$normalized = str_replace(
['$', ',', "\u{00A0}", ' '],
'',
$raw
); // "1234.56"
$request->merge(['price' => $normalized]);
$validated = $request->validate([
'price' => ['required', 'numeric', 'min:0'],
]);
If the package exposes a formatter/normalizer helper or cast, use that instead of hand-rolled str_replace so behaviour stays consistent with the input component.
Use the component for both create and edit forms. On edit, seed it with the raw numeric value, not the pre-formatted string, and let the component handle display formatting:
<x-dynamic-input
name="price"
:value="old('price', $product->price)"
currency="USD"
:decimals="2"
/>
Use the same config/currency source for display in index and show views so the formatted input and the read-only output agree.
Bind with wire:model and normalize in updated hooks or in a computed accessor. Because formatted input changes on every keystroke, prefer deferred updates (wire:model.blur or wire:model.defer) unless you genuinely need live recalculation:
<x-dynamic-input
name="price"
wire:model.blur="price"
currency="USD"
/>
public function updatedPrice($value): void
{
$this->price = $this->normalizeMoney($value);
}
Drive the symbol and separators from config or the user's locale rather than hard-coding them in markup. A single config default plus per-instance overrides covers most cases:
<x-dynamic-input name="price" :currency="config('shop.currency')" />
<x-dynamic-input name="price" currency="EUR" locale="de" />
decimal/integer columns, never as formatted strings.numeric validation. A posted value of 1,234.56 fails Laravel's numeric rule. Normalize before validation, or add a custom rule that strips separators first.1.234,56 means one thousand two hundred thirty-four. Do not assume , is always a thousands separator. Make the separator explicit in config and test at least one comma-decimal locale.<input> value, it will be submitted with the form. Either render the symbol as a separate prefix/suffix element outside the input, or strip it server-side. Confirm which approach the component takes in v1.0.0.updated hooks can trigger another update cycle. Guard against re-normalizing an already-normalized value and compare before assigning.init hook avoid dead inputs."", "-", "$", and "." without throwing. Decide whether empty normalizes to null or 0 and be consistent with your database defaults.How can I help you explore Laravel packages today?