> */ public static function all(): array { $saved = json_decode((string) Setting::where('key', self::SETTING_KEY)->value('value'), true); $saved = is_array($saved) ? $saved : []; $result = []; foreach (self::types() as $type) { $result[$type] = self::normalize($saved[$type] ?? self::defaults($type)); } return $result; } /** * @return list */ public static function forType(?string $type): array { return self::all()[$type] ?? BillingCycleCatalog::codes(); } public static function allows(?string $type, string $cycle): bool { return $cycle === 'onetime' || in_array($cycle, self::forType($type), true); } /** * @return array */ public static function rules(): array { return [ 'cycles' => 'required|array', 'cycles.*' => 'array', 'cycles.*.*' => ['string', Rule::in(BillingCycleCatalog::codes())], ]; } /** * @param array> $cycles * @return array> */ public static function save(array $cycles): array { $merged = array_merge(self::all(), array_intersect_key($cycles, array_flip(self::types()))); $normalized = array_map(self::normalize(...), $merged); Setting::updateOrCreate(['key' => self::SETTING_KEY], ['value' => json_encode($normalized)]); return $normalized; } /** * @return list */ public static function typeOptions(): array { return array_map( fn (string $type) => ['type' => $type, 'label' => ProductTypeEnums::from($type)->label()], self::types() ); } /** * @return list */ private static function types(): array { return array_values(array_filter( array_column(ProductTypeEnums::cases(), 'value'), fn (string $type) => $type !== ProductTypeEnums::ADDON->value )); } /** * @return list */ private static function defaults(string $type): array { return in_array($type, self::YEARLY_TYPES, true) ? ['annually'] : BillingCycleCatalog::codes(); } /** * @param array $cycles * @return list */ private static function normalize(array $cycles): array { return array_values(array_intersect(BillingCycleCatalog::codes(), $cycles)); } }