You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
7.4 KiB
215 lines
7.4 KiB
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Activity;
|
|
use App\Models\Banner;
|
|
use App\Models\Course;
|
|
use App\Support\ApiResponse;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Validation\Rule;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class BannerController extends Controller
|
|
{
|
|
use ApiResponse;
|
|
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
$query = Banner::query()->with(['course', 'activity']);
|
|
|
|
if ($request->filled('type')) {
|
|
$query->where('type', $request->query('type'));
|
|
}
|
|
if ($request->filled('status')) {
|
|
$query->where('status', (int) $request->query('status'));
|
|
}
|
|
if ($kw = $request->query('keyword')) {
|
|
$query->where(function ($q) use ($kw) {
|
|
$q->where('title', 'like', "%{$kw}%")
|
|
->orWhereHas('course', fn ($cq) => $cq->where('title', 'like', "%{$kw}%"))
|
|
->orWhereHas('activity', fn ($aq) => $aq->where('title', 'like', "%{$kw}%"));
|
|
});
|
|
}
|
|
|
|
$paginator = $query
|
|
->orderBy('sort')
|
|
->orderByDesc('id')
|
|
->paginate((int) $request->query('page_size', 20))
|
|
->withQueryString();
|
|
|
|
$paginator->getCollection()->transform(fn (Banner $b) => $this->serialize($b));
|
|
|
|
return $this->paginated($paginator);
|
|
}
|
|
|
|
public function show(int $banner): JsonResponse
|
|
{
|
|
$model = Banner::query()->with(['course', 'activity'])->findOrFail($banner);
|
|
|
|
return $this->ok($this->serialize($model));
|
|
}
|
|
|
|
public function store(Request $request): JsonResponse
|
|
{
|
|
$data = $this->validatedBanner($request);
|
|
$row = Banner::query()->create($data);
|
|
|
|
return $this->ok(['id' => $row->id], '已创建');
|
|
}
|
|
|
|
public function update(Request $request, int $banner): JsonResponse
|
|
{
|
|
$model = Banner::query()->findOrFail($banner);
|
|
$data = $this->validatedBanner($request, $model);
|
|
$model->fill($data);
|
|
$model->save();
|
|
|
|
return $this->ok($this->serialize($model->fresh(['course', 'activity'])), '已保存');
|
|
}
|
|
|
|
public function destroy(int $banner): JsonResponse
|
|
{
|
|
Banner::query()->findOrFail($banner)->delete();
|
|
|
|
return $this->ok(null, '已删除');
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function validatedBanner(Request $request, ?Banner $existing = null): array
|
|
{
|
|
$base = $request->validate([
|
|
'type' => array_merge(
|
|
[$existing ? 'sometimes' : 'required', 'string'],
|
|
[Rule::in([Banner::TYPE_COURSE, Banner::TYPE_ACTIVITY, Banner::TYPE_CUSTOM])]
|
|
),
|
|
'course_id' => ['nullable', 'integer', 'exists:courses,id'],
|
|
'activity_id' => ['nullable', 'integer', 'exists:activities,id'],
|
|
'title' => ['nullable', 'string', 'max:255'],
|
|
'cover_url' => ['nullable', 'string', 'max:512'],
|
|
'content_html' => ['nullable', 'string'],
|
|
'sort' => [$existing ? 'sometimes' : 'required', 'integer', 'min:0'],
|
|
'status' => ['sometimes', 'integer', 'in:0,1'],
|
|
]);
|
|
|
|
$type = $base['type'] ?? $existing?->type;
|
|
if (! $type) {
|
|
throw ValidationException::withMessages(['type' => ['请选择 Banner 类型']]);
|
|
}
|
|
|
|
$payload = [
|
|
'type' => $type,
|
|
'course_id' => null,
|
|
'activity_id' => null,
|
|
'title' => null,
|
|
'cover_url' => null,
|
|
'content_html' => null,
|
|
'sort' => isset($base['sort']) ? (int) $base['sort'] : ($existing?->sort ?? 0),
|
|
'status' => isset($base['status']) ? (int) $base['status'] : ($existing?->status ?? 1),
|
|
];
|
|
|
|
match ($type) {
|
|
Banner::TYPE_COURSE => $this->fillCourse($payload, $base, $existing),
|
|
Banner::TYPE_ACTIVITY => $this->fillActivity($payload, $base, $existing),
|
|
Banner::TYPE_CUSTOM => $this->fillCustom($payload, $base, $existing),
|
|
};
|
|
|
|
return $payload;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $payload
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
protected function fillCourse(array &$payload, array $data, ?Banner $existing): void
|
|
{
|
|
$courseId = $data['course_id'] ?? $existing?->course_id;
|
|
if (! $courseId) {
|
|
throw ValidationException::withMessages(['course_id' => ['请选择课程']]);
|
|
}
|
|
if (! Course::query()->whereKey($courseId)->exists()) {
|
|
throw ValidationException::withMessages(['course_id' => ['所选课程不存在']]);
|
|
}
|
|
$payload['course_id'] = (int) $courseId;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $payload
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
protected function fillActivity(array &$payload, array $data, ?Banner $existing): void
|
|
{
|
|
$activityId = $data['activity_id'] ?? $existing?->activity_id;
|
|
if (! $activityId) {
|
|
throw ValidationException::withMessages(['activity_id' => ['请选择活动']]);
|
|
}
|
|
if (! Activity::query()->whereKey($activityId)->exists()) {
|
|
throw ValidationException::withMessages(['activity_id' => ['所选活动不存在']]);
|
|
}
|
|
$payload['activity_id'] = (int) $activityId;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $payload
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
protected function fillCustom(array &$payload, array $data, ?Banner $existing): void
|
|
{
|
|
$title = array_key_exists('title', $data)
|
|
? trim((string) $data['title'])
|
|
: ($existing?->title ?? '');
|
|
$cover = array_key_exists('cover_url', $data)
|
|
? trim((string) $data['cover_url'])
|
|
: ($existing?->cover_url ?? '');
|
|
$content = array_key_exists('content_html', $data)
|
|
? (string) $data['content_html']
|
|
: ($existing?->content_html ?? '');
|
|
|
|
if ($title === '') {
|
|
throw ValidationException::withMessages(['title' => ['请填写标题']]);
|
|
}
|
|
if ($cover === '') {
|
|
throw ValidationException::withMessages(['cover_url' => ['请上传封面图']]);
|
|
}
|
|
|
|
$payload['title'] = $title;
|
|
$payload['cover_url'] = $cover;
|
|
$payload['content_html'] = trim(strip_tags($content)) === '' ? null : $content;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function serialize(Banner $b): array
|
|
{
|
|
$summary = match ($b->type) {
|
|
Banner::TYPE_COURSE => $b->course?->title,
|
|
Banner::TYPE_ACTIVITY => $b->activity?->title,
|
|
Banner::TYPE_CUSTOM => $b->title,
|
|
default => null,
|
|
};
|
|
|
|
return [
|
|
'id' => $b->id,
|
|
'type' => $b->type,
|
|
'type_label' => Banner::typeLabel($b->type),
|
|
'course_id' => $b->course_id,
|
|
'course_title' => $b->course?->title,
|
|
'activity_id' => $b->activity_id,
|
|
'activity_title' => $b->activity?->title,
|
|
'title' => $b->title,
|
|
'cover_url' => $b->cover_url,
|
|
'content_html' => $b->content_html,
|
|
'summary' => $summary,
|
|
'sort' => (int) $b->sort,
|
|
'status' => (int) $b->status,
|
|
'created_at' => $b->created_at?->toIso8601String(),
|
|
'updated_at' => $b->updated_at?->toIso8601String(),
|
|
];
|
|
}
|
|
}
|