bluebaytravel/dosh
Dosh is a Laravel package for working with money in your application. It provides convenient helpers for formatting and displaying monetary values, helping keep prices consistent, readable, and currency-aware across your app.
bluebaytravel/dosh is a deliberately small, framework-agnostic currency formatter. Treat it as a thin presentation helper, not a money library — it turns a numeric amount plus a currency code into a localized display string and does nothing else.
composer require bluebaytravel/dosh
Where to look first
README.md — the entire public API is usually demonstrated in one or two snippets; the surface area is tiny.src/ — with a package this small you can read every class in a few minutes. Confirm the exact method signature, whether it is static or instance-based, and whether a Laravel service provider / facade is shipped (check composer.json for illuminate/support).composer.json — check the PHP version constraint and whether it depends on ext-intl (almost certainly) versus a pure-PHP fallback.First use case
Formatting a stored price for display in a storefront or invoice. The canonical flow is: persist amounts as integers in minor units (pence/cents), then format only at the edge of the application:
use BlueBayTravel\Dosh\Dosh;
// Illustrative — verify the exact signature in src/ before relying on it.
$formatter = new Dosh();
echo $formatter->format(129900, 'GBP'); // "£1,299.00"
If you are in Laravel, register it once in a service provider so you inject a configured instance rather than new-ing it in views:
$this->app->singleton(Dosh::class, fn () => new Dosh());
Format at the presentation edge, never in models or queries. Keep price as an integer column and expose a formatted string via an accessor, view model, or API resource. Avoid storing pre-formatted strings — they are locale- and ICU-version-dependent.
Wrap it in your own adapter. Because the package is tiny and low-traffic, insulate your codebase behind one class you own:
final class PriceFormatter
{
public function __construct(private Dosh $dosh, private string $locale = 'en_GB') {}
public function format(int $minorUnits, string $currency, int $scale = 2): string
{
$amount = $minorUnits / (10 ** $scale);
return $this->dosh->format($amount, $currency);
}
}
This gives you a single place to swap in Brick\Money or NumberFormatter later, and a single place to handle zero-decimal currencies.
Blade component for views. Keep formatting out of templates beyond a single call:
{{-- resources/views/components/price.blade.php --}}
@props(['minor', 'currency' => 'GBP'])
<span {{ $attributes }}>{{ app(\App\Support\PriceFormatter::class)->format($minor, $currency) }}</span>
<x-price :minor="$product->price" currency="GBP" />
API resources: return both forms. Machine consumers want the raw value; humans want the formatted one. Returning only a formatted string forces clients to parse localized text.
return [
'amount' => $this->price, // 129900
'currency' => 'GBP',
'formatted' => $formatter->format($this->price, 'GBP'),
];
Locale per request. If you serve multiple locales, resolve the locale from the user/request and pass it into your formatter wrapper rather than relying on a global default. Test that de_DE renders 1.299,00 € and en_GB renders £1,299.00.
Testing. Assert on formatted output for a fixed locale and ICU version, and assert on raw minor units everywhere else. Keep a small table-driven test covering: zero, negative, large values, zero-decimal currency (JPY), and an unknown currency code.
Brick\Money / moneyphp/money and let Dosh (or your adapter) handle only the final string.12.99 as a float can surface as 12.989999... depending on the formatter's rounding. Store integers or strings and divide at the last moment, or pass a decimal string if the API accepts one.amount / 100 will be wrong by two orders of magnitude. Guard this in your adapter with a scale map.1,234.56 vs 1.234,56 vs 1 234,56 comes from the locale. Decide whether your app is single-locale (hardcode en_GB) or user-locale driven, and be consistent.ext-intl dependency. Output can shift between ICU versions bundled with different PHP builds. If your CI and production images differ, golden-string tests can fail for reasons unrelated to your code. Pin the base image or assert loosely.GBP, USD). Lowercase codes may silently fall back to a generic symbol or throw — verify behavior for unknown codes and decide whether to fail loudly or degrade to the code itself.-£5.00, (£5.00), or £-5.00. Formatters differ; wrapping the output is usually easier than fighting the library.php -m | grep intl first. Compare Dosh's output against a direct numfmt_format_currency() call to isolate whether the problem is your input or the underlying formatter.£1.2k), implement it in the adapter on top of the formatted string or bypass Dosh entirely.composer.json, read the diff before upgrading, and keep the adapter so a fork or replacement is a one-file change.How can I help you explore Laravel packages today?