Files
JobcardSystem/app/Domain/Jobcard/TimeEntryCommand.php
T

94 lines
3.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Domain\Jobcard;
require_once __DIR__ . '/TimeEntryValidator.php';
/**
* Validates and normalizes a framework-free command for recording jobcard time.
*/
final class TimeEntryCommand
{
public const MAX_NOTES_LENGTH = 10000;
public function __construct(private readonly ?TimeEntryValidator $timeEntries = null)
{
}
/**
* @return array{valid: bool, jobcard_id: ?int, technician_id: ?int, work_date: string, start_time: ?string, end_time: ?string, hours: ?float, notes: ?string, counts_toward_sla: bool, errors: array<string, string>}
*/
public function validate(array $command): array
{
$jobcardId = $this->positiveInteger($command['jobcard_id'] ?? null);
$technicianId = $this->positiveInteger($command['technician_id'] ?? null);
$workDate = $this->text($command['work_date'] ?? null) ?? '';
$startTime = $this->text($command['start_time'] ?? null);
$endTime = $this->text($command['end_time'] ?? null);
$notes = $this->text($command['notes'] ?? null);
$slaFlag = $this->boolean($command['counts_toward_sla'] ?? true);
$manualHours = $command['hours'] ?? null;
if ($manualHours === '') $manualHours = null;
$entry = ($this->timeEntries ?? new TimeEntryValidator())->validate([
'work_date' => $workDate,
'start_time' => $startTime,
'end_time' => $endTime,
'hours' => $manualHours,
]);
$errors = $entry['errors'];
if ($jobcardId === null) $errors['jobcard_id'] = 'Jobcard ID must be a positive integer.';
if ($technicianId === null) $errors['technician_id'] = 'Technician ID must be a positive integer.';
if ($manualHours !== null && ($startTime !== null || $endTime !== null)) $errors['time'] = 'Manual hours and start/end times cannot both be supplied.';
if ($notes !== null && mb_strlen($notes) > self::MAX_NOTES_LENGTH) $errors['notes'] = 'Notes are too long.';
if ($slaFlag === null) $errors['counts_toward_sla'] = 'SLA flag must be boolean.';
return [
'valid' => $errors === [],
'jobcard_id' => $jobcardId,
'technician_id' => $technicianId,
'work_date' => $workDate,
'start_time' => $startTime,
'end_time' => $endTime,
'hours' => $entry['hours'],
'notes' => $notes,
'counts_toward_sla' => $slaFlag ?? false,
'errors' => $errors,
];
}
private function positiveInteger(mixed $value): ?int
{
if (is_int($value)) {
return $value > 0 ? $value : null;
}
if (is_string($value) && preg_match('/^[1-9]\d*$/', $value) === 1) {
$integer = filter_var($value, FILTER_VALIDATE_INT);
return $integer !== false ? $integer : null;
}
return null;
}
private function boolean(mixed $value): ?bool
{
if (is_bool($value)) return $value;
if ($value === 1 || $value === 0) return $value === 1;
if (is_string($value)) {
return match (strtolower(trim($value))) {
'1', 'true', 'yes', 'on' => true,
'0', 'false', 'no', 'off', '' => false,
default => null,
};
}
return null;
}
private function text(mixed $value): ?string
{
if (!is_scalar($value)) return null;
$text = trim((string) $value);
return $text === '' ? null : $text;
}
}