API Resources
API Resources transform Eloquent models into JSON responses. They control exactly what data is sent to the client and how relationships are serialized. The CPR backend has 46+ Resource classes.
Directory Structure
app/Http/Resources/
├── PatientResource.php
├── TransactionResource.php
├── TransactionItemResource.php
├── QueueTicketResource.php
├── BranchResource.php
├── DoctorResource.php
├── MedicineResource.php
├── PatientVisitResource.php
├── BillItemResource.php
├── PharmacyItemResource.php
├── DeliveryResource.php
├── PurchaseOrderResource.php
└── ... (46+ resources)How They Work
Resources extend JsonResource and define a toArray() method that maps model fields to JSON output:
class PatientResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'pin' => $this->pin,
'name' => $this->name, // Computed attribute
'first_name' => $this->first_name,
'last_name' => $this->last_name,
'middle_name' => $this->middle_name,
'nick_name' => $this->nick_name,
'birthdate' => $this->birthdate,
'nationality' => $this->nationality,
'occupation' => $this->occupation,
'sex' => $this->sex,
'civil_status' => $this->civil_status,
'telephone' => $this->telephone,
'mobile_phone' => $this->mobile_phone,
'email' => $this->email,
'photo_url' => $this->photo_path
? Storage::temporaryUrl($this->photo_path, now()->addMinutes(60))
: null,
'refered_by' => $this->refered_by,
'referral_source_type' => $this->referral_source_type,
'status' => $this->status,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}Key Patterns
Conditional Relationship Loading (whenLoaded)
Only includes a relation if it was eager-loaded — prevents accidental N+1 queries:
class TransactionResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'transaction_number' => $this->transaction_number,
'branch_id' => $this->branch_id,
'branch' => $this->whenLoaded('branch'),
'patient_id' => $this->patient_id,
'patient' => PatientResource::make($this->whenLoaded('patient')),
'transaction_date' => $this->transaction_date?->format('Y-m-d'),
'total_amount' => $this->total_amount,
'payment_method_id' => $this->payment_method_id,
'payment_method' => $this->whenLoaded('paymentMethod'),
'status' => $this->status,
'notes' => $this->notes,
'items' => TransactionItemResource::collection($this->whenLoaded('items')),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}If the patient relation wasn't eager-loaded, it will be omitted from the response entirely (no lazy-load triggered).
Nested Resource Collections
Resources can nest other resources for related data:
'items' => TransactionItemResource::collection($this->whenLoaded('items')),Enum Serialization
For models with enum casts, resources access enum values and helper methods:
class QueueTicketResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'ticket_number' => $this->ticket_number,
'status' => $this->status?->value, // Enum string value
'status_label' => $this->status?->label(), // Human-readable label
'status_color' => $this->status?->color(), // UI color code
'priority_type' => $this->priority_type?->value,
'priority_label' => $this->priority_type?->label(),
'is_appointment' => $this->is_appointment,
'patient' => PatientResource::make($this->whenLoaded('patient')),
'service' => $this->whenLoaded('service'),
// ... timestamps, user references
];
}
}Date Formatting
Dates are formatted explicitly when the default ISO 8601 format isn't desired:
'transaction_date' => $this->transaction_date?->format('Y-m-d'),Temporary URLs
For file uploads stored on cloud storage:
'photo_url' => $this->photo_path
? Storage::temporaryUrl($this->photo_path, now()->addMinutes(60))
: null,Usage in Controllers
Single Resource
public function show(Patient $patient)
{
return PatientResource::make($patient)
->additional(['message' => 'Patient retrieved successfully']);
}Resource Collection (Paginated)
public function index(Request $request)
{
$patients = $this->service->getPatients(
$request->input('search'),
$request->all(),
$request->input('perPage', 15)
);
return PatientResource::collection($patients)
->additional(['message' => 'Patients retrieved successfully']);
}The additional() method adds extra data alongside the data key in the response.
Created Response (201)
public function store(StorePatientDataRequest $request)
{
$patient = $this->service->create($request->validated(), $branch);
return PatientResource::make($patient)
->additional(['message' => 'Patient created successfully'])
->response()
->setStatusCode(201);
}Creating a New Resource
php artisan make:resource ExampleResourceThen define toArray():
class ExampleResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
// Map fields...
'related' => RelatedResource::make($this->whenLoaded('related')),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}