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

Exchange rate provider layer for PHP: 30+ rate services behind one ExchangeRateService interface. Supports historical rates, chain fallback between providers, and PSR-16 caching. Use when you need more control than higher-level libraries.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require florianv/exchanger symfony/http-client nyholm/psr7
    

    (Note: symfony/http-client and nyholm/psr7 are required for HTTP operations.)

  2. Basic Usage:

    use Exchanger\Exchanger;
    use Exchanger\ExchangeRateQueryBuilder;
    use Exchanger\Service\FastForex;
    
    $service = new FastForex(['api_key' => env('FASTFOREX_API_KEY')]);
    $exchanger = new Exchanger($service);
    
    $rate = $exchanger->getExchangeRate(
        (new ExchangeRateQueryBuilder('EUR/USD'))->build()
    );
    
    $convertedAmount = 100 * $rate->getValue(); // 100 EUR → USD
    
  3. Fallback Chain (Recommended for Production):

    use Exchanger\Service\Chain;
    use Exchanger\Service\EuropeanCentralBank;
    
    $service = new Chain([
        new FastForex(['api_key' => env('FASTFOREX_API_KEY')]),
        new EuropeanCentralBank(), // Free fallback for EUR-base pairs
    ]);
    

Where to Look First

  • Service Registry: List of 30+ supported providers (commercial/public).
  • QueryBuilder: Construct queries for rates/historical data.
  • Caching: PSR-16 cache integration (e.g., symfony/cache).

Implementation Patterns

1. Service Composition

  • Chaining: Use Chain to prioritize providers (e.g., paid → free fallback).
    $chain = new Chain([
        new FastForex(['api_key' => '...']),
        new EuropeanCentralBank(),
    ]);
    
  • Dynamic Chains: Build chains conditionally (e.g., based on currency pair):
    $primary = $currencyPair->startsWith('EUR') ? new EuropeanCentralBank() : new FastForex(['api_key' => '...']);
    $exchanger = new Exchanger(new Chain([$primary, new EuropeanCentralBank()]));
    

2. Query Customization

  • Historical Rates:
    $query = (new ExchangeRateQueryBuilder('USD/JPY'))
        ->setDate(new DateTime('2023-01-01'))
        ->build();
    
  • Per-Query Options: Pass options to individual services (e.g., FastForex supports symbols):
    $query->setOptions(['symbols' => 'USD,EUR']);
    

3. Caching Strategies

  • PSR-16 Cache Integration:
    use Symfony\Contracts\Cache\CacheInterface;
    
    $cache = new Symfony\Cache\Psr16Cache(new Symfony\Cache\Adapter\FilesystemAdapter());
    $exchanger = new Exchanger($service, $cache);
    
  • Custom Cache Keys: Override ExchangeRateQueryBuilder's getCacheKey() for granular control.

4. Framework Integration

  • Laravel: Use laravel-swap for bindings.
  • Symfony: Use symfony-swap.
  • Custom Facade:
    class CurrencyFacade {
        public function convert(float $amount, string $from, string $to): float {
            $rate = app(Exchanger::class)->getExchangeRate(
                (new ExchangeRateQueryBuilder("$from/$to"))->build()
            );
            return $amount * $rate->getValue();
        }
    }
    

5. Error Handling

  • Chain Fallback: Exceptions from failed services are caught and logged (see ChainException).
  • Graceful Degradation: Return cached rates if fresh data fails:
    try {
        $rate = $exchanger->getExchangeRate($query);
    } catch (ChainException $e) {
        $rate = $exchanger->getExchangeRate($query->withCacheOnly(true));
    }
    

Gotchas and Tips

Pitfalls

  1. API Key Management:

    • Hardcoding keys violates security best practices. Use environment variables or a secrets manager.
    • Some providers (e.g., fastFOREX) require keys; others (e.g., EuropeanCentralBank) are free but limited (e.g., EUR-base only).
  2. Rate Precision:

    • Always cast $rate->getValue() to float to avoid string comparisons (e.g., "1.08" vs. 1.0800).
    • Example:
      $rateValue = (float) $rate->getValue();
      
  3. Historical Data Gaps:

    • Public providers (e.g., EuropeanCentralBank) update daily at fixed times. Historical queries may return null if the date isn’t published yet.
    • Commercial providers (e.g., fastFOREX) offer more granularity (e.g., hourly rates).
  4. Currency Pair Validation:

    • Not all providers support all pairs. Test edge cases (e.g., USD/TRY vs. EUR/USD).
    • Use ExchangeRateQueryBuilder::validate() to check support before querying.
  5. Caching Quirks:

    • Cache Key Collisions: Customize getCacheKey() if using the same query for different contexts (e.g., "live rate" vs. "historical").
    • Stale Data: Clear cache manually if rates change unexpectedly (e.g., during market hours).

Debugging Tips

  1. Log Provider Responses:

    • Wrap services with middleware to log raw API responses:
      $service = new FastForex(['api_key' => '...']);
      $service->setHttpClient(new MiddlewareStack(
          new HttpClient(),
          [new LogMiddleware()]
      ));
      
    • Use Exchanger\Service\HttpService::setDebug(true) for verbose output.
  2. Isolate Service Failures:

    • Test individual services in isolation:
      $fastForex = new FastForex(['api_key' => '...']);
      $rate = $fastForex->getExchangeRate($query); // Bypass chain
      
  3. Rate Limiting:

    • Commercial providers often enforce limits. Implement retries with exponential backoff:
      use Symfony\Component\HttpClient\RetryStrategy;
      
      $client = new HttpClient([
          'retry' => RetryStrategy::fromOptions([
              'max_retries' => 3,
              'delay' => 1000,
          ]),
      ]);
      

Extension Points

  1. Custom Providers:

    • Implement Exchanger\Contract\ExchangeRateService:
      class MyCustomProvider implements ExchangeRateService {
          public function getExchangeRate(ExchangeRateQuery $query): ExchangeRate {
              // Fetch from your custom source (e.g., database, internal API).
              return new ExchangeRate(1.23, new DateTime(), 'my_provider');
          }
      }
      
    • Register via Registry::addService().
  2. Middleware:

    • Add HTTP middleware (e.g., authentication, rate limiting) to services:
      $service = new FastForex(['api_key' => '...']);
      $service->setHttpClient(new MiddlewareStack(
          new HttpClient(),
          [new AuthMiddleware()]
      ));
      
  3. Query Modifiers:

    • Extend ExchangeRateQueryBuilder to add custom options:
      class ExtendedQueryBuilder extends ExchangeRateQueryBuilder {
          public function setCustomOption(string $key, $value): self {
              $this->options[$key] = $value;
              return $this;
          }
      }
      
  4. Rate Objects:

    • Override ExchangeRate to add metadata (e.g., getSourceUrl()):
      class ExtendedRate extends ExchangeRate {
          public function __construct(float $value, DateTimeInterface $date, string $provider, private string $sourceUrl) {
              parent::__construct($value, $date, $provider);
          }
          public function getSourceUrl(): string { return $this->sourceUrl; }
      }
      

Performance Tips

  1. Batch Requests:

    • Use ExchangeRateQueryBuilder::setBatch() to fetch multiple pairs in one call (supported by some providers like fastFOREX):
      $query = (new ExchangeRateQueryBuilder('USD/EUR,GBP/JPY'))
          ->setBatch(true)
          ->build();
      
  2. Cache Warming:

    • Pre-fetch common rates during low-traffic periods:
      $exchanger->getExchangeRate($query, true); // Force refresh
      
  3. Provider Selection:

    • Bench
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.
codraw/framework-extra-bundle
codraw/messenger
codraw/security
codraw/mailer
codraw/contracts
codraw/profiling
codraw/dependency-injection
codraw/tester
codraw/core
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony