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

Money Bundle Laravel Package

kucharovic/money-bundle

A Symfony bundle that integrates the moneyphp/money library, providing Doctrine DBAL types, form types, validators, and Twig helpers for working with monetary values safely and consistently.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the bundle plus the underlying library. The bundle depends on moneyphp/money, but pin it explicitly so you know which Money API you're coding against. v1.3.0 is the first release with Symfony 5.x support, so constrain to it:

    composer require kucharovic/money-bundle:^1.3 moneyphp/money
    
  2. Register the bundle (Symfony <5.3 style bundles.php; on Symfony Flex it may not auto-register since the bundle predates the recipe era):

    // config/bundles.php
    return [
        // ...
        Kucharovic\MoneyBundle\MoneyBundle::class => ['all' => true],
    ];
    
  3. Look here first. Open vendor/kucharovic/money-bundle/ and read, in this order:

    • MoneyBundle.php — what it actually registers.
    • DependencyInjection/Configuration.php + Extension.php — the config tree, service IDs, and default currency keys.
    • Doctrine/DBAL/Types/ and Form/Type/ — the exact FQCNs you'll import. The bundle is small enough that the source is the documentation; there is no full docs site.
  4. First use case: persist and format an amount. Add a Doctrine column typed as the bundle's money type, bind it to a form, and render it in Twig. That single vertical slice validates DBAL type registration, the form transformer, and the Twig filter all at once — the three things this bundle exists to provide.

Implementation Patterns

Doctrine persistence. Register the DBAL type (or confirm the bundle did it) and map the field:

# config/packages/doctrine.yaml
doctrine:
    dbal:
        types:
            money: Kucharovic\MoneyBundle\Doctrine\DBAL\Types\MoneyType
            currency: Kucharovic\MoneyBundle\Doctrine\DBAL\Types\CurrencyType
use Money\Money;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Column(type: 'money')]
private Money $price;

public function changePrice(Money $price): void
{
    // Money is immutable — always assign a new instance
    $this->price = $price;
}

Keep currency in its own column (or embed it) rather than re-deriving it; Money::add() throws when currencies differ, so a mismatched pair in one row fails only at runtime.

Forms. Use the bundle's form type (alias the import — see gotchas) and give it a currency:

use Kucharovic\MoneyBundle\Form\Type\MoneyType as MoneyFormType;

$builder->add('price', MoneyFormType::class, [
    'currency' => 'EUR',
    // scale/precision options if the bundle exposes them
]);

This is where user input becomes a Money value object, so centralize parsing (decimal separators, thousands separators) here rather than sprinkling Money::of() calls through controllers.

Twig rendering. Use the bundle's filter/function for display and keep raw Money out of templates:

{{ order.total|money }}                       {# bundle filter #}
{{ order.total|money_format('EUR', 'de_DE') }} {# if a locale-aware variant exists #}

If the filter only does currency-symbol substitution, wrap NumberFormatter in your own Twig extension for locale-correct output — don't try to bend the bundle's filter into an i18n formatter.

Service layer. Inject Money/Currency as value objects in domain services; the bundle gives you the persistence and presentation edges, not a factory abstraction. For repeated construction from config, define a small MoneyFactory service that reads the bundle's default currency.

Testing. DBAL types are global state. In a base KernelTestCase/TestCase:

if (!Type::hasType('money')) {
    Type::addType('money', \Kucharovic\MoneyBundle\Doctrine\DBAL\Types\MoneyType::class);
}

Also write one round-trip test (persist → refresh → assert equals()) that locks down the on-disk representation. That representation is effectively your schema contract, and it's the thing most likely to drift if you later swap the type.

Gotchas and Tips

  • v1.3.0 is a narrow release — read it as a floor, not a green light. The only change is Symfony 5.x support; there are no new features, no deprecations, and no API changes versus 1.2.x. Before adopting, confirm the branch you're on: Symfony 5.4 is out of community support, and Symfony 6/7 compatibility is still unverified. If you're on 6/7, the pragmatic move is to copy the handful of classes into src/ under your own namespace rather than patch around the bundle — the surface area is tiny.
  • No Laravel path. This is a Symfony bundle (DI extension, bundles.php, Twig extension): there's no service provider or config-publishing hook. On a Laravel project you can still install moneyphp/money and reuse the ideas here — a custom Eloquent cast for the money column, a Money form request/validator, a Blade directive — but none of the bundle classes will be auto-wired for you.
  • Pin moneyphp/money when you upgrade. Bumping to ^1.3 may resolve a newer Money major as a transitive dependency. Do the lockfile diff and check it before merging; the value-object API you code against is not owned by this bundle.
  • DBAL 3+ type registration changed. Type::addType() is static/global and deprecated in places; prefer the doctrine.dbal.types config above. Registering the same type twice in a test suite throws — always guard with Type::hasType().
  • Name collisions. MoneyType exists in both ...\Doctrine\DBAL\Types and ...\Form\Type, and Symfony ships its own MoneyType/CurrencyType form types. Alias every import or you'll silently wire the wrong one and get confusing transformer errors.
  • Watch the storage format. Whether the DBAL type stores minor units (1000) or a decimal string ("10.00") determines how you can query and index. Verify by inspecting a persisted row before writing any raw SQL or reporting queries against that column.
  • Locale and decimal separators. If the type stores a decimal string, a misconfigured locale can write 10,00 and corrupt the column. Keep the form parser and the DBAL type in agreement, and never let a float touch the amount.
  • No float arithmetic. Money is integer-minor-units by design; any (float) cast in a calculation or an average() in SQL bypasses that guarantee. Do totals in PHP with Money::add()/sum() or accept the precision loss knowingly.
  • Debugging. bin/console debug:container Kucharovic (or --tag=form.type) confirms the form types are registered; bin/console debug:config money (adjust to the actual alias) shows which defaults were applied; doctrine:schema:validate catches a missing type mapping.
  • Extension points. The cleanest customizations are: a subclass of the bundle's DBAL type (to change storage format), a custom DataTransformer for the form type, and a MoneyParser chain for user-entered input. Prefer these over forking the bundle.
  • Config quirks. Defaults like currency and scale are usually read from the bundle config at container compile time — changing them won't retroactively reinterpret already-stored data. Treat a config change as a data migration.
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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
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