You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

206 line
7.1KB

  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Controllers;
  4. use App\Models\Employee;
  5. use App\Repositories\EmployeeRepository;
  6. use App\ViewModels\EmployeeFormViewModel;
  7. use Core\Controller;
  8. use Core\Request;
  9. use Core\Response;
  10. use Core\Validator;
  11. class EmployeeController extends Controller
  12. {
  13. private ?EmployeeRepository $employees = null;
  14. public function index(Request $request)
  15. {
  16. $viewModel = $this->buildViewModel((string) $request->input('search', ''));
  17. $viewModel->saved = $request->input('saved') === '1';
  18. return $this->view('employees.create', [
  19. 'model' => $viewModel,
  20. 'pageTitle' => $viewModel->title,
  21. ]);
  22. }
  23. public function store(Request $request)
  24. {
  25. if ($guard = $this->verifyCsrf($request)) {
  26. return $guard;
  27. }
  28. $form = $this->sanitizeFormData($request);
  29. $errors = $this->validateForm($form);
  30. if (empty($errors) && $this->employees()->findByEmail($form['email']) !== null) {
  31. $errors['email'][] = 'That email address is already in use.';
  32. }
  33. if (!empty($errors)) {
  34. $viewModel = $this->buildViewModel();
  35. $viewModel->form = $form;
  36. $viewModel->errors = $errors;
  37. if ($this->isHtmxRequest($request)) {
  38. return $this->fragment('employees.partials.form', [
  39. 'model' => $viewModel,
  40. ]);
  41. }
  42. return $this->view('employees.create', [
  43. 'model' => $viewModel,
  44. 'pageTitle' => $viewModel->title,
  45. ]);
  46. }
  47. $employee = new Employee();
  48. $employee->firstName = $form['first_name'];
  49. $employee->lastName = $form['last_name'];
  50. $employee->email = $form['email'];
  51. $employee->department = $form['department'];
  52. $employee->jobTitle = $form['job_title'];
  53. $employee->startDate = $form['start_date'];
  54. $this->employees()->create($employee);
  55. if ($this->isHtmxRequest($request)) {
  56. $viewModel = $this->buildViewModel();
  57. $viewModel->saved = true;
  58. return $this->fragment('employees.partials.form', [
  59. 'model' => $viewModel,
  60. ], 200, [
  61. 'HX-Trigger' => json_encode(['employees-changed' => true]),
  62. ]);
  63. }
  64. return $this->redirect('/employees?saved=1');
  65. }
  66. public function create()
  67. {
  68. return $this->redirect('/employees');
  69. }
  70. public function summary(Request $request)
  71. {
  72. $viewModel = $this->buildViewModel((string) $request->input('search', ''));
  73. return $this->fragment('employees.partials.summary', [
  74. 'model' => $viewModel,
  75. ]);
  76. }
  77. public function data(Request $request)
  78. {
  79. $search = trim((string) $request->input('search', ''));
  80. $rows = $this->employees()->search($search);
  81. $data = array_map(
  82. static function (array $row): array {
  83. return [
  84. 'id' => (int) $row['id'],
  85. 'full_name' => trim($row['first_name'] . ' ' . $row['last_name']),
  86. 'first_name' => (string) $row['first_name'],
  87. 'last_name' => (string) $row['last_name'],
  88. 'email' => (string) $row['email'],
  89. 'department' => (string) $row['department'],
  90. 'job_title' => (string) $row['job_title'],
  91. 'start_date' => (string) $row['start_date'],
  92. 'created_at' => (string) $row['created_at'],
  93. ];
  94. },
  95. $rows
  96. );
  97. return $this->json($data);
  98. }
  99. /**
  100. * @return array<string, string>
  101. */
  102. private function sanitizeFormData(Request $request): array
  103. {
  104. return [
  105. 'first_name' => trim((string) $request->input('first_name', '')),
  106. 'last_name' => trim((string) $request->input('last_name', '')),
  107. 'email' => trim((string) $request->input('email', '')),
  108. 'department' => trim((string) $request->input('department', '')),
  109. 'job_title' => trim((string) $request->input('job_title', '')),
  110. 'start_date' => trim((string) $request->input('start_date', '')),
  111. ];
  112. }
  113. /**
  114. * @param array<string, string> $form
  115. * @return array<string, list<string>>
  116. */
  117. private function validateForm(array $form): array
  118. {
  119. $validator = new Validator();
  120. $validator
  121. ->required('first_name', $form['first_name'], 'First name is required.')
  122. ->maxLength('first_name', $form['first_name'], 100, 'First name must be 100 characters or fewer.')
  123. ->required('last_name', $form['last_name'], 'Last name is required.')
  124. ->maxLength('last_name', $form['last_name'], 100, 'Last name must be 100 characters or fewer.')
  125. ->required('email', $form['email'], 'Email is required.')
  126. ->maxLength('email', $form['email'], 255, 'Email must be 255 characters or fewer.')
  127. ->email('email', $form['email'], 'Enter a valid email address.')
  128. ->required('department', $form['department'], 'Department is required.')
  129. ->maxLength('department', $form['department'], 100, 'Department must be 100 characters or fewer.')
  130. ->required('job_title', $form['job_title'], 'Job title is required.')
  131. ->maxLength('job_title', $form['job_title'], 150, 'Job title must be 150 characters or fewer.')
  132. ->required('start_date', $form['start_date'], 'Start date is required.')
  133. ->date('start_date', $form['start_date'], 'Y-m-d', 'Enter a valid start date.');
  134. return $validator->errors();
  135. }
  136. private function verifyCsrf(Request $request): ?Response
  137. {
  138. if (!verify_csrf_token((string) $request->input('_token', ''))) {
  139. return new Response('Your session has expired. Please go back and try again.', 419);
  140. }
  141. return null;
  142. }
  143. private function employees(): EmployeeRepository
  144. {
  145. if ($this->employees === null) {
  146. $this->employees = new EmployeeRepository(database());
  147. }
  148. return $this->employees;
  149. }
  150. private function isHtmxRequest(Request $request): bool
  151. {
  152. return strtolower((string) $request->server('HTTP_HX_REQUEST', '')) === 'true';
  153. }
  154. private function buildViewModel(string $search = ''): EmployeeFormViewModel
  155. {
  156. $viewModel = new EmployeeFormViewModel();
  157. $viewModel->search = trim($search);
  158. $employees = $this->employees()->search($viewModel->search);
  159. $newestEmployee = $this->employees()->newestMatching($viewModel->search);
  160. $viewModel->employees = array_slice($employees, 0, 5);
  161. $viewModel->newestEmployee = $newestEmployee;
  162. $viewModel->summary = [
  163. 'employee_count' => $this->employees()->countMatching($viewModel->search),
  164. 'department_count' => $this->employees()->countDepartments($viewModel->search),
  165. 'latest_start_date' => $newestEmployee['start_date'] ?? 'N/A',
  166. ];
  167. return $viewModel;
  168. }
  169. }

Powered by TurnKey Linux.