Kur Modülü Yazma
Kur modülü bir oran kaynağıdır. Tek bir soruyu yanıtlar: benim para birimimin bir birimi şu diğerlerinde ne eder? Zamanlanmış eşitleme yanıtı para birimi tablosuna yazar.
Genel Bakış
Kur, en küçük modül tipidir ve taban sınıfı olmayan tek tiptir. Sözleşme ördek tiplemesiyle kurulur: çekirdek sınıfınızı çözer, adına göre bir metot arar ve çağırır. Yedi modül gelir; ücretsiz varsayılan OpenRates ve platformun kendi WAtlas'ı dahil.
Taban sınıf olmadığı için sözleşme yalnız çağrı noktalarında görünür. Üç tanedir ve tek bir zorunlu metotta anlaşırlar.
// 1. coremio/helpers/Money.php: tek oran yolu, eşitleme görevi bunu kullanır
$instance = self::currency_module(); // yapılandırılmış modül, yoksa OpenRates
if (!$instance) return (string) Language::gc("admin/financial/currency-module-unavailable");
$rates = $instance->exchange_rates($from, $to);
if (!$rates) return $instance->error; // modülün kendi mesajı operatöre ulaşır
// 2. coremio/operations/AdminFinancialCurrencies.php: ayar kaydı, ardından test butonu
$instance = Modules::getInstance("Currency", $module);
if ($instance && method_exists($instance, "save_config")) $instance->save_config($module_data[$module]);
// ...
if (!method_exists($instance, "exchange_rates")) throw new Exception("Module does not implement exchange_rates().");
$instance->config = array_replace_recursive($instance->config, $module_data[$module]); // kaydedilmemiş form değerleri
$rates = $instance->exchange_rates($localCode, $targets);
// 3. coremio/api/Resources/Admin/Concerns/FinancialCurrencies.php: aynı ikisi, API üzerinden
exchange_rates() zorunludur; modülünüzün tek bir ayarı olduğu anda save_config() da zorunlu olur. page_settings() o ayarı gösterir. $config ve $error çağıran taraftan doğrudan okunur, yani var ve public olmak zorundadır.
Ön Koşullar
- Bir temel para birimi ve karşılık kümesi dönen bir oran sağlayıcısı. Ücretsiz katmanına dikkat edin: eşitleme zamanlanmış çalışır.
- Sistemin yerel para birimi tanımlı olmalıdır; her oran ona karşı ifade edilir.
- Örnek
Modules::getInstance('Currency', 'Acme')ile alınır. - Önce
coremio/modules/Currency/OpenRates'i okuyun: yüz satırın altında, kimlik bilgisi istemez, sözleşmenin tamamı tek dosyada.ExchangeRateAPIbuna API anahtarı ve ayar alanı ekler.
Yapı
Acme.php namespace WISECP\Modules\Currency; class Acme (extends yok)
config.php DÜZ bir dizi, diğer modül tiplerindeki meta/settings ayrımı değil
lang/en.php
lang/tr.php
Sunucu, Ödeme ve Servis Sağlayıcı modülleri ['meta' => [...], 'settings' => [...]] döner. Kur modülü ayarların kendisini döner ve onları $this->config['api_key'] olarak okur. İç içe şekil, hep boş kalan bir anahtar verir.
Sınıf hiçbir şeyi genişletmez, yani kendi durumunu kendi bildirir. Çağıranların dokunduğu arayüz bu dört özelliktir.
Adım Adım
Sınıfı ve Durumunu Bildirin
<?php
namespace WISECP\Modules\Currency;
class Acme
{
public string $name = "Acme";
public ?array $config = null;
public ?array $lang = null;
public ?string $error = null; // false döndüğünüzde çağıran bunu okur
public function __construct()
{
// Global sınıflar bu ad alanının içinde baştaki ters bölüyü ister,
// yoksa PHP WISECP\Modules\Currency\Modules sınıfını arar.
$this->config = \Modules::Config("Currency", $this->name);
$this->lang = \Modules::Lang("Currency", $this->name);
}
}
Oranları Çekin
Sağlayıcıdan $from'a karşı elinde ne varsa isteyin ve büyük harfli bir kod haritası döndürün. Sonucu $to'ya göre süzmeyin.
Ayarı Gösterin ve Kalıcılaştırın
page_settings() kur ayarları ekranı için ham işaretleme döner. Her girdiye module_data[{ModulAdi}][{anahtar}] adını verin: kaydetme operation'ının save_config()'a verdiği dizi budur. Test butonu aynı diziyi gönderir ve bellekteki $this->config'in üzerine bindirir. Bir anahtar kaydedilmeden önce denenebilir.
Güvenmeden Önce Doğrulayın
- Modülü kur ayarları ekranından seçip test butonuna tıklayın. Yerel para birimine karşı iki hedefle
exchange_rates()çağırır. - Eşitlemeyi çalıştırın, sonra bir para biriminin
currencies.ratedeğerini sağlayıcının kendi sitesiyle karşılaştırın. - Bir çevrim yapın. Ters kaydedilmiş bir oran makul görünen ama yanlış fiyatlar üretir.
Referans
Sözleşme
// ZORUNLU
public function exchange_rates(string $from = '', array $to = []): array|false;
// Modülün herhangi bir ayarı olduğu anda ZORUNLU
public function save_config(array $data = []): bool;
// İSTEĞE BAĞLI: kur ayarları ekranı için işaretleme
public function page_settings(): string;
// Çağıranların doğrudan okuduğu public durum
public string $name;
public ?array $config; // test butonu bellekte bunun üzerine yazar
public ?array $lang;
public ?string $error; // exchange_rates() false döndüğünde operatöre gösterilir
general/currency'den çözülür. Sağlayıcınız küçük harf istiyorsa siz küçültün.
[para_birimi_id => 'KOD'] geçer; test butonu düz bir kod listesi geçer. Sonucunuzun anahtarı olarak asla kullanmayın.
['USD' => 32.15, 'EUR' => 35.02]; büyük harfli kodlar, float değerler ya da false. Boş dizi, test noktasında başarısızlık sayılır.
$from'un bir biriminin hedef cinsinden karşılığı. Saklanan kolon tutar / oran_kaynak * oran_hedef olarak kullanılır; ters çevirmek her fiyatı bozar.
OpenRates'e düşer. Yalnız OpenRates yüklenemezse null döner.
$error'ünüzü aynen geri verir: çağırana bir dize (hiç mesaj koymadıysanız null) ulaşır, asla false değil. Her çağıranın is_array() ile bakmasının sebebi budur.
Günlük Çağrı Tavanı
Yardımcı, modülünüze ulaşmadan önce günün çağrılarını coremio/storage/currency-overload-limit.php içinde sayar. 48 çağrıda sağlayıcıya dokunmadan admin/financial/currency-api-limit-exceeded mesajını etkin dilde döner. Bu, yanlış zamanlanmış bir görevden ücretsiz katmanı korur.
Eşitleme Yanıtınızla Ne Yapar
$rates = Money::get_exchange_rates($localCode, $targets);
if (!is_array($rates))
return ['success' => false, 'result' => ['error' => is_string($rates) && trim($rates) !== '' ? $rates : self::NO_RATES]];
// Genişletme noktası: sağlayıcının fiyatlayamadığı bir para birimini enjekte edin ya da birini ezin.
Hook::runRefs('filter:money.exchange_rates_fetch', $rates, $localCode, $targets);
// Büyük harfli kod => para birimi id'si; böylece her şeyi döndüren bir sağlayıcı
// yine de yalnız sistemde gerçekten var olan para birimlerini günceller.
$codeToId = [];
foreach ($targets as $cid => $code) $codeToId[strtoupper($code)] = (int) $cid;
foreach ($rates as $code => $rate) {
$codeUp = strtoupper((string) $code);
$rate = (float) $rate;
if (!isset($codeToId[$codeUp])) continue; // burada bir para birimi değil: yok sayılır
if ($rate <= 0 || $rate > 999999999999.99999999) { $skipped++; continue; } // tutarlılık koruması
WDB::update('currencies', ['rate' => $rate])->where('id', '=', $codeToId[$codeUp])->save();
}
Hook::run('action:money.exchange_rates_updated', $changes, $localCode);
İkisinin de API ikizi vardır: POST /financial/currency-modules/test canlı sağlayıcı çağrısını yapar, POST /financial/currencies/sync görevi anında dağıtır.
Örnek
Tek kimlik bilgisi olan eksiksiz bir modül. Üçünü bağlayan şey alanın adıdır: page_settings()'in gösterdiği şey save_config()'un aldığı şeydir, o da exchange_rates()'in okuduğu şeydir.
public function exchange_rates(string $from = '', array $to = []): array|false
{
$apiKey = (string) ($this->config["api_key"] ?? '');
if ($apiKey === '') {
$this->error = "Acme api_key is not configured.";
return false;
}
$url = "https://api.example.com/v1/" . urlencode($apiKey) . "/latest/" . urlencode(strtoupper($from));
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
$response = curl_exec($ch);
if ($response === false) $this->error = curl_error($ch) ?: "Currency provider request failed.";
curl_close($ch);
// Erken dönüşlerden ÖNCE kaydedin: bozuk bir anahtarı ayıklayan operatörün
// yalnız hatayı değil, hatayı üreten isteği de görmesi gerekir.
\Modules::save_log("Currency", $this->name, "exchange", [
'api_url' => $url,
'from' => $from,
'to' => $to,
], $response, $this->error);
if ($this->error) return false;
$decoded = \Utility::jdecode(trim((string) $response), true);
if (!is_array($decoded) || ($decoded["result"] ?? '') !== 'success') {
$this->error = (string) ($decoded["error-type"] ?? 'Acme returned an error.');
return false;
}
// Büyük harfli anahtarlar, float değerler. $to süzgeç olarak KULLANILMAZ: eşitleme
// tanıdığını tutar, kalanını yok sayar.
$result = [];
foreach ((array) ($decoded["conversion_rates"] ?? []) as $code => $rate)
$result[strtoupper((string) $code)] = (float) $rate;
return $result;
}
public function page_settings(): string
{
$apiKey = htmlspecialchars((string) ($this->config["api_key"] ?? ''), ENT_QUOTES);
// Girdinin ADI, kaydetme operation'ıyla yapılan sözleşmedir:
// module_data[Acme][api_key], save_config() içine $data['api_key'] olarak gelir.
return '<div class="row mb-0 align-items-center">'
. '<label for="acme-apikey" class="col-sm-3 col-form-label fw-semibold">API Key</label>'
. '<div class="col-sm-9">'
. '<input type="text" class="form-control" id="acme-apikey"'
. ' name="module_data[Acme][api_key]" value="' . $apiKey . '" placeholder="api_key">'
. '</div></div>';
}
public function save_config(array $data = []): bool
{
// Birleştirin, değiştirmeyin: kısmi bir gönderim diğer anahtarları silmemelidir.
$merged = array_replace_recursive($this->config ?: [], $data);
// file_write() bir .php hedefinde opcode önbelleğini geçersiz kılar; kaydedilen
// değerin daha bir sonraki istekte okunabilmesinin sebebi budur.
return (bool) \FileManager::file_write(
__DIR__ . DS . "config.php",
\Utility::array_export($merged, ['pwith' => true])
);
}
<?php
return [
'api_key' => '',
'help-link' => 'https://example.com',
];
Tuzaklar
Anahtarları eşitlemeden gelirken para birimi id'si, test butonundan gelirken düz tam sayıdır. Ona göre süzmek iki çağıranın farklı sonuç almasına yol açar. Her şeyi döndürün, eşlemeyi kodla çekirdek yapsın.
Çağıran $instance->error'ı aynen yazar. Mesaj yoksa operatör yalnız "kur modülü kullanılabilir bir kur döndürmedi" gibi genel bir not görür; bu not sebebi söylemez.
Sayaç başarılı çağrı başına değil, çağrı başına artar; bir test döngüsü zamanlanmış çalışmayla aynı bütçeyi yakar. Sağlayıcıdan şüphelenmeden önce tavana bakın.
Dosya namespace WISECP\Modules\Currency bildirir; nitelenmemiş bir Modules::Config() o ad alanındaki bir sınıfa çözülür ve çalışma anında patlar.
Seçili modül yüklenemediğinde çözümleyici kimlik bilgisi istemeyen OpenRates'e düşer. Oranlar kaybolmaz, bayatlar.
İlgili Makaleler
Geri bildiriminiz için teşekkürler!
Yukarıda bulamadığınız her şey için destek ekibimiz her zaman yanınızda.