# API Resources

This guide explains how to implement API Resource classes for transforming models into JSON responses.

## Overview

API Resources provide:

1. **Consistent response structure** across all endpoints
2. **Data transformation** - format, compute, and filter fields
3. **Relationship handling** - conditionally include related data
4. **Security** - control what data is exposed

## Standard Resource Template

```php
<?php

namespace App\Domains\YourDomain\YourModule\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class YourEntityResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     */
    public function toArray(Request $request): array
    {
        return [
            // Basic fields
            'id' => $this->id,
            'name' => $this->name,
            'code' => $this->code,
            'description' => $this->description,

            // Formatted fields
            'amount' => (float) $this->amount,
            'formatted_amount' => number_format($this->amount, 2),

            // Status with label
            'status' => $this->status?->value,
            'status_label' => $this->status?->label(),

            // Boolean fields
            'is_active' => (bool) $this->is_active,

            // Date fields
            'date' => $this->date?->format('Y-m-d'),
            'created_at' => $this->created_at?->format('Y-m-d H:i:s'),
            'updated_at' => $this->updated_at?->format('Y-m-d H:i:s'),

            // Conditional relationships
            'category' => $this->whenLoaded('category', fn() => [
                'id' => $this->category->id,
                'name' => $this->category->name,
            ]),

            'items' => YourEntityItemResource::collection(
                $this->whenLoaded('items')
            ),

            'created_by' => $this->whenLoaded('createdBy', fn() => [
                'id' => $this->createdBy->id,
                'name' => $this->createdBy->name,
            ]),

            // Computed values
            'items_count' => $this->whenCounted('items'),
            'total' => $this->when(
                $this->relationLoaded('items'),
                fn() => $this->items->sum('total')
            ),
        ];
    }
}
```

## Resource Patterns

### Basic Field Mapping

```php
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
    ];
}
```

### Type Casting

```php
return [
    // Numeric casting
    'amount' => (float) $this->amount,
    'quantity' => (int) $this->quantity,
    'percentage' => round((float) $this->percentage, 2),

    // Boolean casting
    'is_active' => (bool) $this->is_active,
    'has_items' => (bool) $this->items_count,

    // String casting
    'code' => (string) $this->code,
];
```

### Date Formatting

```php
return [
    // Date only
    'date' => $this->date?->format('Y-m-d'),

    // Date and time
    'created_at' => $this->created_at?->format('Y-m-d H:i:s'),

    // Human readable
    'created_ago' => $this->created_at?->diffForHumans(),

    // ISO 8601
    'timestamp' => $this->created_at?->toIso8601String(),

    // Custom format
    'formatted_date' => $this->date?->format('d M Y'),
];
```

### Enum Handling

```php
return [
    // Enum value
    'status' => $this->status?->value,

    // Enum label
    'status_label' => $this->status?->label(),

    // Both
    'status' => [
        'value' => $this->status?->value,
        'label' => $this->status?->label(),
    ],
];
```

### Conditional Fields

```php
return [
    // Include only when value exists
    'discount' => $this->when($this->discount > 0, $this->discount),

    // Include based on condition
    'admin_notes' => $this->when(
        auth()->user()?->hasRole('admin'),
        $this->admin_notes
    ),

    // Include with transformation
    'secret_code' => $this->when(
        $this->canViewSecret(),
        fn() => decrypt($this->secret_code)
    ),
];
```

### Relationship Loading

#### whenLoaded - Inline

```php
return [
    'category' => $this->whenLoaded('category', fn() => [
        'id' => $this->category->id,
        'name' => $this->category->name,
    ]),

    'created_by' => $this->whenLoaded('createdBy', fn() => [
        'id' => $this->createdBy->id,
        'name' => $this->createdBy->name,
        'email' => $this->createdBy->email,
    ]),
];
```

#### whenLoaded - With Resource

```php
return [
    'category' => new CategoryResource($this->whenLoaded('category')),

    'items' => YourEntityItemResource::collection(
        $this->whenLoaded('items')
    ),

    // With nested resource
    'project' => $this->whenLoaded('project', fn() =>
        new ProjectResource($this->project)
    ),
];
```

#### whenCounted

```php
return [
    'items_count' => $this->whenCounted('items'),
    'comments_count' => $this->whenCounted('comments'),
];
```

#### Nested Relationships

```php
return [
    'category' => $this->whenLoaded('category', fn() => [
        'id' => $this->category->id,
        'name' => $this->category->name,
        // Nested relationship
        'parent' => $this->category->relationLoaded('parent')
            ? [
                'id' => $this->category->parent->id,
                'name' => $this->category->parent->name,
            ]
            : null,
    ]),
];
```

### Computed Values

```php
return [
    // Simple computation
    'total' => $this->quantity * $this->price,

    // From loaded relationship
    'items_total' => $this->when(
        $this->relationLoaded('items'),
        fn() => $this->items->sum(fn($item) => $item->quantity * $item->price)
    ),

    // Formatted computation
    'formatted_total' => number_format(
        $this->quantity * $this->price,
        2
    ),

    // Percentage
    'completion_rate' => $this->total_tasks > 0
        ? round(($this->completed_tasks / $this->total_tasks) * 100, 1)
        : 0,
];
```

### Media/Files

```php
return [
    // Single media
    'image_url' => $this->getFirstMediaUrl('images'),
    'thumbnail_url' => $this->getFirstMediaUrl('images', 'thumbnail'),

    // Multiple media
    'attachments' => $this->getMedia('attachments')->map(fn($media) => [
        'id' => $media->id,
        'name' => $media->file_name,
        'url' => $media->getUrl(),
        'size' => $media->size,
        'mime_type' => $media->mime_type,
    ]),

    // With fallback
    'avatar_url' => $this->getFirstMediaUrl('avatar')
        ?: asset('images/default-avatar.png'),
];
```

## Item Resource (For Nested Resources)

```php
<?php

namespace App\Domains\YourDomain\YourModule\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class YourEntityItemResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'quantity' => (int) $this->quantity,
            'price' => (float) $this->price,
            'total' => (float) ($this->quantity * $this->price),

            // Nested relationship
            'product' => $this->whenLoaded('product', fn() => [
                'id' => $this->product->id,
                'name' => $this->product->name,
                'sku' => $this->product->sku,
            ]),
        ];
    }
}
```

## Collection Resource

For customizing collection responses:

```php
<?php

namespace App\Domains\YourDomain\YourModule\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;

class YourEntityCollection extends ResourceCollection
{
    /**
     * The resource that this resource collects.
     */
    public $collects = YourEntityResource::class;

    /**
     * Transform the resource collection into an array.
     */
    public function toArray(Request $request): array
    {
        return [
            'data' => $this->collection,
            'meta' => [
                'total_amount' => $this->collection->sum('amount'),
                'active_count' => $this->collection->where('is_active', true)->count(),
            ],
        ];
    }
}
```

## Usage in Controller

### Single Resource

```php
public function show(YourEntity $yourEntity): JsonResponse
{
    $entity = $this->service->find($yourEntity->id);

    return $this->sendSuccessResponse(
        new YourEntityResource($entity)
    );
}
```

### Resource Collection (Paginated)

```php
public function index(): JsonResponse
{
    $entities = $this->service->index();

    return $this->sendPaginatedResponse(
        YourEntityResource::collection($entities)
    );
}
```

### Resource with Additional Data

```php
public function show(YourEntity $yourEntity): JsonResponse
{
    $entity = $this->service->find($yourEntity->id);

    return $this->sendSuccessResponse(
        (new YourEntityResource($entity))->additional([
            'permissions' => [
                'can_edit' => auth()->user()->can('update', $entity),
                'can_delete' => auth()->user()->can('delete', $entity),
            ],
        ])
    );
}
```

## Advanced Patterns

### Conditional Structure Based on Context

```php
public function toArray(Request $request): array
{
    // Basic fields always included
    $data = [
        'id' => $this->id,
        'name' => $this->name,
        'status' => $this->status?->value,
    ];

    // Add more fields for detailed view
    if ($request->routeIs('*.show')) {
        $data = array_merge($data, [
            'description' => $this->description,
            'metadata' => $this->metadata,
            'items' => YourEntityItemResource::collection(
                $this->whenLoaded('items')
            ),
        ]);
    }

    // Add admin-only fields
    if (auth()->user()?->hasRole('admin')) {
        $data['internal_notes'] = $this->internal_notes;
        $data['cost_price'] = $this->cost_price;
    }

    return $data;
}
```

### Resource with Parameters

```php
class YourEntityResource extends JsonResource
{
    private bool $includeDetails;

    public function __construct($resource, bool $includeDetails = false)
    {
        parent::__construct($resource);
        $this->includeDetails = $includeDetails;
    }

    public function toArray(Request $request): array
    {
        $data = [
            'id' => $this->id,
            'name' => $this->name,
        ];

        if ($this->includeDetails) {
            $data['description'] = $this->description;
            $data['metadata'] = $this->metadata;
        }

        return $data;
    }
}

// Usage
new YourEntityResource($entity, includeDetails: true);
```

### Pagination Wrapper

When using `sendPaginatedResponse`, the response is automatically formatted:

```json
{
    "success": true,
    "code": 200,
    "message": "OK",
    "items": [
        { "id": 1, "name": "Item 1" },
        { "id": 2, "name": "Item 2" }
    ],
    "current_page": 1,
    "per_page": 15,
    "total": 100,
    "last_page": 7,
    "from": 1,
    "to": 15
}
```

## Best Practices

### DO:

1. **Cast types explicitly** - `(float)`, `(int)`, `(bool)`
2. **Use whenLoaded()** - Only include loaded relationships
3. **Format dates consistently** - Use `Y-m-d H:i:s`
4. **Include enum labels** - For display purposes
5. **Handle null values** - Use null-safe operator `?->`

### DON'T:

1. **Don't expose sensitive data** - Filter out passwords, tokens
2. **Don't include all fields** - Only what API consumers need
3. **Don't compute in Resource** - Keep complex logic in Service
4. **Don't load relationships here** - Load in Service/Controller
5. **Don't include internal IDs** - Unless necessary

## Response Examples

### Single Entity Response

```json
{
    "success": true,
    "code": 200,
    "message": "Entity retrieved successfully",
    "result": {
        "id": 1,
        "name": "Example Entity",
        "code": "ENT-001",
        "amount": 1500.50,
        "formatted_amount": "1,500.50",
        "status": "active",
        "status_label": "Active",
        "is_active": true,
        "date": "2024-01-15",
        "created_at": "2024-01-15 10:30:00",
        "category": {
            "id": 5,
            "name": "Category A"
        },
        "items": [
            {
                "id": 1,
                "name": "Item 1",
                "quantity": 10,
                "price": 100.00,
                "total": 1000.00
            }
        ],
        "created_by": {
            "id": 1,
            "name": "John Doe"
        }
    }
}
```

### Collection Response (Paginated)

```json
{
    "success": true,
    "code": 200,
    "message": "OK",
    "items": [
        {
            "id": 1,
            "name": "Entity 1",
            "status": "active"
        },
        {
            "id": 2,
            "name": "Entity 2",
            "status": "pending"
        }
    ],
    "current_page": 1,
    "per_page": 15,
    "total": 50,
    "last_page": 4,
    "from": 1,
    "to": 15
}
```
