Files
JobcardSystem/app/Domain/Reporting/ClientHistoryService.php
T

65 lines
3.0 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Domain\Reporting {
require_once __DIR__ . '/ReportFilters.php';
/** Deterministic, storage-agnostic filtering for a client's status history. */
final class ClientHistoryService
{
/** @param list<array<string,mixed>> $rows @return list<array<string,mixed>> */
public function filter(array $rows, array|\ReportFilters|null $criteria = null): array
{
$filters = $criteria instanceof \ReportFilters ? $criteria : \ReportFilters::fromArray($criteria ?? []);
$result = [];
foreach ($rows as $row) {
if (!is_array($row) || !$filters->matches($row)) continue;
$result[] = $row;
}
usort($result, static fn (array $a, array $b): int => strcmp((string)($a['changed_at'] ?? $a['created_at'] ?? ''), (string)($b['changed_at'] ?? $b['created_at'] ?? '')) ?: ((int)($a['id'] ?? 0) <=> (int)($b['id'] ?? 0)));
return $result;
}
/** @return list<array<string,mixed>> */
public function forClient(array $rows, int|string $clientId, array $criteria = []): array
{
if (!is_int($clientId) && (!is_string($clientId) || preg_match('/^[1-9]\d*$/', trim($clientId)) !== 1)) throw new \InvalidArgumentException('Client ID must be a positive integer.');
$criteria['client_id'] = (int)$clientId;
return $this->filter($rows, $criteria);
}
/** @return array<string,mixed> */
public function validateForClient(mixed $clientId, array $rows, array $criteria = []): array
{
$id = $this->positiveId($clientId);
if ($id === null) return ['valid' => false, 'client_id' => null, 'timeline' => [], 'errors' => ['client_id' => 'Client ID must be a positive integer.']];
return ['valid' => true, 'client_id' => $id, 'timeline' => $this->forClient($rows, $id, $criteria), 'errors' => []];
}
/** Controller-ready client history timeline command. */
public function timeline(array $rows, mixed $clientId, array $criteria = []): array
{
return $this->validateForClient($clientId, $rows, $criteria);
}
/** @return list<array<string,mixed>> */
public function history(array $rows, array|\ReportFilters|null $criteria = null): array { return $this->filter($rows, $criteria); }
/** @return list<array<string,mixed>> */
public function query(array $rows, array|\ReportFilters|null $criteria = null): array { return $this->filter($rows, $criteria); }
/** @return list<array<string,mixed>> */
public function getHistory(array $rows, array|\ReportFilters|null $criteria = null): array { return $this->filter($rows, $criteria); }
private function positiveId(mixed $value): ?int
{
if (is_int($value) && $value > 0) return $value;
if (is_string($value) && preg_match('/^[1-9]\\d*$/', trim($value)) === 1) { $id = filter_var(trim($value), FILTER_VALIDATE_INT); return $id === false ? null : $id; }
return null;
}
}
}
namespace {
if (!class_exists('ClientHistoryService', false)) class_alias('App\\Domain\\Reporting\\ClientHistoryService', 'ClientHistoryService');
}