DTO Reference
Every DTO lives in BioTime\DTO, has fully readonly public properties, and a static fromArray(array $data): self factory. Every DTO also carries the complete original API row via raw.
Employee
class Employee
{
public readonly ?int $id;
public readonly ?string $empCode;
public readonly ?string $firstName;
public readonly ?string $lastName;
public readonly ?string $department;
public readonly array $raw;
}department reads data['department']['dept_name'] if present (nested object), otherwise falls back to a plain data['department'] value.
AttendanceRecord
class AttendanceRecord
{
public readonly ?int $id;
public readonly ?string $empCode;
public readonly ?string $firstName;
public readonly ?string $lastName;
public readonly ?string $punchTime;
public readonly ?string $punchState;
public readonly ?string $uploadTime;
public readonly array $raw;
}Device
class Device
{
public readonly ?int $id;
public readonly ?string $sn;
public readonly ?string $alias;
public readonly ?string $ipAddress;
public readonly ?bool $isActive;
public readonly ?int $areaId;
public readonly ?string $areaName;
public readonly ?string $lastActivity;
public readonly array $raw;
}isActive is derived from the API's state field (cast to bool). areaId/areaName read from a nested area object (area.id, area.area_name), falling back to a flat area_name field.
Department
class Department
{
public readonly ?int $id;
public readonly ?string $deptCode;
public readonly ?string $deptName;
public readonly ?int $parentDept;
public readonly array $raw;
}Position
class Position
{
public readonly ?int $id;
public readonly ?string $positionCode;
public readonly ?string $name;
public readonly ?int $parentArea;
public readonly array $raw;
}parentArea is mapped from the API's parent_position field.
Area
class Area
{
public readonly ?int $id;
public readonly ?string $areaCode;
public readonly ?string $name;
public readonly ?int $parentArea;
public readonly array $raw;
}Resign
class Resign
{
public readonly ?int $id;
public readonly ?string $resignDate;
public readonly ?int $resignType;
public readonly ?bool $disableAtt;
public readonly ?Employee $employee;
public readonly ?string $firstName;
public readonly ?string $lastName;
public readonly array $raw;
}employee is itself mapped to a nested Employee DTO from the API's employee object.
WARNING
Resign::fromArray() always calls Employee::fromArray($data['employee'] ?? null). Since Employee::fromArray() expects an array, a resignation row whose payload has no employee object will error at the DTO-mapping step rather than yielding a null employee property — this only surfaces if the BioTime API returns a resignation record without an embedded employee object.
None of the DTOs are strictly final in a way that blocks extension
DTOs are plain, constructor-promoted readonly classes — there's no interface to implement or abstract base class to extend if you want your own typed wrapper; you can freely build your own object from a DTO's ->raw array if the built-in properties aren't enough. See DTOs.