'boolean', 'low_balance_alerts' => 'boolean', 'notify_registrations' => 'boolean', 'notify_payouts' => 'boolean', 'default_style' => 'array', 'event_defaults' => 'array', ]; /** @return list */ public static function storableEventDefaultKeys(): array { return [ 'currency', 'mode', 'badge_size', 'brand_color', 'organizer', 'registration_open', 'badge_fields', 'format', ]; } /** @return list */ public static function storableStyleKeys(): array { return [ 'foreground', 'background', 'error_correction', 'margin', 'module_style', 'finder_outer', 'finder_inner', 'frame_style', 'frame_text', 'frame_color', 'gradient_type', 'gradient_color1', 'gradient_color2', 'gradient_rotation', ]; } public function user(): BelongsTo { return $this->belongsTo(User::class); } public function resolvedDefaultType(): string { $type = (string) ($this->default_type ?? QrCode::TYPE_EVENT); return in_array($type, QrTypeCatalog::eventsTypes(), true) ? $type : QrCode::TYPE_EVENT; } /** @return array */ public function resolvedEventDefaults(): array { $defaults = [ 'currency' => 'GHS', 'mode' => 'ticketing', 'badge_size' => '4x3', 'brand_color' => '#4f46e5', 'organizer' => '', 'registration_open' => true, 'badge_fields' => ['Company', 'Role'], ]; $stored = collect($this->event_defaults ?? []) ->only(self::storableEventDefaultKeys()) ->filter(fn ($value) => $value !== null && $value !== '') ->all(); if (isset($stored['badge_fields']) && is_array($stored['badge_fields'])) { $stored['badge_fields'] = array_values(array_filter( array_map('strval', $stored['badge_fields']), fn (string $field) => trim($field) !== '', )); if ($stored['badge_fields'] === []) { unset($stored['badge_fields']); } } return array_merge($defaults, $stored); } /** @param array|null $input */ public static function sanitizeEventDefaults(?array $input): ?array { if ($input === null) { return null; } $allowedModes = ['ticketing', 'contributions', 'free']; $allowedSizes = ['4x3', '4x6', 'cr80']; $allowedCurrencies = ['GHS', 'USD', 'NGN', 'KES']; $mode = (string) ($input['mode'] ?? 'ticketing'); $badgeSize = (string) ($input['badge_size'] ?? '4x3'); $currency = strtoupper(trim((string) ($input['currency'] ?? 'GHS'))); $badgeFields = array_values(array_filter( array_map(fn ($field) => mb_substr(trim((string) $field), 0, 40), (array) ($input['badge_fields'] ?? [])), fn (string $field) => $field !== '', )); $brandColor = (string) ($input['brand_color'] ?? '#4f46e5'); if (! preg_match('/^#[0-9a-fA-F]{6}$/', $brandColor)) { $brandColor = '#4f46e5'; } return [ 'currency' => in_array($currency, $allowedCurrencies, true) ? $currency : 'GHS', 'mode' => in_array($mode, $allowedModes, true) ? $mode : 'ticketing', 'badge_size' => in_array($badgeSize, $allowedSizes, true) ? $badgeSize : '4x3', 'brand_color' => $brandColor, 'organizer' => mb_substr(trim((string) ($input['organizer'] ?? '')), 0, 120), 'registration_open' => filter_var($input['registration_open'] ?? true, FILTER_VALIDATE_BOOL), 'badge_fields' => $badgeFields !== [] ? $badgeFields : ['Company', 'Role'], 'format' => in_array($input['format'] ?? 'in_person', ['in_person', 'virtual', 'hybrid'], true) ? ($input['format'] ?? 'in_person') : 'in_person', ]; } /** @return array */ public function resolvedDefaultStyle(): array { $stored = collect($this->default_style ?? []) ->only(self::storableStyleKeys()) ->filter(fn ($value) => $value !== null && $value !== '') ->all(); return QrStyleDefaults::merge($stored !== [] ? $stored : null); } /** @param array|null $input */ public static function sanitizeDefaultStyle(?array $input): ?array { if ($input === null) { return null; } $merged = QrStyleDefaults::merge( collect($input)->only(self::storableStyleKeys())->all(), ); return collect($merged)->only(self::storableStyleKeys())->all(); } }