новости

This commit is contained in:
2026-03-22 17:28:03 +04:00
parent 573e2afbbf
commit 324d1ad75c
11 changed files with 594 additions and 6 deletions
@@ -0,0 +1,45 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\SaveNewsRequest;
use App\Models\News;
use App\Services\NewsService;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
class NewsController extends Controller
{
public function index(NewsService $news): View
{
return view('admin.news.index', [
'newsItems' => $news->listForAdmin(),
'newsStats' => $news->stats(),
]);
}
public function store(SaveNewsRequest $request, NewsService $news): RedirectResponse
{
$news->create($request->validated(), $request->user(), $request->file('image'));
return back()->with('status', 'Новость добавлена.');
}
public function update(
News $newsItem,
SaveNewsRequest $request,
NewsService $news,
): RedirectResponse {
$news->update($newsItem, $request->validated(), $request->file('image'));
return back()->with('status', 'Новость обновлена.');
}
public function destroy(News $newsItem, NewsService $news): RedirectResponse
{
$news->delete($newsItem);
return back()->with('status', 'Новость удалена.');
}
}
@@ -0,0 +1,66 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\News;
use Illuminate\Http\JsonResponse;
use OpenApi\Attributes as OA;
class LauncherNewsController extends Controller
{
#[OA\Get(
path: '/api/launcher/news',
summary: 'Список новостей лаунчера',
description: 'Возвращает опубликованные новости для отображения в лаунчере. Отсортированы по sort_order и дате создания.',
tags: ['Launcher'],
security: [['bearerAuth' => []]],
responses: [
new OA\Response(
response: 200,
description: 'Список новостей',
content: new OA\JsonContent(
properties: [
new OA\Property(
property: 'data',
type: 'array',
items: new OA\Items(
properties: [
new OA\Property(property: 'id', type: 'integer', example: 1),
new OA\Property(property: 'title', type: 'string', example: 'Обновление 1.2'),
new OA\Property(property: 'body', type: 'string', example: 'Описание обновления...'),
new OA\Property(property: 'image_url', type: 'string', format: 'uri', nullable: true, example: 'https://storage.yandexcloud.net/warcraft-client/news/update-1.2.jpg'),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
],
),
),
],
),
),
new OA\Response(
response: 401,
description: 'Не авторизован',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'message', type: 'string', example: 'Unauthenticated.'),
],
),
),
],
)]
public function index(): JsonResponse
{
$news = News::published()
->ordered()
->get()
->map(fn (News $item) => [
'id' => $item->id,
'title' => $item->title,
'body' => $item->body,
'image_url' => $item->image_url,
'created_at' => $item->created_at->toIso8601String(),
]);
return response()->json(['data' => $news]);
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class SaveNewsRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
protected function prepareForValidation(): void
{
$this->merge([
'is_published' => $this->boolean('is_published'),
]);
}
/**
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:120'],
'body' => ['required', 'string', 'max:20000'],
'sort_order' => ['required', 'integer', 'min:0', 'max:9999'],
'is_published' => ['nullable', 'boolean'],
'image' => ['nullable', 'image', 'max:2048'],
];
}
/**
* @return array<string, string>
*/
public function messages(): array
{
return [
'title.required' => 'Укажи заголовок новости.',
'title.max' => 'Заголовок не должен быть длиннее 120 символов.',
'body.required' => 'Заполни текст новости.',
'body.max' => 'Текст новости слишком длинный.',
'sort_order.required' => 'Укажи порядок вывода.',
'sort_order.integer' => 'Порядок вывода должен быть числом.',
'sort_order.min' => 'Порядок вывода не может быть отрицательным.',
'sort_order.max' => 'Порядок вывода слишком большой.',
'image.image' => 'Файл должен быть изображением.',
'image.max' => 'Изображение не должно превышать 2 МБ.',
];
}
}