|
- <?php
-
- declare(strict_types=1);
-
- namespace App\Controllers;
-
- use App\Models\Employee;
- use App\Repositories\EmployeeRepository;
- use App\ViewModels\EmployeeFormViewModel;
- use Core\Controller;
- use Core\Request;
- use Core\Validator;
-
- class EmployeeController extends Controller
- {
- public function index()
- {
- $request = Request::capture();
- $viewModel = $this->buildViewModel((string) $request->input('search', ''));
- $viewModel->saved = $request->input('saved') === '1';
-
- return $this->view('employees.create', [
- 'model' => $viewModel,
- 'pageTitle' => $viewModel->title,
- ]);
- }
-
- public function store()
- {
- $request = Request::capture();
- $form = $this->sanitizeFormData($request);
- $errors = $this->validateForm($form, $request);
-
- if (empty($errors) && $this->employees()->findByEmail($form['email']) !== null) {
- $errors['email'][] = 'That email address is already in use.';
- }
-
- if (!empty($errors)) {
- $viewModel = $this->buildViewModel();
- $viewModel->form = $form;
- $viewModel->errors = $errors;
-
- if ($this->isHtmxRequest($request)) {
- return $this->fragment('employees.partials.form', [
- 'model' => $viewModel,
- ]);
- }
-
- return $this->view('employees.create', [
- 'model' => $viewModel,
- 'pageTitle' => $viewModel->title,
- ]);
- }
-
- $employee = new Employee();
- $employee->firstName = $form['first_name'];
- $employee->lastName = $form['last_name'];
- $employee->email = $form['email'];
- $employee->department = $form['department'];
- $employee->jobTitle = $form['job_title'];
- $employee->startDate = $form['start_date'];
-
- $this->employees()->create($employee);
-
- if ($this->isHtmxRequest($request)) {
- $viewModel = $this->buildViewModel();
- $viewModel->saved = true;
-
- return $this->fragment('employees.partials.form', [
- 'model' => $viewModel,
- ], 200, [
- 'HX-Trigger' => json_encode(['employees-changed' => true]),
- ]);
- }
-
- return $this->redirect('/employees?saved=1');
- }
-
- public function create()
- {
- return $this->redirect('/employees');
- }
-
- public function summary()
- {
- $request = Request::capture();
- $viewModel = $this->buildViewModel((string) $request->input('search', ''));
-
- return $this->fragment('employees.partials.summary', [
- 'model' => $viewModel,
- ]);
- }
-
- public function data()
- {
- $request = Request::capture();
- $search = trim((string) $request->input('search', ''));
- $rows = $this->employees()->search($search);
-
- $data = array_map(
- static function (array $row): array {
- return [
- 'id' => (int) $row['id'],
- 'full_name' => trim($row['first_name'] . ' ' . $row['last_name']),
- 'first_name' => (string) $row['first_name'],
- 'last_name' => (string) $row['last_name'],
- 'email' => (string) $row['email'],
- 'department' => (string) $row['department'],
- 'job_title' => (string) $row['job_title'],
- 'start_date' => (string) $row['start_date'],
- 'created_at' => (string) $row['created_at'],
- ];
- },
- $rows
- );
-
- return $this->json($data);
- }
-
- /**
- * @return array<string, string>
- */
- private function sanitizeFormData(Request $request): array
- {
- return [
- 'first_name' => trim((string) $request->input('first_name', '')),
- 'last_name' => trim((string) $request->input('last_name', '')),
- 'email' => trim((string) $request->input('email', '')),
- 'department' => trim((string) $request->input('department', '')),
- 'job_title' => trim((string) $request->input('job_title', '')),
- 'start_date' => trim((string) $request->input('start_date', '')),
- ];
- }
-
- /**
- * @param array<string, string> $form
- * @return array<string, list<string>>
- */
- private function validateForm(array $form, Request $request): array
- {
- $validator = new Validator();
-
- $validator
- ->required('first_name', $form['first_name'], 'First name is required.')
- ->maxLength('first_name', $form['first_name'], 100, 'First name must be 100 characters or fewer.')
- ->required('last_name', $form['last_name'], 'Last name is required.')
- ->maxLength('last_name', $form['last_name'], 100, 'Last name must be 100 characters or fewer.')
- ->required('email', $form['email'], 'Email is required.')
- ->maxLength('email', $form['email'], 255, 'Email must be 255 characters or fewer.')
- ->required('department', $form['department'], 'Department is required.')
- ->maxLength('department', $form['department'], 100, 'Department must be 100 characters or fewer.')
- ->required('job_title', $form['job_title'], 'Job title is required.')
- ->maxLength('job_title', $form['job_title'], 150, 'Job title must be 150 characters or fewer.')
- ->required('start_date', $form['start_date'], 'Start date is required.');
-
- $errors = $validator->errors();
-
- if (!verify_csrf_token((string) $request->input('_token', ''))) {
- $errors['_token'][] = 'Your form session expired. Please refresh the page and try again.';
- }
-
- if ($form['email'] !== '' && filter_var($form['email'], FILTER_VALIDATE_EMAIL) === false) {
- $errors['email'][] = 'Enter a valid email address.';
- }
-
- if ($form['start_date'] !== '' && !$this->isValidDate($form['start_date'])) {
- $errors['start_date'][] = 'Enter a valid start date.';
- }
-
- return $errors;
- }
-
- private function isValidDate(string $value): bool
- {
- $date = \DateTimeImmutable::createFromFormat('Y-m-d', $value);
-
- return $date !== false && $date->format('Y-m-d') === $value;
- }
-
- private function employees(): EmployeeRepository
- {
- return new EmployeeRepository(database());
- }
-
- private function isHtmxRequest(Request $request): bool
- {
- return strtolower((string) $request->server('HTTP_HX_REQUEST', '')) === 'true';
- }
-
- private function buildViewModel(string $search = ''): EmployeeFormViewModel
- {
- $viewModel = new EmployeeFormViewModel();
- $viewModel->search = trim($search);
-
- $employees = $this->employees()->search($viewModel->search);
- $newestEmployee = $this->employees()->newestMatching($viewModel->search);
-
- $viewModel->employees = array_slice($employees, 0, 5);
- $viewModel->newestEmployee = $newestEmployee;
- $viewModel->summary = [
- 'employee_count' => $this->employees()->countMatching($viewModel->search),
- 'department_count' => $this->employees()->countDepartments($viewModel->search),
- 'latest_start_date' => $newestEmployee['start_date'] ?? 'N/A',
- ];
-
- return $viewModel;
- }
- }
|