mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-07-16 22:05:20 +00:00
Two plural outliers were the only directories in Services/ and Support/ that didn't follow the singular naming convention we normalized in commit 947d00a9 (Documents -> Document).
**Integrations/ExchangeRate/ -> ExchangeRate/.** The Integrations/ umbrella was both plural AND introduced an extra layer of nesting no other Support subdir had (Pdf/, Hashids/, Module/, Update/ are all one level deep). Dropping the umbrella fixes both problems in one move and matches the existing shape. When AI providers eventually land, they follow the same pattern as Support/Pdf/: a sibling subdir at Support/Ai/, not buried under an umbrella.
**Formatters/ -> Formatting/.** Formatter (singular noun) would have been awkward; Formatting (gerund describing the capability) reads naturally as a namespace segment. The subdir holds DateFormatter, TimeFormatter, TimeZones — classes whose common thread is 'things that do formatting', which the gerund captures better than either the singular or plural noun form.
10 files renamed, 4 consumers updated (DriverRegistryProvider, ExchangeRateProviderService, FormatsController, ExchangeRateDriverFactoryTest). 350 tests pass, Pint clean.
80 lines
2.5 KiB
PHP
80 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace App\Support\ExchangeRate;
|
|
|
|
use InvalidArgumentException;
|
|
use InvoiceShelf\Modules\Registry;
|
|
|
|
class ExchangeRateDriverFactory
|
|
{
|
|
/**
|
|
* Built-in driver fallback map.
|
|
*
|
|
* Kept as a backstop so that direct calls to register() (without going through
|
|
* the module Registry) continue to work. Built-in drivers are also registered
|
|
* via the Registry by DriverRegistryProvider — that registration is the
|
|
* canonical source for driver metadata (label, website, config_fields).
|
|
*
|
|
* @var array<string, class-string<ExchangeRateDriver>>
|
|
*/
|
|
protected static array $drivers = [
|
|
'currency_freak' => CurrencyFreakDriver::class,
|
|
'currency_layer' => CurrencyLayerDriver::class,
|
|
'open_exchange_rate' => OpenExchangeRateDriver::class,
|
|
'currency_converter' => CurrencyConverterDriver::class,
|
|
];
|
|
|
|
/**
|
|
* Register a custom exchange rate driver directly with the factory.
|
|
*
|
|
* Modules should prefer Registry::registerExchangeRateDriver() instead, which
|
|
* carries metadata (label, website, config_fields) the frontend needs to render
|
|
* a configuration form for the driver.
|
|
*/
|
|
public static function register(string $name, string $driverClass): void
|
|
{
|
|
static::$drivers[$name] = $driverClass;
|
|
}
|
|
|
|
public static function make(string $driver, string $apiKey, array $config = []): ExchangeRateDriver
|
|
{
|
|
$class = static::resolveDriverClass($driver);
|
|
|
|
if (! $class) {
|
|
throw new InvalidArgumentException("Unknown exchange rate driver: {$driver}");
|
|
}
|
|
|
|
return new $class($apiKey, $config);
|
|
}
|
|
|
|
/**
|
|
* Get all known driver names — both built-in/factory-registered and Registry-registered.
|
|
*
|
|
* @return array<int, string>
|
|
*/
|
|
public static function availableDrivers(): array
|
|
{
|
|
$local = array_keys(static::$drivers);
|
|
$registry = array_keys(Registry::allDrivers('exchange_rate'));
|
|
|
|
return array_values(array_unique(array_merge($local, $registry)));
|
|
}
|
|
|
|
/**
|
|
* Resolve a driver name to its concrete class.
|
|
*
|
|
* Checks the local $drivers map first (built-ins and factory::register() calls),
|
|
* then falls back to the module Registry.
|
|
*/
|
|
protected static function resolveDriverClass(string $driver): ?string
|
|
{
|
|
if (isset(static::$drivers[$driver])) {
|
|
return static::$drivers[$driver];
|
|
}
|
|
|
|
$meta = Registry::driverMeta('exchange_rate', $driver);
|
|
|
|
return $meta['class'] ?? null;
|
|
}
|
|
}
|