242 lines
9.9 KiB
PHP
242 lines
9.9 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\DocumentType;
|
|
use App\Models\Student;
|
|
use App\Models\StudentDocument;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Validation\Rule;
|
|
use Illuminate\View\View;
|
|
use RealRashid\SweetAlert\Facades\Alert;
|
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class StudentDocumentController extends Controller
|
|
{
|
|
public function create(Student $student): View
|
|
{
|
|
return view('student-documents.create', [
|
|
'student' => $student,
|
|
'documentTypes' => DocumentType::where('is_active', true)->orderBy('name')->get(),
|
|
'verificationStatuses' => $this->verificationStatuses(),
|
|
'defaultMaxFileSizeKb' => 5120,
|
|
]);
|
|
}
|
|
|
|
public function store(Request $request, Student $student): RedirectResponse
|
|
{
|
|
$data = $this->validateDocument($request);
|
|
$uploadedFile = $request->file('document_file');
|
|
$path = $uploadedFile->store("student-documents/{$student->id}", 'public');
|
|
|
|
StudentDocument::create([
|
|
'student_id' => $student->id,
|
|
'document_type_id' => $data['document_type_id'],
|
|
'file_name' => $uploadedFile->getClientOriginalName(),
|
|
'file_path' => $path,
|
|
'file_extension' => $uploadedFile->getClientOriginalExtension(),
|
|
'mime_type' => $uploadedFile->getMimeType(),
|
|
'file_size' => $uploadedFile->getSize(),
|
|
'document_number' => $data['document_number'] ?? null,
|
|
'issued_date' => $data['issued_date'] ?? null,
|
|
'expired_date' => $data['expired_date'] ?? null,
|
|
'description' => $data['description'] ?? null,
|
|
'verification_status' => $data['verification_status'],
|
|
'verification_note' => $data['verification_note'] ?? null,
|
|
'uploaded_by' => auth()->id(),
|
|
'verified_by' => in_array($data['verification_status'], ['valid', 'invalid', 'revision']) ? auth()->id() : null,
|
|
'verified_at' => in_array($data['verification_status'], ['valid', 'invalid', 'revision']) ? now() : null,
|
|
]);
|
|
|
|
Alert::success('Berhasil', 'Dokumen siswa berhasil diunggah.');
|
|
|
|
return redirect()->route('students.show', $student)->with('success', 'Dokumen siswa berhasil diunggah.');
|
|
}
|
|
|
|
public function edit(Student $student, StudentDocument $document): View
|
|
{
|
|
abort_unless($document->student_id === $student->id, 404);
|
|
|
|
return view('student-documents.edit', [
|
|
'student' => $student,
|
|
'document' => $document,
|
|
'documentTypes' => DocumentType::where('is_active', true)->orderBy('name')->get(),
|
|
'verificationStatuses' => $this->verificationStatuses(),
|
|
'defaultMaxFileSizeKb' => 5120,
|
|
]);
|
|
}
|
|
|
|
public function update(Request $request, Student $student, StudentDocument $document): RedirectResponse
|
|
{
|
|
abort_unless($document->student_id === $student->id, 404);
|
|
|
|
$data = $this->validateDocument($request, false);
|
|
|
|
if ($request->hasFile('document_file')) {
|
|
if ($document->file_path && Storage::disk('public')->exists($document->file_path)) {
|
|
Storage::disk('public')->delete($document->file_path);
|
|
}
|
|
|
|
$uploadedFile = $request->file('document_file');
|
|
$path = $uploadedFile->store("student-documents/{$student->id}", 'public');
|
|
|
|
$document->file_name = $uploadedFile->getClientOriginalName();
|
|
$document->file_path = $path;
|
|
$document->file_extension = $uploadedFile->getClientOriginalExtension();
|
|
$document->mime_type = $uploadedFile->getMimeType();
|
|
$document->file_size = $uploadedFile->getSize();
|
|
}
|
|
|
|
$document->fill([
|
|
'document_type_id' => $data['document_type_id'],
|
|
'document_number' => $data['document_number'] ?? null,
|
|
'issued_date' => $data['issued_date'] ?? null,
|
|
'expired_date' => $data['expired_date'] ?? null,
|
|
'description' => $data['description'] ?? null,
|
|
'verification_status' => $data['verification_status'],
|
|
'verification_note' => $data['verification_note'] ?? null,
|
|
]);
|
|
|
|
if (in_array($data['verification_status'], ['valid', 'invalid', 'revision'])) {
|
|
$document->verified_by = auth()->id();
|
|
$document->verified_at = now();
|
|
} else {
|
|
$document->verified_by = null;
|
|
$document->verified_at = null;
|
|
}
|
|
|
|
$document->save();
|
|
|
|
Alert::success('Berhasil', 'Dokumen siswa berhasil diperbarui.');
|
|
|
|
return redirect()->route('students.show', $student)->with('success', 'Dokumen siswa berhasil diperbarui.');
|
|
}
|
|
|
|
public function destroy(Student $student, StudentDocument $document): RedirectResponse
|
|
{
|
|
$this->ensureAdmin();
|
|
abort_unless($document->student_id === $student->id, 404);
|
|
|
|
if ($document->file_path && Storage::disk('public')->exists($document->file_path)) {
|
|
Storage::disk('public')->delete($document->file_path);
|
|
}
|
|
|
|
$document->delete();
|
|
|
|
Alert::success('Berhasil', 'Dokumen siswa berhasil dihapus.');
|
|
|
|
return redirect()->route('students.show', $student)->with('success', 'Dokumen siswa berhasil dihapus.');
|
|
}
|
|
|
|
public function preview(StudentDocument $document): Response|View
|
|
{
|
|
abort_unless($document->file_path && Storage::disk('public')->exists($document->file_path), 404);
|
|
|
|
$mimeType = $document->mime_type ?: Storage::disk('public')->mimeType($document->file_path);
|
|
$url = asset('storage/' . $document->file_path);
|
|
$isImage = str_starts_with((string) $mimeType, 'image/');
|
|
$isPdf = $mimeType === 'application/pdf';
|
|
|
|
return view('student-documents.preview', [
|
|
'document' => $document,
|
|
'fileUrl' => $url,
|
|
'mimeType' => $mimeType,
|
|
'isImage' => $isImage,
|
|
'isPdf' => $isPdf,
|
|
]);
|
|
}
|
|
|
|
public function quickVerify(Request $request, Student $student, StudentDocument $document): RedirectResponse
|
|
{
|
|
$this->ensureAdmin();
|
|
abort_unless($document->student_id === $student->id, 404);
|
|
|
|
$data = $request->validate([
|
|
'verification_status' => ['required', Rule::in(['valid', 'revision', 'invalid'])],
|
|
'verification_note' => ['nullable', 'string', 'max:1000'],
|
|
]);
|
|
|
|
$document->update([
|
|
'verification_status' => $data['verification_status'],
|
|
'verification_note' => $data['verification_note'] ?? null,
|
|
'verified_by' => auth()->id(),
|
|
'verified_at' => now(),
|
|
]);
|
|
|
|
Alert::success('Berhasil', 'Status verifikasi dokumen berhasil diperbarui.');
|
|
|
|
return redirect()->route('students.show', $student)->with('success', 'Status verifikasi dokumen berhasil diperbarui.');
|
|
}
|
|
|
|
public function download(StudentDocument $document): StreamedResponse
|
|
{
|
|
abort_unless($document->file_path && Storage::disk('public')->exists($document->file_path), 404);
|
|
|
|
return Storage::disk('public')->download($document->file_path, $document->file_name);
|
|
}
|
|
|
|
public function publicPreview(StudentDocument $document): Response|View
|
|
{
|
|
abort_unless($document->file_path && Storage::disk('public')->exists($document->file_path), 404);
|
|
|
|
$mimeType = $document->mime_type ?: Storage::disk('public')->mimeType($document->file_path);
|
|
$url = asset('storage/' . $document->file_path);
|
|
$isImage = str_starts_with((string) $mimeType, 'image/');
|
|
$isPdf = $mimeType === 'application/pdf';
|
|
|
|
return view('public.document-preview', [
|
|
'document' => $document,
|
|
'fileUrl' => $url,
|
|
'mimeType' => $mimeType,
|
|
'isImage' => $isImage,
|
|
'isPdf' => $isPdf,
|
|
]);
|
|
}
|
|
|
|
public function publicDownload(StudentDocument $document): StreamedResponse
|
|
{
|
|
return $this->download($document);
|
|
}
|
|
|
|
protected function validateDocument(Request $request, bool $fileRequired = true): array
|
|
{
|
|
$documentType = DocumentType::findOrFail($request->input('document_type_id'));
|
|
$maxFileSizeKb = $documentType->max_file_size_kb ?: 5120;
|
|
$allowedExtensions = $documentType->allowed_extensions
|
|
? collect(explode(',', $documentType->allowed_extensions))->map(fn ($item) => trim($item))->filter()->implode(',')
|
|
: 'jpg,jpeg,png,pdf,webp';
|
|
|
|
return $request->validate([
|
|
'document_type_id' => ['required', 'exists:document_types,id'],
|
|
'document_number' => ['nullable', 'string', 'max:100'],
|
|
'issued_date' => ['nullable', 'date'],
|
|
'expired_date' => ['nullable', 'date', 'after_or_equal:issued_date'],
|
|
'description' => ['nullable', 'string'],
|
|
'verification_status' => ['required', Rule::in(array_keys($this->verificationStatuses()))],
|
|
'verification_note' => ['nullable', 'string', 'max:1000'],
|
|
'document_file' => [$fileRequired ? 'required' : 'nullable', 'file', 'mimes:' . $allowedExtensions, 'max:' . $maxFileSizeKb],
|
|
], [
|
|
'document_file.max' => 'Ukuran file melebihi batas maksimal untuk jenis dokumen ini (' . number_format($maxFileSizeKb / 1024, 2) . ' MB).',
|
|
'document_file.mimes' => 'Format file tidak sesuai. Format yang diizinkan untuk jenis dokumen ini: ' . strtoupper(str_replace(',', ', ', $allowedExtensions)) . '.',
|
|
]);
|
|
}
|
|
|
|
protected function verificationStatuses(): array
|
|
{
|
|
return [
|
|
'pending' => 'Pending',
|
|
'valid' => 'Valid',
|
|
'invalid' => 'Invalid',
|
|
'revision' => 'Perlu Revisi',
|
|
];
|
|
}
|
|
|
|
protected function ensureAdmin(): void
|
|
{
|
|
abort_unless(auth()->user()?->isAdmin(), 403, 'Aksi ini hanya boleh dilakukan administrator.');
|
|
}
|
|
}
|