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

Swap Laravel Package

florianv/swap

PHP 8.2+ currency exchange rate library with a single API over 30+ providers. Supports conversion, historical rates, PSR-16 caching, and provider fallback. Works with PSR-18 HTTP clients and PSR-17 factories for flexible integrations.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

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

    (Alternative: Use php-http/guzzle7-adapter if preferring Guzzle.)

  2. Basic Setup:

    use Swap\Builder;
    
    $swap = (new Builder())
        ->add('fastforex', ['api_key' => env('FASTFOREX_API_KEY')])
        ->build();
    
  3. First Use Case: Fetch a rate and convert an amount:

    $rate = $swap->latest('EUR/USD');
    $convertedAmount = 100.00 * $rate->getValue(); // 100 EUR → USD
    

Where to Look First

  • README.md: Quickstart and provider list.
  • doc/readme.md: Deep dive into caching, HTTP clients, and provider configs.
  • src/Swap.php: Core API methods (latest(), historical(), etc.).

Implementation Patterns

Core Workflows

  1. Provider Chaining (Fallback Logic):

    $swap = (new Builder())
        ->add('fastforex', ['api_key' => env('FASTFOREX_API_KEY')]) // Primary
        ->add('european_central_bank') // Fallback for EUR pairs
        ->build();
    
    • Order matters: Providers are tried sequentially. Use commercial APIs first, then free fallbacks.
  2. Historical Rates:

    $rate = $swap->historical('USD/JPY', new \DateTime('2023-01-01'));
    
  3. Caching Integration:

    use Symfony\Contracts\Cache\SimpleCacheInterface;
    
    $cache = new \Symfony\Cache\Simple\FilesystemCache();
    $swap = (new Builder())
        ->add('fastforex', ['api_key' => env('FASTFOREX_API_KEY')])
        ->useCache($cache)
        ->build();
    

Laravel-Specific Patterns

  1. Service Provider Binding:

    // app/Providers/AppServiceProvider.php
    public function register()
    {
        $this->app->singleton(Swap::class, function ($app) {
            return (new Builder())
                ->add('fastforex', ['api_key' => env('FASTFOREX_API_KEY')])
                ->useCache($app->make(\Symfony\Contracts\Cache\CacheInterface::class))
                ->build();
        });
    }
    
  2. Facade (Optional): Create a facade (app/Facades/SwapFacade.php) to wrap Swap::latest() calls:

    public static function rate(string $pair): float
    {
        return app(Swap::class)->latest($pair)->getValue();
    }
    
  3. Job Queues for Heavy Conversions:

    // Process bulk conversions asynchronously
    RateConversionJob::dispatch($amount, $fromCurrency, $toCurrency);
    

Integration Tips

  • MoneyPHP Integration: Use SwapExchange for type-safe conversions:

    use Money\Money;
    use Swap\Money\SwapExchange;
    
    $exchange = new SwapExchange($swap);
    $money = new Money(10000, 'EUR');
    $converted = $exchange->convert($money, 'USD');
    
  • Environment-Based Config: Load providers dynamically from .env:

    $builder = new Builder();
    if (env('USE_FASTFOREX')) {
        $builder->add('fastforex', ['api_key' => env('FASTFOREX_API_KEY')]);
    }
    $builder->add('european_central_bank');
    
  • Rate Limiting: Implement middleware to throttle API calls (e.g., using GuzzleHttp\Middleware).


Gotchas and Tips

Pitfalls

  1. Provider-Specific Quirks:

    • ECB: Only supports EUR as the base currency. Non-EUR pairs (e.g., USD/JPY) will fail silently.
    • Crypto Providers: Some (e.g., coin_layer) require explicit crypto pair formatting (e.g., BTC/USD).
    • Rate Granularity: Public providers (e.g., national banks) often update less frequently (daily vs. real-time).
  2. Caching Caveats:

    • TTL Misconfiguration: Default cache TTL is null (no expiry). Set explicitly:
      ->useCache($cache)->withDefaultTTL(3600) // 1-hour expiry
      
    • Stale Data: Historical rates may not align with provider updates. Validate timestamps:
      if ($rate->getDate()->diff(new \DateTime())->days > 1) {
          throw new \RuntimeException('Rate too stale');
      }
      
  3. Error Handling:

    • ChainException: Catches all provider failures. Log errors before rethrowing:
      try {
          $rate = $swap->latest('EUR/XYZ'); // Unsupported currency
      } catch (ChainException $e) {
          Log::error('Currency conversion failed', ['errors' => $e->getErrors()]);
          throw new \InvalidArgumentException('Unsupported currency pair');
      }
      
    • Rate Unavailability: Some providers (e.g., webservicex) may return null for unsupported pairs. Check:
      if ($rate === null) {
          throw new \RuntimeException('Unsupported currency pair');
      }
      
  4. API Key Management:

    • Hardcoded Keys: Avoid committing keys to version control. Use Laravel’s .env or a secrets manager.
    • Key Rotation: Implement a ProviderKeyResolver interface to dynamically fetch keys from a secure source.

Debugging Tips

  1. Log Provider Responses: Enable debug logging for HTTP requests:

    $client = \Symfony\Contracts\HttpClient\HttpClientInterface::create([
        'debug' => true,
    ]);
    $builder->useHttpClient($client);
    
  2. Inspect Rate Objects: Dump the full rate object to debug:

    dd($rate->getValue(), $rate->getDate(), $rate->getProviderName(), $rate->getMetadata());
    
  3. Test Fallback Logic: Mock providers to test fallback behavior:

    $builder->addExchangeRateService(new MockFailingProvider());
    $builder->add('european_central_bank'); // Should trigger on failure
    

Extension Points

  1. Custom Providers: Implement Exchanger\Contract\ExchangeRateService:

    class MyCustomProvider implements ExchangeRateService
    {
        public function latest(string $pair): ?Rate
        {
            // Fetch from your custom source
            return new Rate(1.2, new \DateTime(), 'my_custom_provider');
        }
    }
    

    Register with:

    $builder->addExchangeRateService(new MyCustomProvider());
    
  2. Override HTTP Client: Use a custom client for retries or auth:

    $client = \Http\Adapter\Guzzle\Guzzle18::createWithConfig([
        'timeout' => 10.0,
        'headers' => ['Authorization' => 'Bearer ' . env('API_TOKEN')],
    ]);
    $builder->useHttpClient($client);
    
  3. Modify Rate Processing: Extend Swap to add pre/post-processing:

    class CustomSwap extends Swap
    {
        public function latest(string $pair): Rate
        {
            $rate = parent::latest($pair);
            // Apply business logic (e.g., rounding, fees)
            return new Rate(round($rate->getValue(), 4), $rate->getDate(), $rate->getProviderName());
        }
    }
    

Performance Tips

  1. Batch Requests: Use Swap::latestMultiple() to fetch multiple rates in one call (supported by some providers):

    $rates = $swap->latestMultiple(['EUR/USD', 'GBP/JPY']);
    
  2. Cache Invalidation: Invalidate cache manually when rates change (e.g., after a manual override):

    $cache->delete('swap:latest:EUR/USD');
    
  3. Provider Selection:

    • High Volume: Use fastforex or apilayer_currency_data (paid tiers).
    • Low Volume: european_central_bank (free, but EUR-only).
    • Crypto: coin_layer or cryptonator.
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