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

Filament Brl Money Field Laravel Package

a21ns1g4ts/filament-brl-money-field

A Filament form field for Brazilian Real (BRL) monetary values, offering currency masking, formatting, and smooth integration with Filament forms. Makes entering and displaying amounts in Brazilian currency simple in Laravel admin panels.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and confirm compatibility before writing any form code:

composer require a21ns1g4ts/filament-brl-money-field

Then, in order:

  1. Inspect composer.json for the supported Filament (^3 vs ^4), Laravel, Livewire, and PHP constraints. The field APIs differ between Filament majors — do not assume v1.0.1 supports yours.
  2. Read src/ for the exact field class namespace and available fluent methods (prefix, suffix, mask, dehydrateStateUsing, etc.). The README is thin and this package has one star, so treat the source as the source of truth.
  3. Publish any config if present:
    php artisan vendor:publish --tag="filament-brl-money-field-config"
    
  4. Decide the persistence strategy before touching the form. For BRL money prefer integer cents (bigInteger) or decimal(15,2) — never float.

First use case — a resource form field with a matching model cast:

use A21ns1g4ts\FilamentBrlMoneyField\Fields\BrlMoneyField;

BrlMoneyField::make('price')
    ->label('Preço')
    ->required();
protected $casts = [
    'price' => 'integer', // stored in cents
];

Smoke-test with three inputs — 0, 1.234,56, and a negative like -10,00 — then check both the Livewire state and the raw DB value. That single test surfaces most mask/persistence bugs.

v1.0.1 is a patch release with no published notes beyond a diff link. Pin to ^1.0, review the v1.0.0...v1.0.1 diff once, and treat upgrades as bug-fix-only.

Implementation Patterns

Field in form, standard Filament in table

Keep editing localized to the package and display with core Filament so exports/sorting behave normally:

// Form
BrlMoneyField::make('price')->required();

// Table
TextColumn::make('price')
    ->money('BRL', divideBy: 100)
    ->sortable();

Normalize at the dehydration boundary — only once

If the field already dehydrates to cents, do nothing. If it doesn't, convert explicitly and verify against source first to avoid double-multiplying:

BrlMoneyField::make('price')
    ->dehydrateStateUsing(function ($state) {
        $normalized = str_replace(['.', ','], ['', '.'], (string) $state);
        return (int) round(((float) $normalized) * 100);
    });

Reactive Livewire forms

When another field depends on the amount, use live() + afterStateUpdated(), normalizing before arithmetic:

BrlMoneyField::make('price')
    ->live()
    ->afterStateUpdated(function ($state, Set $set) {
        $cents = /* normalize $state */;
        $set('tax', (int) round($cents * 0.1));
    });

Custom validation via normalized value

Standard numeric / decimal rules reject "1.234,56", so validate the converted value:

BrlMoneyField::make('price')
    ->rules([
        fn () => function ($attribute, $value, $fail) {
            if (/* normalized cents */ < 0) {
                $fail('O valor não pode ser negativo.');
            }
        },
    ]);

Feature-test through Livewire, assert the DB

Skip unit-testing the mask itself. Exercise the real form flow and assert persistence:

Livewire::test(CreateProduct::class)
    ->fillForm(['price' => '1.234,56'])
    ->call('create')
    ->assertHasNoFormErrors();

$this->assertDatabaseHas('products', ['price' => 123456]);

Integration with a decimal(15,2) column

If you can't use cents, pick decimal strings consistently — field dehydrates to "1234.56", model casts to decimal:2, and the table uses money('BRL') without divideBy. Do not mix conventions across resources.

Gotchas and Tips

  • Masked state vs stored state is the primary risk. . = thousands, , = decimal. Browser input, Livewire state, validation, and DB each may see a different representation. Log::info($state) during development until you're confident.
  • Never store money as float. Use integer cents or decimal. Convert once at dehydration and never again.
  • Check for built-in dehydration first. If the field already outputs cents, adding your own dehydrateStateUsing() multiplies by 100 twice — a silent 100x bug.
  • Filament v3 vs v4 field APIs diverge. A patch release will not widen the major constraint. Read composer.json and don't assume compatibility.
  • Livewire rehydration can reset the mask. After $refresh, dependent field updates, or partial re-renders, confirm the formatted value survives. Test with ->call('$refresh').
  • numeric/decimal rules reject the masked string. Either normalize in a custom rule or validate a separate hidden raw field.
  • ext-intl and app locale. Formatting may depend on intl or pt_BR locale. Behavior can differ between local and production — verify PHP extensions on the server.
  • Edge values. Test 0,00, -10,00, empty input, and a value with only whitespace. Mask libraries sometimes swallow the minus sign or coerce empty to 0.
  • Column type alignment. If the column is decimal(15,2), dehydrate to a decimal string, not cents. If bigInteger, dehydrate to cents only. Enforce with a model cast and keep all resources consistent.
  • Extension points. If the affix/mask doesn't fit, look for configurable prefix, suffix, or protected methods before subclassing. Subclassing a Filament field is brittle across minor releases — prefer config or formatStateUsing() over overriding internals.
  • Small package, small surface. One star and minimal docs mean source reading is part of the day-to-day cost. For anything beyond simple price inputs, weigh the field against a custom TextInput with a mask + a single dehydrateStateUsing() — often less risk than an unmaintained dependency.
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.
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge