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

Currency Bundle Laravel Package

lexik/currency-bundle

Symfony bundle for multi-currency apps: manages currency definitions, fetches exchange rates from configurable providers, converts amounts, and integrates with Twig, forms, and Doctrine. Ships with caching, CLI tools, and an extensible provider architecture. Ideal for e-commerce and billing.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the bundle with Composer and register it in the Symfony kernel:

composer require lexik/currency-bundle
// app/AppKernel.php
new Lexik\Bundle\CurrencyBundle\LexikCurrencyBundle(),

Add the minimal configuration. The two settings that matter first are the base currency and the currencies you want to support:

# app/config/config.yml
lexik_currency:
    default_currency: EUR
    currencies:
        - EUR
        - USD
        - GBP

The bundle persists exchange rates, so create/update the rate table and then import the first set of rates:

php app/console doctrine:schema:update --force
php app/console lexik:currency:rate:update

Check the exact command name in your version with php app/console list lexik. Put the update command on a daily cron, because ECB-sourced rates are not fetched live on every request.

The first day-to-day use case is displaying a product price in the visitor’s currency:

{% set converted = product.price|currency_convert('USD') %}
{{ converted|currency_format('USD') }}

Where to look first in the source:

  • DependencyInjection/Configuration.php — the real config keys and defaults.
  • Resources/config/services.xml — service IDs, especially lexik_currency.converter.
  • Currency/Converter.php — conversion logic and argument order.
  • Twig/CurrencyExtension.php — exact filter names and signatures.
  • Command/ — rate refresh command and provider wiring.
  • Entity/CurrencyRate.php and its repository — how latest rates are selected.

Important context: this is a Symfony 2-era bundle, not a Laravel package. In Laravel you will not get auto-discovery, a service provider, or Blade directives. For a Laravel app, either port the small converter/rate-provider logic or bind the underlying classes manually in a service provider.

Implementation Patterns

Inject the converter, not the Twig filter. Use the converter service in application code and keep Twig for presentation:

use Lexik\Bundle\CurrencyBundle\Currency\Converter;

class PricePresenter
{
    private $converter;

    public function __construct(Converter $converter)
    {
        $this->converter = $converter;
    }

    public function convert(int $amount, string $targetCurrency, string $sourceCurrency = null): float
    {
        return $this->converter->convert($amount, $targetCurrency, $sourceCurrency);
    }
}

In Symfony, wire it with the lexik_currency.converter service ID or the concrete class. In Laravel, bind it as a singleton and resolve it from the container:

$this->app->singleton(Converter::class, function ($app) {
    // construct with the rate repository/provider used by your port
});

Store money in a single base currency. Persist prices, order totals, and invoices in your accounting/base currency. Convert only at the display boundary or when the customer explicitly chooses a checkout currency. This avoids compounding rounding errors across the application.

Use a presenter or view model for repeated conversions. Do not scatter currency_convert calls through templates. Convert once in a presenter, pass both the original and converted values to the view, and keep the formatting decision in one place:

final class MoneyView
{
    public $amount;
    public $currency;
    public $formatted;

    public static function fromBase(Converter $converter, float $amount, string $currency): self
    {
        $converted = $converter->convert($amount, $currency);

        return new self($converted, $currency, number_format($converted, 2) . ' ' . $currency);
    }
}

Snapshot the rate on orders and invoices. Never recalculate a historical order using today’s rate. When an order is placed, store:

  • the source currency,
  • the target/display currency,
  • the exchange rate used,
  • the converted total,
  • the conversion timestamp or rate date.

That snapshot is your audit trail when rates move or the rate table is refreshed.

Use Twig filters as a chain, not as a combined magic filter. The common pattern is convert first, then format:

{% set usd = product.price|currency_convert('USD') %}
{{ usd|currency_format('USD') }}

Be explicit about the from currency when it is not the configured default. The converter signature is typically convert($amount, $to, $from = null), so omitting $from means “from the default currency.”

Refresh rates on a schedule and monitor the command. A daily cron is the normal setup:

15 17 * * 1-5 cd /path/to/app && php app/console lexik:currency:rate:update >> var/log/currency.log 2>&1

ECB publishes rates on working days, so weekend and holiday runs may not add new data. Alert on command failure or on “latest rate is older than N days.”

Implement a custom rate provider when ECB is not enough. If you need a paid feed, crypto rates, or manually maintained rates, implement the bundle’s rate-provider interface and override the provider service. Keep the provider focused on fetching and normalizing rates; let the bundle’s converter handle cross-rate calculation.

Test with fixed rates, not live HTTP. In tests, seed the rate table with known values or replace the converter/provider with a fake. This keeps assertions deterministic:

$converter->convert(100, 'USD', 'EUR'); // assert against your seeded rate

For Laravel integration, wrap the package. Expose a Currency facade or helper, bind the converter in a service provider, and create a Blade directive only if templates need it. Schedule the rate refresh through Laravel’s console kernel instead of Symfony cron conventions.

Gotchas and Tips

This is a Symfony bundle, not a Laravel package. There is no composer.json auto-discovery, no Laravel service provider, and no Blade extension. Budget time for manual wiring or for extracting the converter logic. For a modern Laravel app, moneyphp/money plus a dedicated exchange-rate client is often a cleaner long-term choice.

ECB rates are EUR-based. Cross-currency conversions such as GBP to USD usually go through EUR. That introduces an extra rounding step and can produce small discrepancies versus a direct GBP/USD feed. Do not promise exact rates; define a rounding policy.

Rates are historical snapshots, not live values. The converter reads the latest stored rate. If your cron stops, prices silently become stale. Add a freshness check and a fallback policy: block checkout, use the last known rate with a warning, or fall back to the base currency.

Missing currencies or rates throw exceptions. A currency may be enabled in config but have no stored rate yet. Catch conversion exceptions at the boundary and decide whether to display the base currency, hide the price, or return an error. Do not let a missing rate break an entire product listing.

Watch the amount units. The converter works with numeric amounts, but your application may store minor units (cents) or major units (dollars). Pick one convention and enforce it. Mixing them produces prices that are off by a factor of 100.

Avoid floats for persisted money. The bundle’s converter returns numeric values, but for storage and accounting use integer minor units or a decimal library. Convert to a float only at the final formatting step.

currency_format formats; it does not necessarily convert. The reliable pattern is convert then format. If you chain them in the wrong order you will format the original amount with the target currency symbol.

Argument order is easy to reverse. The converter is typically convert($amount, $to, $from = null), while human intuition often reads “from, to.” Write a small wrapper in your app with named arguments or explicit parameter names so call sites are unambiguous.

Run schema migrations, not just schema:update, in production. The bundle needs its rate table. Treat the entity mapping as part of your deployment and include it in migrations. Also plan to prune old rate rows if the table grows indefinitely.

Clear the Symfony cache after config changes. Currency lists and service overrides are cached. A changed currencies list that “does nothing” is often a stale container cache.

Overriding services is the main extension point. To customize behavior, decorate or replace lexik_currency.converter, the rate provider, or the Twig extension in your service configuration. Prefer decoration over forking the bundle.

The project is small and old. With low stars and Symfony 2-era code, expect limited maintenance and modern-framework incompatibilities. Pin versions, read the source before relying on undocumented behavior, and keep your integration layer thin so you can replace it later.

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.
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
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons