New_Micro_Learning/backend/app/Modules/Courses/Http/CourseVersionController.php

97 خطوط
5.0 KiB
PHP

<?php
namespace App\Modules\Courses\Http;
use App\Http\Controllers\Controller;
use App\Modules\Courses\Application\CourseVersionComparison;
use App\Modules\Courses\Domain\Course;
use App\Modules\Courses\Domain\CourseVersion;
use App\Modules\Identity\Application\RolePermissions;
use App\Modules\Identity\Domain\Enums\Permission;
use App\Modules\Tenancy\Application\TenantContext;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
final class CourseVersionController extends Controller
{
public function __construct(private readonly TenantContext $tenant, private readonly RolePermissions $permissions, private readonly CourseVersionComparison $comparison) {}
public function index(Request $request, string $course): JsonResponse
{
$this->authorize($request);
$courseModel = $this->course($course);
$filters = $request->validate([
'search' => ['nullable', 'string', 'max:100'],
'status' => ['nullable', Rule::in(['draft', 'in_review', 'published'])],
'page' => ['nullable', 'integer', 'min:1'],
'perPage' => ['nullable', 'integer', 'min:1', 'max:50'],
]);
$base = CourseVersion::query()->where('organization_id', $this->tenant->id())->where('course_id', $courseModel->getKey());
$summary = [
'total' => (clone $base)->count(),
'published' => (clone $base)->where('status', 'published')->count(),
'drafts' => (clone $base)->whereIn('status', ['draft', 'in_review'])->count(),
];
$search = trim((string) ($filters['search'] ?? ''));
$versions = $base
->when($search !== '', function ($query) use ($search) {
$query->where(function ($nested) use ($search) {
$nested->where('title', 'like', '%'.$search.'%')->orWhere('description', 'like', '%'.$search.'%');
if (ctype_digit($search)) {
$nested->orWhere('version_number', (int) $search);
}
});
})
->when($filters['status'] ?? null, fn ($query, string $status) => $query->where('status', $status))
->with(['creator:id,name', 'publisher:id,name', 'modules.lessons.blocks', 'assessments.questions'])
->withCount(['modules', 'lessons', 'blocks', 'assessments'])
->orderByDesc('version_number')
->paginate($filters['perPage'] ?? 10);
return response()->json(['data' => [
'summary' => $summary,
'items' => $versions->getCollection()->map(fn (CourseVersion $version) => $this->payload($version))->values(),
'meta' => ['currentPage' => $versions->currentPage(), 'lastPage' => $versions->lastPage(), 'total' => $versions->total()],
]]);
}
public function compare(Request $request, string $course): JsonResponse
{
$this->authorize($request);
$courseModel = $this->course($course);
$data = $request->validate(['ids' => ['required', 'array', 'size:2'], 'ids.*' => ['required', 'string', 'distinct']]);
$versions = CourseVersion::query()->where('organization_id', $this->tenant->id())->where('course_id', $courseModel->getKey())->whereIn('id', $data['ids'])->get()->keyBy(fn (CourseVersion $version) => (string) $version->getKey());
abort_unless($versions->count() === 2, 422, 'نسخه‌های انتخاب‌شده قابل مقایسه نیستند.');
$before = $versions->get($data['ids'][0]);
$after = $versions->get($data['ids'][1]);
return response()->json(['data' => $this->comparison->compare($before, $after)]);
}
private function payload(CourseVersion $version): array
{
$actor = $version->status->value === 'published' ? ($version->publisher ?? $version->creator) : $version->creator;
return [
'id' => $version->getKey(), 'number' => $version->version_number, 'status' => $version->status->value,
'title' => $version->title, 'description' => $version->description, 'sourceVersionId' => $version->source_version_id,
'createdAt' => $version->created_at?->toISOString(), 'updatedAt' => $version->updated_at?->toISOString(), 'publishedAt' => $version->published_at?->toISOString(),
'actor' => $actor ? ['id' => $actor->getKey(), 'name' => $actor->name] : null,
'moduleCount' => (int) $version->modules_count, 'lessonCount' => (int) $version->lessons_count,
'assessmentCount' => (int) $version->assessments_count, 'blockCount' => (int) $version->blocks_count,
'unpublishedChanges' => $this->comparison->unpublishedChangeCount($version),
];
}
private function course(string $id): Course
{
return Course::query()->where('organization_id', $this->tenant->id())->findOrFail($id);
}
private function authorize(Request $request): void
{
abort_unless($this->permissions->allows($request->user(), Permission::CoursesAuthor), 403);
}
}