Create job that fetches conversion rates
ADD
app/Jobs/FetchConversionRates.php
+<?php
+
+namespace App\Jobs;
+
+use App\Repositories\ConversionRateRepository;
+use App\Repositories\CurrencyRepository;
+use Exception;
+use GuzzleHttp\Client;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+use Symfony\Component\DomCrawler\Crawler;
+
+class FetchConversionRates implements ShouldQueue
+{
+ use Dispatchable;
+ use InteractsWithQueue;
+ use Queueable;
+ use SerializesModels;
+
+ private $currencyRepository;
+ private $conversionRateRepository;
+
+ public function __construct()
+ {
+ //
+ }
+
+ public function handle(
+ CurrencyRepository $currencyRepository,
+ ConversionRateRepository $conversionRateRepository
+ ): void {
+ $this->currencyRepository = $currencyRepository;
+ $this->conversionRateRepository = $conversionRateRepository;
+
+ $client = new Client();
+
+ foreach ($this->currencyRepository->getAll() as $baseCurrency) {
+ if (!$baseCurrency->iso) {
+ continue;
+ }
+
+ foreach ($this->currencyRepository->getAll() as $targetCurrency) {
+ if (!$targetCurrency->iso || $baseCurrency->iso === $targetCurrency->iso) {
+ continue;
+ }
+
+ $url = 'https://www.valutafx.com/' . $baseCurrency->iso . '-' . $targetCurrency->iso . '.htm';
+
+ try {
+ $response = $client->request('GET', $url);
+ } catch (Exception $e) {
+ continue;
+ }
+
+ $crawler = new Crawler($response->getBody()->__toString());
+
+ $result = $crawler->filter('.converter-result > .rate-value')->first()->text();
+ $rate = str_replace(',', '', $result); // Get rid of any potential thousands separators
+
+ $this->conversionRateRepository->createOrUpdate(
+ $baseCurrency->id,
+ $targetCurrency->id,
+ $rate
+ );
+ }
+ }
+ }
+}

MODIFY
app/Models/ConversionRate.php
app/Models/ConversionRate.php
@@ -6,6 +6,12 @@ use Illuminate\Database\Eloquent\Model;
class ConversionRate extends Model
{
+ protected $fillable = [
+ 'base_currency_id',
+ 'target_currency_id',
+ 'rate'
+ ];
+
public function base()
{
return $this->belongsTo(Currency::class);

ADD
app/Repositories/ConversionRateRepository.php
+<?php
+
+namespace App\Repositories;
+
+use App\Models\ConversionRate;
+
+class ConversionRateRepository
+{
+ public function createOrUpdate(int $baseCurrencyId, int $targetCurrencyId, float $rate): void
+ {
+ $existingConversionRate = ConversionRate::where('base_currency_id', $baseCurrencyId)
+ ->where('target_currency_id', $targetCurrencyId)
+ ->first();
+
+ if ($existingConversionRate) {
+ $existingConversionRate->fill([
+ 'rate' => $rate
+ ])->save();
+ } else {
+ ConversionRate::create([
+ 'base_currency_id' => $baseCurrencyId,
+ 'target_currency_id' => $targetCurrencyId,
+ 'rate' => $rate
+ ]);
+ }
+ }
+}

MODIFY
composer.json
composer.json
"doctrine/dbal": "^2.8",
"fideloper/proxy": "^4.0",
"fruitcake/laravel-cors": "^1.0",
+ "guzzlehttp/guzzle": "^7.0",
"intervention/image": "^2.4",
"laravel/framework": "^7.0",
"laravel/tinker": "^2.0",
"martinlindhe/laravel-vue-i18n-generator": "^0.1.39",
"nesbot/carbon": "^2.32",
+ "symfony/dom-crawler": "^5.1",
"symfony/process": "^5.0"
},
"require-dev": {

Create job that fetches conversion rates