Weave Code
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

Exchanger Laravel Package

florianv/exchanger

Exchanger is a PHP framework for retrieving currency exchange rates from multiple providers (Fixer, Currency Data, Exchange Rates Data, and more). It offers a unified API for live and historical rates, supporting different base/quote currency constraints per service.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install Dependencies:
    composer require florianv/exchanger php-http/curl-client nyholm/psr7
    
  2. Configure a Service (e.g., Fixer):
    use Exchanger\Service\ApiLayer\Fixer;
    use Http\Client\Curl\Client as CurlClient;
    
    $client = new CurlClient();
    $service = new Fixer($client, null, ['api_key' => 'YOUR_API_KEY']);
    
  3. Initialize Exchanger:
    $exchanger = new \Exchanger\Exchanger($service);
    
  4. First Use Case: Fetch latest EUR/USD rate:
    $query = (new \Exchanger\ExchangeRateQueryBuilder('EUR/USD'))->build();
    $rate = $exchanger->getExchangeRate($query);
    echo $rate->getValue(); // Output: e.g., 1.1159
    

Where to Look First:


Implementation Patterns

Core Workflows

  1. Fetching Rates:

    // Latest rate
    $query = (new ExchangeRateQueryBuilder('USD/JPY'))->build();
    $rate = $exchanger->getExchangeRate($query);
    
    // Historical rate (if supported)
    $query = (new ExchangeRateQueryBuilder('EUR/GBP'))
        ->setDate(new \DateTime('2023-01-01'))
        ->build();
    
  2. Chaining Services (Fallback Logic):

    use Exchanger\Service\Chain;
    
    $chain = new Chain([
        new Fixer($client, null, ['api_key' => 'FIXER_KEY']),
        new CurrencyData($client, null, ['api_key' => 'CURRENCY_DATA_KEY']),
    ]);
    $exchanger = new Exchanger($chain);
    
  3. Caching Integration (PSR-16):

    use Cache\Adapter\Filesystem\FilesystemCachePool;
    use Cache\Bridge\SimpleCache\SimpleCacheBridge;
    
    $cache = new SimpleCacheBridge(new FilesystemCachePool());
    $exchanger = new Exchanger($service, $cache, ['cache_ttl' => 3600]);
    

Laravel-Specific Patterns

  1. Service Provider Binding:

    // app/Providers/AppServiceProvider.php
    public function register()
    {
        $this->app->singleton(Exchanger::class, function ($app) {
            $client = new CurlClient();
            $service = new Fixer($client, null, ['api_key' => config('services.fixer.key')]);
            return new Exchanger($service, $app->make('cache.store'));
        });
    }
    
  2. Config File (config/exchanger.php):

    return [
        'default_service' => 'fixer',
        'services' => [
            'fixer' => [
                'api_key' => env('FIXER_API_KEY'),
                'base_currency' => 'EUR',
            ],
        ],
        'cache' => [
            'enabled' => true,
            'ttl' => 3600,
        ],
    ];
    
  3. Facade Usage:

    // Create ExchangerFacade.php
    namespace App\Facades;
    use Illuminate\Support\Facades\Facade;
    
    class ExchangerFacade extends Facade
    {
        protected static function getFacadeAccessor() { return 'exchanger'; }
    }
    

    Register in config/app.php under aliases.

  4. Eloquent Model Observers (Auto-update rates):

    // app/Observers/OrderObserver.php
    use Exchanger\Facades\Exchanger;
    
    class OrderObserver
    {
        public function saved(Order $order)
        {
            if ($order->currency !== config('app.default_currency')) {
                $rate = Exchanger::getExchangeRate(
                    (new ExchangeRateQueryBuilder(config('app.default_currency') . '/' . $order->currency))->build()
                );
                $order->amount_in_default_currency = $order->amount * $rate->getValue();
                $order->save();
            }
        }
    }
    
  5. API Response Formatting:

    // app/Http/Controllers/CurrencyController.php
    use Exchanger\Facades\Exchanger;
    
    class CurrencyController extends Controller
    {
        public function getRates(Request $request)
        {
            $query = (new ExchangeRateQueryBuilder($request->currency_pair))->build();
            $rate = Exchanger::getExchangeRate($query);
            return response()->json([
                'rate' => $rate->getValue(),
                'date' => $rate->getDate()->format('Y-m-d'),
                'provider' => $rate->getProviderName(),
            ]);
        }
    }
    
  6. Command for Bulk Rate Updates:

    // app/Console/Commands/UpdateExchangeRates.php
    use Exchanger\Facades\Exchanger;
    use Illuminate\Console\Command;
    
    class UpdateExchangeRates extends Command
    {
        protected $signature = 'exchange:update {--currencies=*}';
        protected $description = 'Update exchange rates for specified currencies';
    
        public function handle()
        {
            $currencies = $this->option('currencies') ?: ['USD', 'EUR', 'GBP'];
            foreach ($currencies as $currency) {
                $query = (new ExchangeRateQueryBuilder('USD/' . $currency))->build();
                $rate = Exchanger::getExchangeRate($query);
                Cache::put("exchange_rate_{$currency}", $rate->getValue(), 3600);
            }
        }
    }
    

Gotchas and Tips

Pitfalls

  1. API Key Management:

    • Never hardcode API keys in source files. Use Laravel's .env and config/services.php.
    • Gotcha: Some services (e.g., Fixer) require SSL for paid plans. Configure CURL options:
      $client = new CurlClient(null, null, [
          CURLOPT_SSL_VERIFYPEER => true,
          CURLOPT_SSL_VERIFYHOST => 2,
      ]);
      
  2. Rate Limiting:

    • Free tiers (e.g., 1,000 requests/day) can be hit quickly. Implement caching aggressively.
    • Tip: Use cache_ttl per-query to balance freshness and rate limits:
      $query->addOption('cache_ttl', 1800); // 30-minute cache
      
  3. Currency Pair Validity:

    • Not all services support every currency pair. Use chaining to handle unsupported pairs:
      $chain = new Chain([$service1, $service2, $service3]);
      
    • Gotcha: Historical rates may fail silently. Validate dates before querying:
      if ($service->supportsHistorical()) {
          $query->setDate($date);
      }
      
  4. Caching Quirks:

    • PSR-16 cache keys are limited to 64 chars. Avoid long cache_key_prefix:
      // Bad: 'myapp-long-prefix-' (exceeds 24 chars)
      // Good: 'app-'
      $query->addOption('cache_key_prefix', 'app-');
      
    • Tip: Use cache/predis-adapter for Redis caching with TTL:
      $cache = new SimpleCacheBridge(new PredisCachePool(new \Predis\Client('redis://localhost')));
      
  5. HTTPlug Configuration:

    • Gotcha: Some services require specific headers (e.g., Accept: application/json). Configure the client:
      $client = new CurlClient(null, [
          'headers' => ['Accept' => 'application/json'],
      ]);
      
  6. Timezone Handling:

    • Historical queries use the service's timezone. Convert dates explicitly:
      $query->setDate((new \DateTime('2023-01-01', new \DateTimeZone('UTC'))));
      
  7. Error Handling:

    • Services may return null or throw exceptions. Wrap calls:
      try {
          $rate = $exchanger->getExchangeRate($query);
      } catch (\Exception $e) {
          Log::error("Exchange rate failed: {$e->getMessage()}");
          return response()->json(['error' => 'Service unavailable'], 503);
      }
      

Debugging Tips

  1. Log Queries:
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport