feat(slides): add sort_order column, bulk delete, and reorder endpoints

Add sort_order to slides table with migration and model fillable.
Add destroyBulk() for batch soft-delete by type/service_id and
reorder() for drag-and-drop slide ordering. Auto-assign sort_order
on image and zip uploads.
This commit is contained in:
Thorsten Bus 2026-03-02 23:02:19 +01:00
parent 04d271f96a
commit bef910b126
3 changed files with 79 additions and 1 deletions

View file

@ -76,6 +76,50 @@ public function destroy(Slide $slide): JsonResponse
]);
}
public function destroyBulk(Request $request): JsonResponse
{
$validated = $request->validate([
'type' => ['required', Rule::in(['information', 'moderation', 'sermon'])],
'service_id' => ['nullable', 'exists:services,id'],
]);
$query = Slide::where('type', $validated['type']);
if ($validated['service_id']) {
$query->where('service_id', $validated['service_id']);
} else {
// Information slides without service_id (global)
$query->whereNull('service_id');
}
$count = $query->count();
$query->delete(); // soft-delete via SoftDeletes trait
return response()->json([
'success' => true,
'message' => $count.' Folien wurden gelöscht.',
'count' => $count,
]);
}
public function reorder(Request $request): JsonResponse
{
$validated = $request->validate([
'slides' => ['required', 'array', 'min:1'],
'slides.*.id' => ['required', 'integer', 'exists:slides,id'],
'slides.*.sort_order' => ['required', 'integer', 'min:0'],
]);
foreach ($validated['slides'] as $item) {
Slide::where('id', $item['id'])->update(['sort_order' => $item['sort_order']]);
}
return response()->json([
'success' => true,
'message' => 'Reihenfolge wurde aktualisiert.',
]);
}
public function updateExpireDate(Request $request, Slide $slide): JsonResponse
{
if ($slide->type !== 'information') {
@ -97,6 +141,13 @@ public function updateExpireDate(Request $request, Slide $slide): JsonResponse
]);
}
private function nextSortOrder(string $type, ?int $serviceId): int
{
return (int) Slide::where('type', $type)
->when($serviceId, fn ($q) => $q->where('service_id', $serviceId), fn ($q) => $q->whereNull('service_id'))
->max('sort_order') + 1;
}
private function handleImage(
UploadedFile $file,
FileConversionService $conversionService,
@ -117,6 +168,7 @@ private function handleImage(
'expire_date' => $expireDate,
'uploader_name' => $uploaderName,
'uploaded_at' => now(),
'sort_order' => $this->nextSortOrder($type, $serviceId),
]);
return response()->json([
@ -179,6 +231,8 @@ private function handleZip(
$results = $conversionService->processZip($file);
$slides = [];
$sortOrder = $this->nextSortOrder($type, $serviceId);
foreach ($results as $result) {
// Skip PPT job results (they are handled asynchronously)
if (isset($result['job_id'])) {
@ -194,6 +248,7 @@ private function handleZip(
'expire_date' => $expireDate,
'uploader_name' => $uploaderName,
'uploaded_at' => now(),
'sort_order' => $sortOrder++,
]);
}

View file

@ -21,6 +21,7 @@ class Slide extends Model
'expire_date',
'uploader_name',
'uploaded_at',
'sort_order',
];
protected function casts(): array

View file

@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('slides', function (Blueprint $table) {
$table->unsignedInteger('sort_order')->default(0)->after('uploaded_at');
});
}
public function down(): void
{
Schema::table('slides', function (Blueprint $table) {
$table->dropColumn('sort_order');
});
}
};