*/ public static function builtInCodes(): array { return array_values(array_map( fn (ProductBillingCycle $cycle) => $cycle->value, array_filter(ProductBillingCycle::cases(), fn (ProductBillingCycle $cycle) => $cycle->months() > 0) )); } /** * @return list */ public static function builtInMonths(): array { return array_values(array_filter(array_map( fn (ProductBillingCycle $cycle) => $cycle->months(), ProductBillingCycle::cases() ))); } /** * @return list */ public static function customMonths(): array { $saved = json_decode((string) Setting::where('key', self::SETTING_KEY)->value('value'), true); return self::normalizeMonths(is_array($saved) ? $saved : []); } /** * @param array $months * @return list */ public static function saveCustomMonths(array $months): array { $normalized = self::normalizeMonths($months); Setting::updateOrCreate(['key' => self::SETTING_KEY], ['value' => json_encode($normalized)]); return $normalized; } /** * @return list */ public static function codes(): array { $codes = [...self::builtInCodes(), ...array_map(self::codeFor(...), self::customMonths())]; usort($codes, fn (string $a, string $b) => self::monthsFor($a) <=> self::monthsFor($b)); return $codes; } public static function codeFor(int $months): string { return self::CODE_PREFIX . $months; } public static function monthsFor(?string $code): ?int { if ($code === null || $code === '') { return null; } $builtIn = ProductBillingCycle::tryFrom($code); if ($builtIn) { return $builtIn->months(); } return preg_match('/^' . self::CODE_PREFIX . '(\d{1,3})$/', $code, $m) && (int) $m[1] > 0 ? (int) $m[1] : null; } public static function labelForMonths(int $months): string { return $months > 0 && $months % 12 === 0 ? ($months / 12) . ' năm' : $months . ' tháng'; } public static function labelForCode(?string $code): ?string { $months = self::monthsFor($code); return $months ? self::labelForMonths($months) : null; } /** * @param array $months * @return list */ private static function normalizeMonths(array $months): array { $valid = array_filter( array_map('intval', $months), fn (int $m) => $m >= 1 && $m <= self::MAX_MONTHS && !in_array($m, self::builtInMonths(), true) ); $unique = array_values(array_unique($valid)); sort($unique); return $unique; } }