選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

209 行
7.2KB

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

Powered by TurnKey Linux.