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

Retrieve live currency exchange rates from multiple providers (Fixer, currencylayer, exchangeratesapi, etc.) with optional caching and fallbacks. PSR-friendly and integrates with MoneyPHP; also available via Symfony bundle and Laravel package.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install Dependencies:

    composer require florianv/swap php-http/curl-client nyholm/psr7 php-http/message
    
  2. Basic Configuration (in a service provider or bootstrap file):

    use Swap\Builder;
    
    $swap = (new Builder())
        ->add('apilayer_fixer', ['api_key' => env('FIXER_API_KEY')])
        ->add('apilayer_currency_data', ['api_key' => env('CURRENCYLAYER_API_KEY')])
        ->build();
    
  3. First Use Case:

    // Register the swap instance in Laravel's service container
    $this->app->singleton('swap', fn() => $swap);
    
    // Retrieve latest EUR/USD rate
    $rate = app('swap')->latest('EUR/USD');
    $value = $rate->getValue(); // e.g., 1.129
    

Where to Look First


Implementation Patterns

Core Workflow

  1. Service Chaining: Configure a fallback chain for reliability (e.g., Fixer → CurrencyLayer → ExchangeRatesAPI).

    $swap = (new Builder())
        ->add('apilayer_fixer', ['api_key' => env('FIXER_API_KEY')])
        ->add('apilayer_currency_data', ['api_key' => env('CURRENCYLAYER_API_KEY')])
        ->build();
    
  2. Rate Retrieval:

    // Latest rate
    $rate = $swap->latest('USD/EUR');
    
    // Historical rate (e.g., 15 days ago)
    $date = (new \DateTime())->modify('-15 days');
    $historicalRate = $swap->historical('USD/EUR', $date);
    
  3. Provider Awareness: Track which service provided the rate (useful for debugging or logging):

    $provider = $rate->getProviderName(); // e.g., 'apilayer_fixer'
    

Integration Tips

  1. Laravel Service Provider: Bind the Swap instance to the container for easy access:

    public function register()
    {
        $this->app->singleton('swap', fn() => (new Builder())
            ->add('apilayer_fixer', ['api_key' => env('FIXER_API_KEY')])
            ->build()
        );
    }
    
  2. Caching Rates (Redis example):

    use Cache\Adapter\Predis\PredisCachePool;
    use Cache\Bridge\SimpleCache\SimpleCacheBridge;
    
    $client = new \Predis\Client(env('REDIS_URL'));
    $cache = new SimpleCacheBridge(new PredisCachePool($client));
    
    $swap = (new Builder(['cache_ttl' => 3600]))
        ->useSimpleCache($cache)
        ->build();
    
  3. HTTP Request Caching (for bulk requests): Cache API responses to avoid redundant calls (e.g., for multiple rates with the same base currency):

    use Http\Client\Common\PluginClient;
    use Http\Client\Common\Plugin\CachePlugin;
    use Cache\Adapter\PHPArray\ArrayCachePool;
    
    $pool = new ArrayCachePool();
    $cachePlugin = new CachePlugin($pool, new \Http\Message\StreamFactory\GuzzleStreamFactory());
    $client = new PluginClient(new \Http\Adapter\Guzzle6\Client(), [$cachePlugin]);
    
    $swap = (new Builder())->setHttpClient($client)->build();
    
  4. Dynamic Configuration: Override cache settings per request:

    $rate = $swap->latest('USD/EUR', ['cache_ttl' => 60, 'cache' => true]);
    
  5. Custom Services: Extend functionality by implementing ExchangeRateService for unsupported APIs.


Gotchas and Tips

Pitfalls

  1. API Key Management:

    • Never hardcode API keys. Use Laravel's .env or a secrets manager.
    • Free tiers (e.g., Fixer, CurrencyLayer) have request limits (e.g., 100/month). Monitor usage to avoid hitting limits.
  2. Cache Key Limits:

    • PSR-6 cache keys are limited to 64 characters. The cache_key_prefix must not exceed 24 characters (due to SHA-1 hashing).
    • Avoid special characters ({}()/\@:) in prefixes; they are auto-replaced with -.
  3. Fallback Behavior:

    • If the primary service fails (e.g., rate limit exceeded), the next in the chain is used. Log failures to track issues:
      try {
          $rate = $swap->latest('USD/EUR');
      } catch (\Exception $e) {
          \Log::error("Currency rate failed: " . $e->getMessage());
      }
      
  4. Historical Data Gaps:

    • Some services (e.g., webservicex) do not support historical rates. Verify service capabilities before relying on them.
  5. Timezone Handling:

    • Historical dates must be in UTC or the service's expected timezone. Use \DateTime::createFromFormat() for precision:
      $date = \DateTime::createFromFormat('Y-m-d', '2023-01-01', new \DateTimeZone('UTC'));
      

Debugging Tips

  1. Inspect Provider: Use getProviderName() to debug which service is being used:

    $rate = $swap->latest('USD/EUR');
    \Log::debug("Provider: " . $rate->getProviderName());
    
  2. Disable Caching Temporarily: Bypass cache to test live API responses:

    $rate = $swap->latest('USD/EUR', ['cache' => false]);
    
  3. HTTP Client Logging: Enable Guzzle logging to debug API requests:

    $client = new \Http\Adapter\Guzzle6\Client([
        'debug' => fopen('guzzle.log', 'w'),
    ]);
    $swap = (new Builder())->setHttpClient($client)->build();
    

Extension Points

  1. Custom Services: Implement Swap\Service\ExchangeRateService to add support for new APIs:

    class MyCustomService implements ExchangeRateService {
        public function latest(string $pair): Rate {
            // Fetch from your custom API
        }
    
        public function historical(string $pair, \DateTime $date): Rate {
            // Fetch historical data
        }
    }
    

    Register it with the builder:

    $swap = (new Builder())->add('my_custom_service', new MyCustomService())->build();
    
  2. Middleware for Requests: Add request/response middleware to the HTTP client (e.g., for auth headers):

    use Http\Message\BaseMessage;
    
    $client = new \Http\Adapter\Guzzle6\Client();
    $stack = \Http\Message\Middleware::createStack();
    $stack->push(function (callable $handler) {
        return function (BaseMessage $request) use ($handler) {
            $request = $request->withHeader('X-Custom-Header', 'value');
            return $handler($request);
        };
    });
    $swap = (new Builder())->setHttpClient($stack($client))->build();
    
  3. Rate Validation: Validate rates before use (e.g., check for null or invalid values):

    $rate = $swap->latest('USD/EUR');
    if ($rate->getValue() === null) {
        throw new \RuntimeException("Invalid rate received");
    }
    
  4. Bulk Rate Fetching: For efficiency, fetch all rates for a base currency at once (if supported by the service):

    $rates = $swap->latest(['USD/EUR', 'USD/GBP']); // If service supports bulk
    

Performance Tips

  1. Cache TTL Strategy:

    • Short TTL (e.g., 60s) for volatile rates (e.g., crypto).
    • Long TTL (e.g., 3600s) for stable currencies (e.g., EUR/USD).
  2. Batch Requests: Use the latest() method with an array of pairs to reduce API calls:

    $rates = $swap->latest(['USD/EUR', 'USD/GBP', 'USD/JPY']);
    
  3. Fallback Optimization: Place the most reliable/cheapest service first in the chain to minimize costs.

  4. **Avoid Redundant C

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