*/ public function listForAdmin(): Collection { return LoginScreenNews::query() ->ordered() ->get(); } /** * @return array{total: int, published: int, draft: int} */ public function stats(): array { $total = LoginScreenNews::query()->count(); $published = LoginScreenNews::query()->published()->count(); return [ 'total' => $total, 'published' => $published, 'draft' => $total - $published, ]; } /** * @param array{title: string, body: string, sort_order: int, is_published?: bool} $attributes */ public function create(array $attributes, ?GameAccountUser $actor): LoginScreenNews { return LoginScreenNews::query()->create([ ...$this->normalizeAttributes($attributes), 'created_by_account_id' => $actor?->id, 'created_by_username' => $actor?->username, 'updated_by_account_id' => $actor?->id, 'updated_by_username' => $actor?->username, ]); } /** * @param array{title: string, body: string, sort_order: int, is_published?: bool} $attributes */ public function update(LoginScreenNews $newsItem, array $attributes, ?GameAccountUser $actor): LoginScreenNews { $newsItem->forceFill([ ...$this->normalizeAttributes($attributes), 'updated_by_account_id' => $actor?->id, 'updated_by_username' => $actor?->username, ])->save(); return $newsItem->refresh(); } public function delete(LoginScreenNews $newsItem): void { $newsItem->delete(); } public function renderFeed(): string { $sections = LoginScreenNews::query() ->published() ->ordered() ->pluck('body') ->map(fn (mixed $body): string => $this->normalizeBody((string) $body)) ->filter(fn (string $body): bool => $body !== '') ->values(); $payload = $sections->isNotEmpty() ? $sections->implode("\n".self::SECTION_SEPARATOR."\n") : self::EMPTY_MESSAGE; return self::FEED_HEADER."\n\n".$payload."\n"; } /** * @param array{title: string, body: string, sort_order: int, is_published?: bool} $attributes * @return array{title: string, body: string, sort_order: int, is_published: bool} */ private function normalizeAttributes(array $attributes): array { return [ 'title' => trim($attributes['title']), 'body' => $this->normalizeBody($attributes['body']), 'sort_order' => (int) $attributes['sort_order'], 'is_published' => (bool) ($attributes['is_published'] ?? false), ]; } private function normalizeBody(string $body): string { $normalized = trim(str_replace(["\r\n", "\r"], "\n", $body)); if (Str::startsWith($normalized, self::FEED_HEADER)) { $normalized = trim(Str::after($normalized, self::FEED_HEADER)); } return $normalized; } }