|
1 | | -# <img src="https://s3.amazonaws.com/swap.assets/swap_logo.png" height="30px" width="30px"/> Symfony Swap |
| 1 | +# Symfony Swap |
2 | 2 |
|
3 | 3 | [](https://github.com/florianv/symfony-swap/actions/workflows/tests.yml) |
4 | 4 | [](https://github.com/florianv/symfony-swap/actions/workflows/psalm.yml) |
5 | 5 | [](https://packagist.org/packages/florianv/swap-bundle) |
6 | 6 | [](https://packagist.org/packages/florianv/swap-bundle) |
7 | 7 |
|
8 | | -Swap allows you to retrieve currency exchange rates from various services such as **[Fixer](https://fixer.io/)**, **[Currency Data](https://currencylayer.com)** |
9 | | -or **[Exchange Rates Data](https://exchangeratesapi.io)** and optionally cache the results. |
| 8 | +> _Drop-in Symfony bundle for currency conversion. Multi-provider exchange rates with fallback, caching, and Symfony Cache integration. Maintained since 2014._ |
10 | 9 |
|
11 | | -## QuickStart |
| 10 | +**Install the bundle, drop a `florianv_swap.yaml` in `config/packages/`, and the `florianv_swap.swap` service is ready to inject. No service container plumbing, no boilerplate.** |
| 11 | + |
| 12 | +Symfony Swap is a drop-in package for **Symfony currency conversion**. Install it, configure providers in `config/packages/florianv_swap.yaml`, and pull **Symfony exchange rates** from multiple providers in one call. The bundle integrates with Symfony Cache out of the box and supports Symfony 6.4 / 7 / 8. Used in real-world Symfony applications since 2014. |
| 13 | + |
| 14 | +## 💡 What is Symfony Swap? |
| 15 | + |
| 16 | +- Symfony Swap is the Symfony integration of [Swap](https://github.com/florianv/swap), the PHP currency conversion library. |
| 17 | +- It registers a `florianv_swap.swap` service in the container (`Swap\Swap` class). |
| 18 | +- Configuration lives in `config/packages/florianv_swap.yaml`. |
| 19 | +- Caching uses Symfony Cache (`array`, `apcu`, `filesystem`, or any PSR-16 service ID). |
| 20 | +- Providers are tried in priority order (higher priority first). |
| 21 | + |
| 22 | +## 🎯 When should you use Symfony Swap? |
| 23 | + |
| 24 | +- Use Symfony Swap when you need exchange rates inside a Symfony application: localized prices, invoice totals, multi-currency reporting, historical FX data. |
| 25 | +- You do not need to install [Swap](https://github.com/florianv/swap) separately. It is pulled in as a dependency, and Symfony Swap exposes it through Symfony's container and cache. |
| 26 | + |
| 27 | +## 🧠 Why Symfony Swap and not raw Swap? |
| 28 | + |
| 29 | +Using [Swap](https://github.com/florianv/swap) directly inside a Symfony app means three pieces of plumbing on every project: registering the builder and the Swap service yourself, wiring Symfony Cache to the PSR-16 contract, and configuring providers in PHP rather than in the container. Doable, but boilerplate every project pays for. |
| 30 | + |
| 31 | +Symfony Swap does this for you: |
| 32 | + |
| 33 | +- **Drop-in.** Add the bundle to `config/bundles.php` and you are set. |
| 34 | +- **Symfony Cache integration.** Choose `array`, `apcu`, `filesystem`, or any PSR-16 service ID under `cache.type`. |
| 35 | +- **Container service.** `florianv_swap.swap` is ready to inject from any controller, service, or command. |
| 36 | +- **Configurable.** `config/packages/florianv_swap.yaml` exposes providers, options, and the cache. |
| 37 | +- **Priority-ordered providers.** Each provider has a `priority`; the bundle sorts them (higher priority tried first). |
| 38 | + |
| 39 | +If you are not on Symfony, use [Swap](https://github.com/florianv/swap) directly. |
| 40 | + |
| 41 | +## 📦 Installation |
| 42 | + |
| 43 | +Symfony Swap requires PHP 8.2 or newer and Symfony 6.4, 7, or 8. |
12 | 44 |
|
13 | 45 | ```bash |
14 | | -$ composer require florianv/swap-bundle php-http/message php-http/guzzle6-adapter ^1.0 |
| 46 | +composer require florianv/swap-bundle symfony/http-client nyholm/psr7 |
15 | 47 | ``` |
16 | 48 |
|
17 | | -## Documentation |
18 | | - |
19 | | -The complete documentation can be found [here](https://github.com/florianv/symfony-swap/blob/master/Resources/doc/index.md). |
20 | | - |
21 | | -## Services |
22 | | - |
23 | | -Here is the list of the currently implemented services: |
24 | | - |
25 | | -| Service | Base Currency | Quote Currency | Historical | |
26 | | -|---------------------------------------------------------------------------|----------------------|----------------|----------------| |
27 | | -| [Fixer](https://fixer.io/) | EUR (free, no SSL), * (paid) | * | Yes | |
28 | | -| [Currency Data](https://currencylayer.com) | USD (free), * (paid) | * | Yes | |
29 | | -| [Exchange Rates Data](https://exchangeratesapi.io) | USD (free), * (paid) | * | Yes | |
30 | | -| [Abstract](https://www.abstractapi.com) | * | * | Yes | |
31 | | -| [coinlayer](https://coinlayer.com) | * Crypto (Limited standard currencies) | * Crypto (Limited standard currencies) | Yes | |
32 | | -| [Fixer](https://fixer.io) | EUR (free, no SSL), * (paid) | * | Yes | |
33 | | -| [currencylayer](https://currencylayer.com) | USD (free), * (paid) | * | Yes | |
34 | | -| [exchangeratesapi](https://exchangeratesapi.io) | USD (free), * (paid) | * | Yes | |
35 | | -| [European Central Bank](https://www.ecb.europa.eu/home/html/index.en.html) | EUR | * | Yes | |
36 | | -| [National Bank of Georgia](https://nbg.gov.ge) | * | GEL | Yes | |
37 | | -| [National Bank of the Republic of Belarus](https://www.nbrb.by) | * | BYN (from 01-07-2016),<br>BYR (01-01-2000 - 30-06-2016),<br>BYB (25-05-1992 - 31-12-1999) | Yes | |
38 | | -| [National Bank of Romania](http://www.bnr.ro) | RON, AED, AUD, BGN, BRL, CAD, CHF, CNY, CZK, DKK, EGP, EUR, GBP, HRK, HUF, INR, JPY, KRW, MDL, MXN, NOK, NZD, PLN, RSD, RUB, SEK, TRY, UAH, USD, XAU, XDR, ZAR | RON, AED, AUD, BGN, BRL, CAD, CHF, CNY, CZK, DKK, EGP, EUR, GBP, HRK, HUF, INR, JPY, KRW, MDL, MXN, NOK, NZD, PLN, RSD, RUB, SEK, TRY, UAH, USD, XAU, XDR, ZAR | Yes | |
39 | | -| [National Bank of Ukranie](https://bank.gov.ua) | * | UAH | Yes | |
40 | | -| [Central Bank of the Republic of Turkey](http://www.tcmb.gov.tr) | * | TRY | Yes | |
41 | | -| [Central Bank of the Republic of Uzbekistan](https://cbu.uz) | * | UZS | Yes | |
42 | | -| [Central Bank of the Czech Republic](https://www.cnb.cz) | * | CZK | Yes | |
43 | | -| [Central Bank of Russia](https://cbr.ru) | * | RUB | Yes | |
44 | | -| [Bulgarian National Bank](http://bnb.bg) | * | BGN | Yes | |
45 | | -| [WebserviceX](http://www.webservicex.net) | * | * | No | |
46 | | -| [1Forge](https://1forge.com) | * (free but limited or paid) | * (free but limited or paid) | No | |
47 | | -| [Cryptonator](https://www.cryptonator.com) | * Crypto (Limited standard currencies) | * Crypto (Limited standard currencies) | No | |
48 | | -| [CurrencyDataFeed](https://currencydatafeed.com) | * (free but limited or paid) | * (free but limited or paid) | No | |
49 | | -| [Open Exchange Rates](https://openexchangerates.org) | USD (free), * (paid) | * | Yes | |
50 | | -| [Xignite](https://www.xignite.com) | * | * | Yes | |
51 | | -| [Currency Converter API](https://www.currencyconverterapi.com) | * | * | Yes (free but limited or paid) | |
52 | | -| [xChangeApi.com](https://xchangeapi.com) | * | * | Yes | |
53 | | -| [fastFOREX.io](https://www.fastforex.io) | USD (free), * (paid) | * | No | |
54 | | -| [exchangerate.host](https://www.exchangerate.host) | * | * | Yes | |
55 | | -| Array | * | * | Yes | |
56 | | - |
57 | | -## Credits |
| 49 | +Register the bundle in `config/bundles.php` (Symfony Flex skips this step if a recipe applies): |
58 | 50 |
|
59 | | -- [Florian Voutzinos](https://github.com/florianv) |
60 | | -- [All Contributors](https://github.com/florianv/symfony-swap/contributors) |
| 51 | +```php |
| 52 | +// config/bundles.php |
| 53 | +return [ |
| 54 | + // ... |
| 55 | + Florianv\SwapBundle\FlorianvSwapBundle::class => ['all' => true], |
| 56 | +]; |
| 57 | +``` |
| 58 | + |
| 59 | +Skip to [Quickstart](#-quickstart). |
| 60 | + |
| 61 | +--- |
61 | 62 |
|
62 | | -## License |
| 63 | +_Optional: any PSR-18 HTTP client paired with a PSR-17 factory works. If your app already uses Guzzle, swap `symfony/http-client` for `php-http/guzzle7-adapter`. See the [documentation](Resources/doc/index.md) for alternatives._ |
| 64 | + |
| 65 | +## ⚡ Quickstart |
| 66 | + |
| 67 | +Configure at least one provider in `config/packages/florianv_swap.yaml`. The European Central Bank works without an API key: |
| 68 | + |
| 69 | +```yaml |
| 70 | +# config/packages/florianv_swap.yaml |
| 71 | +florianv_swap: |
| 72 | + providers: |
| 73 | + european_central_bank: |
| 74 | + priority: 0 |
| 75 | +``` |
| 76 | +
|
| 77 | +Inject the service: |
| 78 | +
|
| 79 | +```php |
| 80 | +use Swap\Swap; |
| 81 | +use Symfony\Component\DependencyInjection\Attribute\Autowire; |
| 82 | + |
| 83 | +final class CurrencyController |
| 84 | +{ |
| 85 | + public function __construct( |
| 86 | + #[Autowire(service: 'florianv_swap.swap')] |
| 87 | + private readonly Swap $swap, |
| 88 | + ) {} |
| 89 | + |
| 90 | + public function rate(): array |
| 91 | + { |
| 92 | + // EUR → USD exchange rate |
| 93 | + $rate = $this->swap->latest('EUR/USD'); |
| 94 | + |
| 95 | + return [ |
| 96 | + 'value' => $rate->getValue(), // e.g. 1.0823 |
| 97 | + 'date' => $rate->getDate()->format('Y-m-d'), // e.g. 2026-04-29 |
| 98 | + 'provider' => $rate->getProviderName(), // 'european_central_bank' |
| 99 | + ]; |
| 100 | + } |
| 101 | +} |
| 102 | +``` |
| 103 | + |
| 104 | +Or fetch directly from the container: |
| 105 | + |
| 106 | +```php |
| 107 | +$swap = $container->get('florianv_swap.swap'); |
| 108 | +$rate = $swap->latest('EUR/USD'); |
| 109 | +``` |
| 110 | + |
| 111 | +Add commercial providers under `providers:` and they will be chained with the configured priority order: |
| 112 | + |
| 113 | +```yaml |
| 114 | +# config/packages/florianv_swap.yaml |
| 115 | +florianv_swap: |
| 116 | + cache: |
| 117 | + ttl: 3600 |
| 118 | + type: filesystem |
| 119 | + providers: |
| 120 | + apilayer_fixer: |
| 121 | + api_key: '%env(SWAP_FIXER_KEY)%' |
| 122 | + priority: 10 # tried first |
| 123 | + open_exchange_rates: |
| 124 | + app_id: '%env(SWAP_OER_APP_ID)%' |
| 125 | + priority: 5 # tried second |
| 126 | + european_central_bank: |
| 127 | + priority: 0 # free fallback for EUR-base pairs |
| 128 | +``` |
63 | 129 |
|
64 | | -The MIT License (MIT). Please see [LICENSE](https://github.com/florianv/symfony-swap/blob/master/Resources/meta/LICENSE) for more information. |
| 130 | +Providers are tried in priority order (higher first). If a provider does not support the requested currency pair, it is skipped silently. If a provider throws an error, the next provider is tried. If every provider fails, a `ChainException` is thrown with all collected errors. |
| 131 | + |
| 132 | +## 💾 Caching |
| 133 | + |
| 134 | +Set `cache` in `config/packages/florianv_swap.yaml`: |
| 135 | + |
| 136 | +```yaml |
| 137 | +# config/packages/florianv_swap.yaml |
| 138 | +florianv_swap: |
| 139 | + cache: |
| 140 | + ttl: 3600 |
| 141 | + type: filesystem # array, apcu, filesystem, or a PSR-16 service ID |
| 142 | +``` |
| 143 | + |
| 144 | +For a custom cache, point `type` at any service implementing `Psr\SimpleCache\CacheInterface`: |
| 145 | + |
| 146 | +```yaml |
| 147 | +florianv_swap: |
| 148 | + cache: |
| 149 | + ttl: 3600 |
| 150 | + type: my_psr16_cache_service |
| 151 | +``` |
| 152 | + |
| 153 | +Per-query overrides are documented in the [full documentation](Resources/doc/index.md#-caching). |
| 154 | + |
| 155 | +## 🛠 Common use cases |
| 156 | + |
| 157 | +- Display localized prices in multi-currency Symfony storefronts. |
| 158 | +- Compute invoice totals across currencies in a Symfony API. |
| 159 | +- Reconcile multi-currency ledgers using historical rates. |
| 160 | +- Power internal FX dashboards with rate history. |
| 161 | +- Build currency conversion infrastructure for Symfony-based fintech and ERP applications. |
| 162 | + |
| 163 | +## 🧭 Which package should I use? |
| 164 | + |
| 165 | +The Swap ecosystem is a layered toolkit for currency conversion in PHP: |
| 166 | + |
| 167 | +- **Swap.** The easy-to-use, high-level API for plain PHP. |
| 168 | +- **Exchanger.** Lower-level, more granular alternative; direct access to provider implementations. |
| 169 | +- **Laravel Swap.** Laravel application of Swap. |
| 170 | +- **Symfony Swap.** Symfony integration of Swap (this package). |
| 171 | + |
| 172 | +All four packages are MIT-licensed and require PHP 8.2 or newer. |
| 173 | + |
| 174 | +## 📚 Documentation |
| 175 | + |
| 176 | +The full documentation, with the per-provider configuration reference, custom service registration, cache types, and FAQ, is in [Resources/doc/index.md](Resources/doc/index.md). The full provider list with capabilities is in the [Swap README](https://github.com/florianv/swap#-providers). |
| 177 | + |
| 178 | +## 🧩 Related packages |
| 179 | + |
| 180 | +The Swap ecosystem: |
| 181 | + |
| 182 | +- [**Swap**](https://github.com/florianv/swap): easy-to-use PHP currency conversion library. |
| 183 | +- [**Exchanger**](https://github.com/florianv/exchanger): exchange rate provider layer. |
| 184 | +- [**Laravel Swap**](https://github.com/florianv/laravel-swap): Laravel application of Swap. |
| 185 | +- [**Symfony Swap**](https://github.com/florianv/symfony-swap): Symfony integration of Swap (this package). |
| 186 | + |
| 187 | +## 🤝 Sponsorship |
| 188 | + |
| 189 | +The Swap ecosystem is open to selected sponsorships from exchange rate API providers and financial infrastructure companies. |
| 190 | + |
| 191 | +Sponsorship can include: |
| 192 | + |
| 193 | +- Documentation visibility |
| 194 | +- Integration examples |
| 195 | +- Ecosystem-level visibility across Swap, Exchanger, Laravel Swap, and Symfony Swap |
| 196 | + |
| 197 | +For inquiries, contact the maintainer via [GitHub](https://github.com/florianv). |
| 198 | + |
| 199 | +## 🙌 Contributing |
| 200 | + |
| 201 | +Issues and pull requests are welcome. Please see the existing [issues](https://github.com/florianv/symfony-swap/issues) before opening a new one. |
| 202 | + |
| 203 | +## 📄 License |
| 204 | + |
| 205 | +The MIT License (MIT). Please see [LICENSE](LICENSE) for more information. |
| 206 | + |
| 207 | +## 👏 Credits |
| 208 | + |
| 209 | +- [Florian Voutzinos](https://github.com/florianv) |
| 210 | +- [All contributors](https://github.com/florianv/symfony-swap/contributors) |
0 commit comments