> */ 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 */ 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; } }