65 lines
1.7 KiB
PHP
65 lines
1.7 KiB
PHP
<?php
|
|||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Maps raw domain rows into an explicitly allow-listed client view and a
|
||
|
|
* separately retained internal view. This class has no framework or storage
|
||
|
|
* dependency and is safe to use before an export format is selected.
|
||
|
|
*/
|
||
|
|
final class ReportDataMapper
|
||
|
|
{
|
||
|
|
/** @var list<string> */
|
||
|
|
private const CLIENT_FIELDS = [
|
||
|
|
'id',
|
||
|
|
'name',
|
||
|
|
'registration_number',
|
||
|
|
'status',
|
||
|
|
'support_email',
|
||
|
|
'support_phone',
|
||
|
|
'preferred_contact_method',
|
||
|
|
'physical_address',
|
||
|
|
'postal_address',
|
||
|
|
'general_notes',
|
||
|
|
'client_id',
|
||
|
|
'client_name',
|
||
|
|
'reference_no',
|
||
|
|
'priority',
|
||
|
|
'work_requested',
|
||
|
|
'completed_at',
|
||
|
|
'closed_at',
|
||
|
|
'allocated_hours',
|
||
|
|
'used_hours',
|
||
|
|
'remaining_hours',
|
||
|
|
'usage_percentage',
|
||
|
|
'status_label',
|
||
|
|
'period_type',
|
||
|
|
'start_date',
|
||
|
|
'end_date',
|
||
|
|
'hours',
|
||
|
|
];
|
||
|
|
|
||
|
|
/** @return array<string, mixed> */
|
||
|
|
public function clientFacing(array $record): array
|
||
|
|
{
|
||
|
|
$safe = [];
|
||
|
|
foreach (self::CLIENT_FIELDS as $field) {
|
||
|
|
if (array_key_exists($field, $record)) {
|
||
|
|
$safe[$field] = $record[$field];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return $safe;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @return array<string, mixed> */
|
||
|
|
public function internal(array $record): array
|
||
|
|
{
|
||
|
|
return array_diff_key($record, $this->clientFacing($record));
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @return array{client: array<string, mixed>, internal: array<string, mixed>} */
|
||
|
|
public function map(array $record): array
|
||
|
|
{
|
||
|
|
return ['client' => $this->clientFacing($record), 'internal' => $this->internal($record)];
|
||
|
|
}
|
||
|
|
}
|