stonkeep/moeda-real
stonkeep/moeda-real is a Laravel/PHP package for working with Brazilian Real (BRL). It offers convenient helpers to format monetary values as R$, parse localized currency strings, and handle Real amounts cleanly in your applications. Simple, framework-friendly utilities for currency input, display,...
stonkeep/moeda-real is a Laravel Nova field that renders and edits monetary values in Brazilian Real (R$ 1.234,56) inside Nova's form, index, and detail screens.
Install
composer require stonkeep/moeda-real
Nova packages usually ship a service provider registered via package discovery. Verify with:
php artisan vendor:publish --tag=moeda-real-assets
If no tag exists, check the package's README.md and src/ for a provide() or Nova::script(...) call. On Nova 4+, packages that register assets in a service provider's boot() method work without publishing; on Nova 3, you typically need php artisan nova:publish after composer require.
First use case — attach to a Nova resource
use Stonkeep\MoedaReal\MoedaReal;
public function fields(Request $request): array
{
return [
MoedaReal::make('Preço', 'price')
->rules('required', 'numeric', 'min:0')
->sortable(),
];
}
Point the field at a numeric column (DECIMAL(10,2), BIGINT cents, or FLOAT — see Gotchas). Where to look first in the repo:
src/ — the field class. Confirm the namespace (Stonkeep\MoedaReal vs MateusGalasso\MoedaReal) before importing.resources/js/ — the Vue component. Confirms whether the input is masked live or only formatted on blur.composer.json — Nova version constraint (laravel/nova in require or require-dev). This tells you whether you're on the v3 (Vue 2) or v4 (Vue 3) line.Formatting vs. storage. The field is presentational; the value that hits the database should be a plain number. Two common approaches:
// Storage as reais (decimal 10,2) — most common
MoedaReal::make('Preço', 'price')
->rules('numeric', 'min:0')
->step(0.01);
// Storage as cents (unsigned bigint) — safer for money
MoedaReal::make('Preço em centavos', 'price_cents')
->displayUsing(fn ($value) => $value / 100);
If the field ships a ->step() or ->min() passthrough to the underlying <input type="number">, use it — it prevents the browser from showing validation bubbles with the wrong locale decimal separator.
Index vs. form vs. detail. Nova renders the same field in three contexts. Guard expensive or distracting behavior:
MoedaReal::make('Preço', 'price')
->onlyOnForms() // hide from index/detail if too noisy
->hideWhenCreating() // only editable on update
Currency fields are frequently the opposite problem: read-only on index, editable on forms. Use ->readonly() combined with ->onlyOnDetail() if you want the formatted display everywhere but editing limited.
Computed / derived values. Since the field formats whatever the model returns, it's the natural place to show derived money:
MoedaReal::make('Total (com imposto)', 'total_with_tax')
->resolveUsing(fn () => $this->subtotal * 1.27)
->readonly(),
Validation. The field is a UI wrapper, not a validator. Always pair with numeric and a currency-appropriate rule so 1.234,56 (which is what the user types) doesn't reach the DB as a string:
->rules('required', 'numeric', 'between:0,9999999.99')
Nova Actions / Lenses. On Lenses, the field renders fine as long as the underlying query selects the column. Avoid ->onlyOnForms() in a Lens class — it's a no-op but confuses future readers.
Float vs. DECIMAL. FLOAT/DOUBLE columns will accumulate rounding error once you store 1234.56. Use DECIMAL(15,2) or an integer cents column. If the package formats with PHP's number_format($v, 2, ',', '.'), it rounds at display time, which masks the underlying precision bug — check the actual stored value with tinker before blaming the field.
Locale ≠ Laravel locale. Brazilian formatting (1.234,56) is applied by the field's PHP/Vue code, not by app()->setLocale('pt_BR'). Setting the Laravel locale will not change how this field renders, and vice versa — changing the package's format won't change dates, messages, etc.
Input mask vs. type="number". If the Vue component masks the input (e.g., v-money), browsers with a pt-BR locale may still accept , as a decimal separator in a native number input, producing 1234.56 from one user and 1234,56 from another. If the mask is client-side only, normalize server-side with a prepareForValidation on the FormRequest, or a Mutator/Attribute cast on the model. Relying on either layer alone is a common source of "worked in staging" bugs.
Nova asset caching. After composer update, if the field looks stale or throws a Vue error, run php artisan nova:publish --force and hard-reload the browser. Nova aggressively caches compiled assets in public/vendor/nova.
Nova 3 vs. Nova 4. The package's composer.json pins one. On Nova 4 the Vue component API changed (this.field is still there, but the fieldValue prop and mixin signatures moved). If the component fails to mount, it's almost always a Nova version mismatch, not your code. Check nova:version and re-read the README.
sortable() needs a numeric column. Adding ->sortable() against a VARCHAR column with 1.234,56 will sort lexicographically. Either sort on the numeric column or drop sortable().
Null vs. zero. Decide early whether a blank input should store null or 0. Nova's default ->nullable() behavior sometimes sends '' through prepareForValidation, and DECIMAL will silently coerce '' to 0.00 on certain MySQL modes. Add ->nullable() explicitly and coerce in the request if null is meaningful for your domain.
Extension point. If the field exposes ->currency() or ->locale(), that's your escape hatch for multi-currency apps. If not, the cheapest fix is a subclass in your own app/Nova/Fields/ that overrides resolve() and the component string — but be aware the Vue asset is still the package's, so you can only override formatting logic, not the input mask.
Dependency weight. This package depends on Nova (a paid product). It's not usable in a non-Nova context, and composer require will fail if laravel/nova isn't already in the project. Install Nova first.
Zero stars / zero dependents. Treat it as a single-maintainer utility. Pin the version in composer.json (e.g., "^1.0", not "*"), and consider vendoring the field class if it's essential — a two-file Nova field is trivial to fork and own.
How can I help you explore Laravel packages today?