111 lines
2.9 KiB
PHP
111 lines
2.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Support;
|
|
|
|
use App\Enums\Product\ProductTypeEnums;
|
|
use App\Models\Setting;
|
|
use Illuminate\Validation\Rule;
|
|
|
|
final class ProductBillingCycles
|
|
{
|
|
public const SETTING_KEY = 'product_billing_cycles';
|
|
|
|
private const YEARLY_TYPES = ['domain', 'ssl'];
|
|
|
|
/**
|
|
* @return array<string, list<string>>
|
|
*/
|
|
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<string>
|
|
*/
|
|
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<string, mixed>
|
|
*/
|
|
public static function rules(): array
|
|
{
|
|
return [
|
|
'cycles' => 'required|array',
|
|
'cycles.*' => 'array',
|
|
'cycles.*.*' => ['string', Rule::in(BillingCycleCatalog::codes())],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array<string, array<int, string>> $cycles
|
|
* @return array<string, list<string>>
|
|
*/
|
|
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<array{type: string, label: string}>
|
|
*/
|
|
public static function typeOptions(): array
|
|
{
|
|
return array_map(
|
|
fn (string $type) => ['type' => $type, 'label' => ProductTypeEnums::from($type)->label()],
|
|
self::types()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return list<string>
|
|
*/
|
|
private static function types(): array
|
|
{
|
|
return array_values(array_filter(
|
|
array_column(ProductTypeEnums::cases(), 'value'),
|
|
fn (string $type) => $type !== ProductTypeEnums::ADDON->value
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @return list<string>
|
|
*/
|
|
private static function defaults(string $type): array
|
|
{
|
|
return in_array($type, self::YEARLY_TYPES, true) ? ['annually'] : BillingCycleCatalog::codes();
|
|
}
|
|
|
|
/**
|
|
* @param array<int, string> $cycles
|
|
* @return list<string>
|
|
*/
|
|
private static function normalize(array $cycles): array
|
|
{
|
|
return array_values(array_intersect(BillingCycleCatalog::codes(), $cycles));
|
|
}
|
|
}
|