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 Money Laravel Package

ymsoft/filament-money

Filament Money provides currency-aware money input fields and table columns for Laravel Filament admin panels, making it easy to enter, format, and display monetary values consistently with locale-friendly output.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require ymsoft/filament-money
php artisan vendor:publish --tag="filament-money-config"   # if a config is shipped

Filament auto-discovers the service provider. If the package registers views/assets, add it to your panel:

// app/Providers/Filament/AdminPanelProvider.php
public function panel(Panel $panel): Panel
{
    return $panel
        ->plugin(\Ymsoft\FilamentMoney\FilamentMoneyPlugin::make());
}

Where to look first

  1. config/filament-money.php — default currency, decimal separator, and the minor-unit exponent live here. Set the default currency before writing any field, because it silently becomes the currency for every field that doesn't override it.
  2. The package's src/ tree — at ~10 stars and 0 dependents there is essentially no community knowledge base. The source is the documentation. Read the form component, the table column, and the cast (three or four files) before designing your schema.
  3. composer.json — confirm the Filament major version constraint. A plugin released in 2026 is almost certainly Filament v4 (Filament\Schemas), and the v3/v4 namespace split is the most common install failure.

First use case: a price on a product

The package's whole value proposition is that it stores money as integer minor units (cents) in the database while the Filament form/table speaks major units (dollars) to the user. Wire up the three pieces together:

// migration
$table->unsignedBigInteger('price');       // 1999, not 19.99
$table->char('currency', 3)->default('USD');

// app/Models/Product.php
protected $casts = [
    'price' => \Ymsoft\FilamentMoney\Casts\MoneyCast::class,
];

// app/Filament/Resources/ProductResource.php
use Ymsoft\FilamentMoney\Forms\Components\MoneyInput;
use Ymsoft\FilamentMoney\Tables\Columns\MoneyColumn;

MoneyInput::make('price')
    ->currency('USD')
    ->required(),

MoneyColumn::make('price')
    ->currency('USD'),

Verify the exact class names against the repo's src/ — with this few stars the README may lag the code, and the namespaces are the one thing you cannot guess cheaply.

Implementation Patterns

One currency per record, not per field. If your model can hold more than one currency, store it in a column and drive the components from the record instead of hard-coding:

MoneyInput::make('price')
    ->currency(fn (?Product $record) => $record?->currency ?? 'USD'),

MoneyColumn::make('price')
    ->currency(fn (Product $record) => $record->currency),

Hard-coded ->currency('EUR') on a field whose rows are in USD is the single most common source of "the number is right but the symbol is wrong" bugs.

Keep the cast at the persistence boundary. The idiomatic layering is: form component → MoneyInput state (major units, string) → cast → integer minor units in the DB. Do your arithmetic in the domain layer with a real money library (moneyphp/money, brick/money), not with the cast's return value:

$total = $order->lines->reduce(
    fn (Money $c, Line $l) => $c->add(new Money($l->price_minor, new Currency($order->currency))),
    new Money(0, new Currency($order->currency)),
);

MoneyCast is a storage/formatting concern; it is not a calculator.

Bulk columns and summaries. Table columns usually delegate formatting to Filament. If MoneyColumn supports summarize(), remember that sum() and avg() are computed by the database over the integer column and returned as a plain int — the cast does not apply. Either accept raw cents output or format the summary yourself:

MoneyColumn::make('price')
    ->summarize(
        Summarizers\Sum::make()
            ->formatStateUsing(fn ($state) => '$'.number_format($state / 100, 2))
    ),

Filters need conversion in both directions. A ->query() closure receives what the user typed in major units:

Filter::make('price')
    ->form([MoneyInput::make('min')])
    ->query(fn (Builder $q, array $data) => $q->when(
        $data['min'] ?? null,
        fn ($q, $v) => $q->where('price', '>=', (int) round($v * 100)),
    )),

Repeaters and nested state. MoneyInput inside a Repeater inherits the repeater's state path, so casting/formatting hooks that assume a flat price key break. Prefer calling the component's public API rather than overriding afterStateHydrated yourself.

Testing. Livewire form tests fill major units and assert against minor units:

livewire(CreateProduct::class)
    ->fillForm(['price' => '19.99'])
    ->call('create')
    ->assertHasNoFormErrors();

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

Writing 1999 into fillForm is the classic mistake — the component will treat it as $1,999.00.

Exports, APIs, and seeders bypass the plugin entirely. They see the raw integer. Document that price is minor units in your API resource definitions and cast explicitly there; don't assume MoneyCast travels with toArray().

Gotchas and Tips

  • Filament v3 vs v4. Check the constraint before anything else. v4 moved schema components under Filament\Schemas; a v3-only plugin will install fine and then throw Class not found on the first form render. A 2026 release date is a good sign, but confirm.
  • Never let a float touch persistence. The component should hand back a string in major units. If you find yourself doing (int) ($state * 100), you get 1998 for 19.99 on some inputs. Round only at the boundary and prefer string/decimal math.
  • / 100 is not universal. JPY has 0 decimal places, KWD/BHD have 3. If the package reads the exponent from config or a currency table, use that; hard-coded 100 produces wrong values on non-2-decimal currencies. If it doesn't, this is your first extension point.
  • Column width. unsignedBigInteger is right; unsignedInteger caps you at ~$42.9M in cents and overflows silently in MySQL's non-strict mode.
  • Unsigned + refunds. Discounts and credit notes are negative. Use a signed bigInteger for anything that isn't strictly a price.
  • Locale decimal separators. Many money inputs accept only .. Users on a , locale will type 19,99 and either get a validation error or, worse, 1999. Verify with an actual browser in your target locale, not just a Livewire test.
  • State hydration mismatch. After Livewire hydrates, form state may be a string ("19.99") while a freshly-loaded record's state is int (1999) if the cast is missing from the model. If the input shows 19.99 on edit but 1999.00 on create, your cast is on the wrong model or the field name doesn't match the column.
  • Aggregates bypass casts. Model::sum('price'), withSum(), and Filament summarizers all return raw integers. Any formatter you attach to a column does not run on them.
  • Config caching. default_currency read at boot is cached by config:cache. Changing it in .env during a deploy without php artisan config:clear silently keeps the old symbol.
  • Extension points to reach for first: the component's ->view() override (fastest way to fix a formatting/markup quirk without a fork), the config file for currency/format defaults, and a thin custom cast wrapping the package's cast if you need multi-currency attributes. With 10 stars and 0 dependents, fork-and-patch is a legitimate strategy — the surface is small, and you will not find Stack Overflow answers. Pin the version in composer.json and note the fork in your README.
  • Debugging checklist when a value looks wrong: (1) is the cast present on the model? (2) does the field name match the column exactly? (3) what's the raw DB value? (4) what does dd($livewire->data) show before save? That sequence resolves almost every issue with this class of package.
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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle