Files
alrahma_sunday_school/app/Controllers/View/CertificateController.php
T
2026-05-17 17:04:47 -04:00

297 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Controllers\View;
use App\Controllers\BaseController;
use App\Models\ClassSectionModel;
use App\Models\ConfigurationModel;
class CertificateController extends BaseController
{
protected $classSectionModel;
protected $configModel;
protected $schoolYear;
public function __construct()
{
$this->classSectionModel = new ClassSectionModel();
$this->configModel = new ConfigurationModel();
$this->schoolYear = $this->configModel->getConfig('school_year');
}
public function index()
{
$db = \Config\Database::connect();
$classSectionId = $this->request->getGet('class_section_id');
$schoolYear = $this->request->getGet('school_year') ?? $this->schoolYear;
$classSections = $db->table('classSection cs')
->select('cs.class_section_id, cs.class_section_name')
->join('student_class sc', 'sc.class_section_id = cs.class_section_id')
->join('students s', 's.id = sc.student_id')
->where('s.is_active', 1)
->where('sc.school_year', $schoolYear)
->groupBy('cs.class_section_id, cs.class_section_name')
->having('COUNT(s.id) >', 1)
->orderBy('cs.class_section_name', 'ASC')
->get()->getResultArray();
$students = [];
if ($classSectionId) {
$students = $db->table('student_class sc')
->select('s.id, s.firstname, s.lastname, cs.class_section_name AS grade')
->join('students s', 's.id = sc.student_id')
->join('classSection cs', 'cs.class_section_id = sc.class_section_id')
->where('sc.class_section_id', (int) $classSectionId)
->where('s.is_active', 1)
->where('sc.school_year', $schoolYear)
->orderBy('s.lastname', 'ASC')
->orderBy('s.firstname', 'ASC')
->get()->getResultArray();
}
return view('admin/certificates/index', [
'classSections' => $classSections,
'students' => $students,
'selectedClassId' => $classSectionId,
'schoolYear' => $schoolYear,
'certDate' => date('m/d/Y'),
]);
}
public function generate()
{
$studentIds = $this->request->getPost('student_ids') ?? [];
$certDate = trim($this->request->getPost('cert_date') ?? date('m/d/Y'));
$classSectionId = $this->request->getPost('class_section_id');
if (empty($studentIds)) {
return redirect()->to('administrator/certificates')->with('error', 'Please select at least one student.');
}
// Sanitize inputs
$studentIds = array_filter(array_map('intval', $studentIds));
$certDate = preg_replace('/[^0-9\/\-]/', '', $certDate);
if (empty($studentIds)) {
return redirect()->to('administrator/certificates')->with('error', 'Invalid student selection.');
}
$db = \Config\Database::connect();
$students = [];
foreach ($studentIds as $id) {
if ($classSectionId) {
$row = $db->table('student_class sc')
->select('s.id, s.firstname, s.lastname, cs.class_section_name AS grade')
->join('students s', 's.id = sc.student_id')
->join('classSection cs', 'cs.class_section_id = sc.class_section_id')
->where('sc.class_section_id', (int) $classSectionId)
->where('s.id', $id)
->get()->getRowArray();
} else {
$row = null;
}
if (!$row) {
$s = $db->table('students')
->select('id, firstname, lastname, registration_grade AS grade')
->where('id', $id)
->get()->getRowArray();
$row = $s ?: null;
}
if ($row) {
$students[] = $row;
}
}
if (empty($students)) {
return redirect()->to('administrator/certificates')->with('error', 'No valid students found.');
}
$pdfData = $this->buildPdf($students, $certDate);
$filename = 'Certificates_' . date('Ymd_His') . '.pdf';
return $this->response
->setHeader('Content-Type', 'application/pdf')
->setHeader('Content-Disposition', 'inline; filename="' . $filename . '"')
->setBody($pdfData);
}
// ─── PDF generation ────────────────────────────────────────────────────────
private function buildPdf(array $students, string $certDate): string
{
$fontDir = FCPATH . 'assets' . DIRECTORY_SEPARATOR . 'certificates' . DIRECTORY_SEPARATOR . 'fonts' . DIRECTORY_SEPARATOR;
$imgDir = FCPATH . 'assets' . DIRECTORY_SEPARATOR . 'certificates' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR;
// Convert TTF → TCPDF format on first run (cached in TCPDF's own fonts dir).
// K_PATH_FONTS must stay pointing at the TCPDF vendor fonts directory so that
// built-in fonts (helvetica, times, …) remain findable by SetFont().
$edwardianFont = \TCPDF_FONTS::addTTFfont($fontDir . 'Edwardian Script ITC Regular.ttf', 'TrueTypeUnicode', '', 32);
$garamondBold = \TCPDF_FONTS::addTTFfont($fontDir . 'Garamond Bold.ttf', 'TrueTypeUnicode', '', 32);
$ebGaramond = \TCPDF_FONTS::addTTFfont($fontDir . 'EBGaramond-Regular.ttf', 'TrueTypeUnicode', '', 32);
// A4 landscape in points: 841.89 × 595.28
$pdf = new \TCPDF('L', 'pt', 'A4', true, 'UTF-8', false);
$pdf->SetCreator('Al Rahma Sunday School');
$pdf->SetTitle('Student Certificates');
$pdf->SetMargins(0, 0, 0, true);
$pdf->SetAutoPageBreak(false, 0);
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$pdf->SetHeaderMargin(0);
$pdf->SetFooterMargin(0);
$W = $pdf->getPageWidth(); // 841.89 pt
$H = $pdf->getPageHeight(); // 595.28 pt
foreach ($students as $student) {
$pdf->AddPage();
$name = $student['firstname'] . ' ' . $student['lastname'];
$grade = $this->formatGrade($student['grade'] ?? '');
$this->drawCertificate($pdf, $W, $H, $name, $grade, $certDate, $imgDir, $edwardianFont, $garamondBold, $ebGaramond);
}
return $pdf->Output('', 'S');
}
private function formatGrade(string $raw): string
{
$clean = trim($raw);
$lower = strtolower($clean);
// Numeric grades 1-9 → "Grade N"
if (preg_match('/^\d+$/', $clean) && (int)$clean >= 1 && (int)$clean <= 9) {
return 'Grade ' . $clean;
}
// Youth variants
if ($lower === 'youth') {
return 'Youth';
}
// KG → Kindergarten
if ($lower === 'kg') {
return 'Kindergarten';
}
return $clean;
}
/**
* Draws one certificate page. Coordinate mapping from the original C# iTextSharp code:
* iTextSharp uses (x, y) with origin at bottom-left, y increases upward.
* TCPDF uses (x, y) with origin at top-left, y increases downward.
* Conversion: y_tcpdf = H - y_iTextSharp
*/
private function drawCertificate(
\TCPDF $pdf,
float $W,
float $H,
string $name,
string $grade,
string $certDate,
string $imgDir,
string $edwardianFont,
string $garamondBold,
string $ebGaramond
): void {
// ── Title image: iTS SetAbsolutePosition(126, 450), ScaleToFit(600, 300)
// Bottom at y=450 from page bottom → top at H-450 ≈ 145. Place at top of page.
$pdf->Image($imgDir . 'title.png', 126, 0, 600);
// ── Background image: iTS position(280, 79), ScaleToFit(2500, 340)
// background.png is 336×393 px → ScaleToFit gives ≈291×340 pt.
// Top in TCPDF: H-79-340 = ~176
$pdf->Image($imgDir . 'background.png', 280, 176, 291, 340);
// ── Signature image: iTS position(140, 106), ScaleToFit(90, 90)
// Top in TCPDF: H-106-90 = ~399
$pdf->Image($imgDir . 'signature.png', 140, 399, 90, 90);
// ── "Presented to:" — iTS ALIGN_CENTER baseline y=420 → TCPDF y≈155
$pdf->SetFont('times', 'B', 24);
$pdf->SetTextColor(0, 0, 0);
$pdf->SetXY(0, 151);
$pdf->Cell($W, 24, 'Presented to:', 0, 0, 'C');
// ── Student name — iTS baseline y=334, 38 pt Edwardian Script → TCPDF y≈219
// Bold effect: fill the glyph AND add a thin stroke around it (Edwardian has no bold variant).
$pdf->SetFont($edwardianFont, '', 38);
$pdf->SetDrawColor(0, 0, 0);
$pdf->setTextRenderingMode(0.4, true, false);
$pdf->SetXY(0, 219);
$pdf->Cell($W, 38, $name, 0, 0, 'C');
$pdf->setTextRenderingMode(0, true, false);
// ── Line under name — iTS ALIGN_RIGHT at x=600, y=332 → TCPDF y≈243
$pdf->SetFont('times', '', 20);
$pdf->SetXY(0, 243);
$pdf->Cell(600, 20, '___________________________________', 0, 0, 'R');
// ── Description — iTS ALIGN_CENTER baseline y=302 → TCPDF y≈273
$pdf->SetFont($ebGaramond, '', 20);
$pdf->SetXY(0, 273);
$pdf->Cell($W, 20, 'for successfully completing the requirements of', 0, 0, 'C');
// ── Grade — iTS ALIGN_CENTER baseline y=265 → TCPDF y≈310
$pdf->SetFont($garamondBold, '', 20);
$pdf->SetXY(0, 310);
$pdf->Cell($W, 20, $grade, 0, 0, 'C');
// ── "at" — iTS ALIGN_CENTER baseline y=232 → TCPDF y≈343
$pdf->SetFont('times', '', 20);
$pdf->SetXY(0, 343);
$pdf->Cell($W, 20, 'at', 0, 0, 'C');
// ── School name — iTS ALIGN_CENTER baseline y=200 → TCPDF y≈375
$pdf->SetFont($garamondBold, '', 20);
$pdf->SetXY(0, 375);
$pdf->Cell($W, 20, 'Al Rahma Sunday School', 0, 0, 'C');
// ── Certificate date — iTS DrawGradientText at (598, 116), 26 pt Edwardian
// TCPDF y ≈ H-116-26 = ~453
$this->drawGradientText($pdf, $edwardianFont, 26, $certDate, 598, 453);
// ── Date underline — iTS ALIGN_RIGHT at x=742, y=110 → TCPDF y≈465
$pdf->SetFont('times', '', 20);
$pdf->SetXY(0, 463);
$pdf->Cell(742, 20, '_________________', 0, 0, 'R');
// ── "Date" label — iTS at (690, 78) → TCPDF y≈497, x≈650
$pdf->SetXY(650, 492);
$pdf->Cell(80, 20, 'Date', 0, 0, 'C');
// ── Signature underline — iTS ALIGN_LEFT at x=106, y=110 → TCPDF y≈465
$pdf->SetXY(106, 463);
$pdf->Cell(200, 20, '_________________', 0, 0, 'L');
// ── "Signature" label — iTS at (190, 78) → TCPDF y≈497
$pdf->SetXY(106, 492);
$pdf->Cell(168, 20, 'Signature', 0, 0, 'C');
}
/**
* Mimics the C# DrawGradientText — shadow passes give depth, final pass is solid black.
*/
private function drawGradientText(\TCPDF $pdf, string $fontName, float $fontSize, string $text, float $x, float $y): void
{
$pdf->SetFont($fontName, '', $fontSize);
// Shadow passes (progressively more opaque, slightly offset)
for ($i = 0; $i < 6; $i++) {
$alpha = ($i + 1) * 25 / 255; // 0.098 → 0.588
$pdf->setAlpha($alpha);
$pdf->SetTextColor(0, 0, 0);
$pdf->Text($x + $i / 5, $y + $i / 5, $text);
}
// Main solid pass
$pdf->setAlpha(1);
$pdf->SetTextColor(0, 0, 0);
$pdf->Text($x, $y, $text);
}
}