mibo/currencies
A Laravel/PHP package for working with currencies, offering helpers to define, format, convert, and validate currency codes and amounts in your applications.
mibo/currencies is a plain PHP library — not a Laravel package. There is no service provider, no config file to publish, and no facade. That means installation is a one-liner and the entire integration cost sits in how you wrap it.
composer require mibo/currencies
Where to look first: open src/ before writing any code. This library is a static data set with a thin accessor layer, so the whole API surface is one or two classes. Confirm three things before you commit to it:
Currency value object? This determines whether you can type-hint it or need a DTO of your own.2 for USD, 0 for JPY, 3 for KWD)? If not, you cannot safely store money with it.A first use case that pays for itself immediately: replacing a hardcoded <select> of currencies in a registration or checkout form, plus the matching Rule::in() validation.
// tinker
$all = \Mibo\Currencies\Currencies::all(); // verify exact class/method in src/
// Inspect the shape of a single record before you build anything on top of it.
Sanity-check the output for USD, JPY, and KWD — those three expose exponent bugs immediately.
Wrap it. Do not call it from controllers. With zero stars and zero dependents there is no community vetting, so treat it as a vendor detail behind a small service you own:
namespace App\Support;
use Mibo\Currencies\Currencies; // adjust to the real entry point
class CurrencyRepository
{
public function all(): array
{
return cache()->rememberForever('currencies.all', fn () => Currencies::all());
}
public function codes(): array
{
return array_column($this->all(), 'code');
}
public function find(string $code): ?array
{
return collect($this->all())->firstWhere('code', strtoupper($code));
}
public function exponent(string $code): int
{
return $this->find($code)['exponent'] ?? 2;
}
}
Bind it in a service provider ($this->app->singleton(CurrencyRepository::class)) so tests can swap it and so a future migration to moneyphp/money or symfony/intl is a one-file change.
Validation. Pair the repository with a rule rather than duplicating a code list:
$request->validate([
'currency' => ['required', 'string', Rule::in(app(CurrencyRepository::class)->codes())],
]);
Blade select. One component, reused everywhere, sorted by name with the code as the value — never the symbol:
@foreach (app(CurrencyRepository::class)->all() as $currency)
<option value="{{ $currency['code'] }}">{{ $currency['code'] }} — {{ $currency['name'] }}</option>
@endforeach
Storage and casting. Store the ISO code as a char(3) column and store amounts as integer minor units, deriving the multiplier from the exponent. This is the single most valuable thing the library gives you; skip it and you will ship rounding bugs.
$exponent = $repo->exponent($order->currency); // 2
$minor = (int) round($order->total * (10 ** $exponent));
Seeding a reference table. If you need currencies joinable in SQL (reporting, filters), seed them once and keep the repository as the source of truth:
foreach (app(CurrencyRepository::class)->all() as $currency) {
DB::table('currencies')->updateOrInsert(
['code' => $currency['code']],
['name' => $currency['name'], 'exponent' => $currency['exponent'] ?? 2]
);
}
API resources. Expose code, name, and symbol — but make code the canonical identifier clients persist. Cache the full list at the HTTP layer too; it never changes at runtime.
composer.lock, keep the wrapper thin, and assume you may vendor-fork the data file if the package goes stale. The abstraction above is your insurance policy.symfony/intl or moneyphp/money if you can absorb the weight.$ maps to a dozen currencies, ¥ to both JPY and CNY. Never resolve a currency from a symbol — it is a display-only field. Key everything by the uppercase alpha-3 code.usd, Usd, and USD. Uppercase at the boundary, then look up. If the library's lookup is case-sensitive you will get silent nulls.0; KWD/BHD/JOD are 3. Hardcoding * 100 anywhere is a defect. Also guard against the library omitting the field entirely — default to 2 only as a last resort, not as a silent habit.cache()->rememberForever() on static data is correct, but add a php artisan cache:clear note to your deploy docs, and remember that a long-lived Octane worker holds container singletons across requests — make the repository stateless.CurrencyRepository, never the vendor class. In feature tests, assert against real codes (assertSee('USD')) rather than asserting the library's internal array shape, which you do not control.final or fully static, you cannot subclass it. Your extension point is composition, not inheritance — build your own facade and leave the vendor class untouched.composer.json constraints against your PHP version before installing — small libraries often lag on newer PHP minor versions, and a ^8.1 cap will block a 8.3 project if the maintainer hasn't bumped it.How can I help you explore Laravel packages today?