Initial commit: sync from server - all domains and code
This commit is contained in:
294
www/fahrschultermin.de/app/Repositories/StudentRepository.php
Normal file
294
www/fahrschultermin.de/app/Repositories/StudentRepository.php
Normal file
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
final class StudentRepository extends BaseRepository
|
||||
{
|
||||
public function allForTenant(int $tenantId): array
|
||||
{
|
||||
$statement = $this->db->prepare(
|
||||
'SELECT s.*, t.code AS template_code, t.name AS template_name, t.is_combination AS template_is_combination
|
||||
FROM students s
|
||||
JOIN license_class_templates t ON t.id = s.template_id
|
||||
WHERE s.tenant_id = :tenant_id
|
||||
ORDER BY s.last_name, s.first_name'
|
||||
);
|
||||
$statement->execute(['tenant_id' => $tenantId]);
|
||||
|
||||
return $statement->fetchAll();
|
||||
}
|
||||
|
||||
public function find(int $tenantId, int $id): ?array
|
||||
{
|
||||
$statement = $this->db->prepare(
|
||||
'SELECT s.*, t.code AS template_code, t.name AS template_name, t.is_combination AS template_is_combination
|
||||
FROM students s
|
||||
JOIN license_class_templates t ON t.id = s.template_id
|
||||
WHERE s.tenant_id = :tenant_id AND s.id = :id'
|
||||
);
|
||||
$statement->execute(['tenant_id' => $tenantId, 'id' => $id]);
|
||||
$student = $statement->fetch() ?: null;
|
||||
|
||||
if ($student !== null) {
|
||||
$student['requirements'] = $this->requirements($id);
|
||||
}
|
||||
|
||||
return $student;
|
||||
}
|
||||
|
||||
public function create(int $tenantId, array $data): array
|
||||
{
|
||||
$timestamp = $this->now();
|
||||
$statement = $this->db->prepare(
|
||||
'INSERT INTO students
|
||||
(tenant_id, template_id, first_name, last_name, phone, email, needs_glasses, notes, status, created_at, updated_at)
|
||||
VALUES (:tenant_id, :template_id, :first_name, :last_name, :phone, :email, :needs_glasses, :notes, :status, :created_at, :updated_at)'
|
||||
);
|
||||
$statement->execute([
|
||||
'tenant_id' => $tenantId,
|
||||
'template_id' => $data['template_id'],
|
||||
'first_name' => $data['first_name'],
|
||||
'last_name' => $data['last_name'],
|
||||
'phone' => $data['phone'] ?? '',
|
||||
'email' => $data['email'] ?? '',
|
||||
'needs_glasses' => !empty($data['needs_glasses']) ? 1 : 0,
|
||||
'notes' => $data['notes'] ?? '',
|
||||
'status' => $data['status'] ?? 'active',
|
||||
'created_at' => $timestamp,
|
||||
'updated_at' => $timestamp,
|
||||
]);
|
||||
|
||||
$studentId = (int) $this->db->lastInsertId();
|
||||
$this->snapshotRequirements($studentId, (int) $data['template_id']);
|
||||
|
||||
return $this->find($tenantId, $studentId);
|
||||
}
|
||||
|
||||
public function update(int $tenantId, int $id, array $data): ?array
|
||||
{
|
||||
$statement = $this->db->prepare(
|
||||
'UPDATE students
|
||||
SET template_id = :template_id, first_name = :first_name, last_name = :last_name, phone = :phone, email = :email, needs_glasses = :needs_glasses,
|
||||
notes = :notes, status = :status, updated_at = :updated_at
|
||||
WHERE tenant_id = :tenant_id AND id = :id'
|
||||
);
|
||||
$statement->execute([
|
||||
'tenant_id' => $tenantId,
|
||||
'id' => $id,
|
||||
'template_id' => $data['template_id'],
|
||||
'first_name' => $data['first_name'],
|
||||
'last_name' => $data['last_name'],
|
||||
'phone' => $data['phone'] ?? '',
|
||||
'email' => $data['email'] ?? '',
|
||||
'needs_glasses' => !empty($data['needs_glasses']) ? 1 : 0,
|
||||
'notes' => $data['notes'] ?? '',
|
||||
'status' => $data['status'] ?? 'active',
|
||||
'updated_at' => $this->now(),
|
||||
]);
|
||||
|
||||
return $this->find($tenantId, $id);
|
||||
}
|
||||
|
||||
public function delete(int $tenantId, int $id): bool
|
||||
{
|
||||
$student = $this->find($tenantId, $id);
|
||||
if ($student === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->beginTransaction();
|
||||
try {
|
||||
$updateAppointments = $this->db->prepare(
|
||||
'UPDATE appointments SET student_id = NULL, updated_at = :updated_at WHERE tenant_id = :tenant_id AND student_id = :student_id'
|
||||
);
|
||||
$updateAppointments->execute([
|
||||
'tenant_id' => $tenantId,
|
||||
'student_id' => $id,
|
||||
'updated_at' => $this->now(),
|
||||
]);
|
||||
|
||||
$deleteSnapshots = $this->db->prepare('DELETE FROM student_requirement_snapshots WHERE student_id = :student_id');
|
||||
$deleteSnapshots->execute(['student_id' => $id]);
|
||||
|
||||
$deleteStudent = $this->db->prepare('DELETE FROM students WHERE tenant_id = :tenant_id AND id = :id');
|
||||
$deleteStudent->execute(['tenant_id' => $tenantId, 'id' => $id]);
|
||||
|
||||
$this->db->commit();
|
||||
return true;
|
||||
} catch (\Throwable $exception) {
|
||||
$this->db->rollBack();
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
public function requirements(int $studentId): array
|
||||
{
|
||||
$this->syncRequirementSnapshots($studentId);
|
||||
|
||||
$statement = $this->db->prepare(
|
||||
'SELECT * FROM student_requirement_snapshots WHERE student_id = :student_id ORDER BY sort_order, phase_label, label'
|
||||
);
|
||||
$statement->execute(['student_id' => $studentId]);
|
||||
|
||||
return $statement->fetchAll();
|
||||
}
|
||||
|
||||
public function updatePriorCompletedUnits(int $tenantId, int $studentId, array $rows): ?array
|
||||
{
|
||||
$student = $this->find($tenantId, $studentId);
|
||||
if ($student === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$statement = $this->db->prepare(
|
||||
'UPDATE student_requirement_snapshots
|
||||
SET prior_completed_units = :prior_completed_units
|
||||
WHERE student_id = :student_id AND requirement_key = :requirement_key'
|
||||
);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$statement->execute([
|
||||
'student_id' => $studentId,
|
||||
'requirement_key' => (string) ($row['requirement_key'] ?? ''),
|
||||
'prior_completed_units' => max(0, (int) ($row['prior_completed_units'] ?? 0)),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->find($tenantId, $studentId);
|
||||
}
|
||||
|
||||
private function snapshotRequirements(int $studentId, int $templateId): void
|
||||
{
|
||||
$this->syncRequirementSnapshots($studentId, $templateId);
|
||||
}
|
||||
|
||||
private function syncRequirementSnapshots(int $studentId, ?int $templateId = null): void
|
||||
{
|
||||
if ($templateId === null) {
|
||||
$student = $this->db->prepare('SELECT template_id FROM students WHERE id = :student_id');
|
||||
$student->execute(['student_id' => $studentId]);
|
||||
$templateId = (int) ($student->fetch()['template_id'] ?? 0);
|
||||
}
|
||||
|
||||
if ($templateId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$requirements = $this->db->prepare(
|
||||
'SELECT requirement_key, label, required_units, sort_order, phase_label
|
||||
FROM license_template_requirements WHERE template_id = :template_id'
|
||||
);
|
||||
$requirements->execute(['template_id' => $templateId]);
|
||||
$rows = [];
|
||||
|
||||
foreach ($requirements->fetchAll() as $row) {
|
||||
$rows[$row['requirement_key']] = $row;
|
||||
}
|
||||
$hasPhasedRequirements = array_reduce(
|
||||
$rows,
|
||||
static fn (bool $carry, array $row): bool => $carry || trim((string) ($row['phase_label'] ?? '')) !== '',
|
||||
false
|
||||
);
|
||||
|
||||
$lessonTypes = $this->db->prepare(
|
||||
'SELECT COALESCE(NULLIF(TRIM(lt.requirement_key), \'\'), \'lesson_type_\' || lt.id) AS requirement_key,
|
||||
lt.name AS label,
|
||||
lt.sort_order
|
||||
FROM lesson_types lt
|
||||
JOIN lesson_type_template_visibility vis ON vis.lesson_type_id = lt.id
|
||||
WHERE vis.template_id = :template_id
|
||||
AND lt.category = \'student\'
|
||||
AND lt.is_counted = 1
|
||||
ORDER BY lt.sort_order, lt.name'
|
||||
);
|
||||
$lessonTypes->execute(['template_id' => $templateId]);
|
||||
|
||||
foreach ($lessonTypes->fetchAll() as $row) {
|
||||
if (isset($rows[$row['requirement_key']])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($hasPhasedRequirements) {
|
||||
$hasMatchingPhasedRequirement = false;
|
||||
foreach ($rows as $existingRow) {
|
||||
$phaseLabel = trim((string) ($existingRow['phase_label'] ?? ''));
|
||||
if ($phaseLabel === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_ends_with((string) $existingRow['requirement_key'], '::' . $row['requirement_key'])) {
|
||||
$hasMatchingPhasedRequirement = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasMatchingPhasedRequirement) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$rows[$row['requirement_key']] = [
|
||||
'requirement_key' => $row['requirement_key'],
|
||||
'label' => $row['label'],
|
||||
'required_units' => 0,
|
||||
'sort_order' => $row['sort_order'],
|
||||
];
|
||||
}
|
||||
|
||||
$insert = $this->db->prepare(
|
||||
'INSERT INTO student_requirement_snapshots
|
||||
(student_id, requirement_key, label, required_units, prior_completed_units, sort_order, phase_label)
|
||||
SELECT :student_id, :requirement_key, :label, :required_units, 0, :sort_order, :phase_label
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM student_requirement_snapshots
|
||||
WHERE student_id = :student_id AND requirement_key = :requirement_key
|
||||
)'
|
||||
);
|
||||
$update = $this->db->prepare(
|
||||
'UPDATE student_requirement_snapshots
|
||||
SET label = :label,
|
||||
required_units = :required_units,
|
||||
sort_order = :sort_order,
|
||||
phase_label = :phase_label
|
||||
WHERE student_id = :student_id AND requirement_key = :requirement_key'
|
||||
);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$insert->execute([
|
||||
'student_id' => $studentId,
|
||||
'requirement_key' => $row['requirement_key'],
|
||||
'label' => $row['label'],
|
||||
'required_units' => $row['required_units'],
|
||||
'sort_order' => $row['sort_order'],
|
||||
'phase_label' => $row['phase_label'] ?? '',
|
||||
]);
|
||||
$update->execute([
|
||||
'student_id' => $studentId,
|
||||
'requirement_key' => $row['requirement_key'],
|
||||
'label' => $row['label'],
|
||||
'required_units' => $row['required_units'],
|
||||
'sort_order' => $row['sort_order'],
|
||||
'phase_label' => $row['phase_label'] ?? '',
|
||||
]);
|
||||
}
|
||||
|
||||
if ($rows !== []) {
|
||||
$placeholders = implode(', ', array_fill(0, count($rows), '?'));
|
||||
$delete = $this->db->prepare(
|
||||
"DELETE FROM student_requirement_snapshots
|
||||
WHERE student_id = ?
|
||||
AND requirement_key NOT IN ($placeholders)
|
||||
AND prior_completed_units = 0
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM appointment_units au
|
||||
JOIN appointments a ON a.id = au.appointment_id
|
||||
WHERE a.student_id = student_requirement_snapshots.student_id
|
||||
AND au.requirement_key = student_requirement_snapshots.requirement_key
|
||||
)"
|
||||
);
|
||||
$delete->execute(array_merge([$studentId], array_keys($rows)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user