# Controller

This guide explains how to implement HTTP controllers following our established patterns.

## Overview

Controllers are the **HTTP layer** of the application. They:

1. Receive HTTP requests
2. Validate input via FormRequest classes
3. Convert validated data to DTOs
4. Delegate business logic to services
5. Return formatted JSON responses

## Base Controller

All controllers extend the base Controller class:

**Location:** `app/Http/Controllers/Controller.php`

```php
abstract class Controller extends BaseController
{
    use AuthorizesRequests, ValidatesRequests, InteractWithResponse, AddPermissions;
}
```

## Standard Controller Template

```php
<?php

namespace App\Domains\YourDomain\YourModule\Http\Controllers\V1;

use App\Http\Controllers\Controller;
use App\Domains\YourDomain\YourModule\DTOs\YourEntityDTO;
use App\Domains\YourDomain\YourModule\Http\Requests\V1\StoreYourEntityRequest;
use App\Domains\YourDomain\YourModule\Http\Requests\V1\UpdateYourEntityRequest;
use App\Domains\YourDomain\YourModule\Http\Resources\YourEntityResource;
use App\Domains\YourDomain\YourModule\Models\YourEntity;
use App\Domains\YourDomain\YourModule\Services\YourEntityService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class YourEntityController extends Controller
{
    public function __construct(
        private readonly YourEntityService $service
    ) {
        $this->applyPermissions(
            'your-entities',
            ['index', 'show', 'store', 'update', 'destroy'],
            []
        );
    }

    /**
     * List all entities
     */
    public function index(Request $request): JsonResponse
    {
        $entities = $this->service->index();

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

    /**
     * Get single entity
     */
    public function show(YourEntity $yourEntity): JsonResponse
    {
        $entity = $this->service->find($yourEntity->id);

        return $this->sendSuccessResponse(
            new YourEntityResource($entity),
            __('Entity retrieved successfully')
        );
    }

    /**
     * Create new entity
     */
    public function store(StoreYourEntityRequest $request): JsonResponse
    {
        try {
            $dto = YourEntityDTO::fromRequest($request->validated());
            $entity = $this->service->store($dto);

            return $this->sendSuccessResponse(
                new YourEntityResource($entity),
                __('Entity created successfully'),
                201
            );
        } catch (\Exception $e) {
            return $this->sendFailedResponse($e->getMessage());
        }
    }

    /**
     * Update existing entity
     */
    public function update(UpdateYourEntityRequest $request, YourEntity $yourEntity): JsonResponse
    {
        try {
            $dto = YourEntityDTO::fromRequest($request->validated());
            $entity = $this->service->update($dto, $yourEntity);

            return $this->sendSuccessResponse(
                new YourEntityResource($entity),
                __('Entity updated successfully')
            );
        } catch (\Exception $e) {
            return $this->sendFailedResponse($e->getMessage());
        }
    }

    /**
     * Delete entity
     */
    public function destroy(YourEntity $yourEntity): JsonResponse
    {
        try {
            $this->service->destroy($yourEntity);

            return $this->sendSuccessResponse(
                message: __('Entity deleted successfully')
            );
        } catch (\Exception $e) {
            return $this->sendFailedResponse($e->getMessage());
        }
    }
}
```

## Response Methods

The `InteractWithResponse` trait provides three response methods:

### Success Response (Single Item)

```php
$this->sendSuccessResponse(
    data: $resource,           // Resource or array
    message: 'Success message', // Optional message
    code: 200,                  // HTTP status code
    to: null                    // Redirect URL (optional)
);
```

**Response Format:**
```json
{
    "success": true,
    "code": 200,
    "message": "Success message",
    "to": null,
    "result": { /* resource data */ }
}
```

### Paginated Response (Collections)

```php
$this->sendPaginatedResponse(
    resource: $collection,  // Resource collection
    code: 200,              // HTTP status code
    message: 'OK'           // Optional message
);
```

**Response Format:**
```json
{
    "success": true,
    "code": 200,
    "message": "OK",
    "items": [ /* array of resources */ ],
    "current_page": 1,
    "per_page": 15,
    "total": 100,
    "last_page": 7,
    "from": 1,
    "to": 15
}
```

### Failed Response (Errors)

```php
$this->sendFailedResponse(
    message: 'Error message',  // Error description
    code: 400,                 // HTTP status code
    to: null                   // Redirect URL (optional)
);
```

**Response Format:**
```json
{
    "success": false,
    "code": 400,
    "message": "Error message",
    "to": null,
    "result": null
}
```

## Permission Handling

### Using AddPermissions Trait

```php
public function __construct(private readonly YourEntityService $service)
{
    // Apply standard CRUD permissions
    $this->applyPermissions(
        'your-entities',                              // Permission prefix
        ['index', 'show', 'store', 'update', 'destroy'], // CRUD methods
        [
            'approve' => 'approve',                   // Custom actions
            'export' => 'export',
        ]
    );
}
```

**Permission Mapping:**
| Method | Permission |
|--------|------------|
| `index` | `your-entities.list` |
| `show` | `your-entities.show` |
| `store` | `your-entities.create` |
| `update` | `your-entities.edit` |
| `destroy` | `your-entities.delete` |
| `approve` | `your-entities.approve` |

### Manual Permission Checks

```php
public function sensitiveAction(YourEntity $entity): JsonResponse
{
    $user = auth()->user();

    if (!$user->hasPermissionTo('your-entities.sensitive-action')) {
        return $this->sendFailedResponse(__('Unauthorized'), 403);
    }

    // Proceed with action
}
```

## Controller Patterns

### Index with Filters

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

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

### Show with Route Model Binding

```php
// Route: GET /your-entities/{yourEntity}
public function show(YourEntity $yourEntity): JsonResponse
{
    // Laravel automatically finds the model by ID
    // Throws 404 if not found

    return $this->sendSuccessResponse(
        new YourEntityResource($yourEntity->load(['category', 'items']))
    );
}
```

### Store with DTO

```php
public function store(StoreYourEntityRequest $request): JsonResponse
{
    try {
        // 1. Get validated data from FormRequest
        $validated = $request->validated();

        // 2. Create DTO from validated data
        $dto = YourEntityDTO::fromRequest($validated);

        // 3. Pass DTO to service
        $entity = $this->service->store($dto);

        // 4. Return success response with resource
        return $this->sendSuccessResponse(
            new YourEntityResource($entity),
            __('Created successfully'),
            201
        );
    } catch (\Exception $e) {
        return $this->sendFailedResponse($e->getMessage());
    }
}
```

### Update with Validation

```php
public function update(
    UpdateYourEntityRequest $request,
    YourEntity $yourEntity
): JsonResponse {
    try {
        $dto = YourEntityDTO::fromRequest($request->validated());
        $entity = $this->service->update($dto, $yourEntity);

        return $this->sendSuccessResponse(
            new YourEntityResource($entity),
            __('Updated successfully')
        );
    } catch (\Exception $e) {
        return $this->sendFailedResponse($e->getMessage(), 422);
    }
}
```

### Destroy with Confirmation

```php
public function destroy(YourEntity $yourEntity): JsonResponse
{
    try {
        $this->service->destroy($yourEntity);

        return $this->sendSuccessResponse(
            message: __('Deleted successfully')
        );
    } catch (\Exception $e) {
        return $this->sendFailedResponse($e->getMessage(), 422);
    }
}
```

## Advanced Patterns

### Status Change Actions

```php
public function approve(YourEntity $yourEntity): JsonResponse
{
    try {
        $entity = $this->service->approve($yourEntity);

        return $this->sendSuccessResponse(
            new YourEntityResource($entity),
            __('Approved successfully')
        );
    } catch (\Exception $e) {
        return $this->sendFailedResponse($e->getMessage(), 422);
    }
}

public function reject(Request $request, YourEntity $yourEntity): JsonResponse
{
    $validated = $request->validate([
        'reason' => 'required|string|max:500',
    ]);

    try {
        $entity = $this->service->reject($yourEntity, $validated['reason']);

        return $this->sendSuccessResponse(
            new YourEntityResource($entity),
            __('Rejected successfully')
        );
    } catch (\Exception $e) {
        return $this->sendFailedResponse($e->getMessage(), 422);
    }
}
```

### Bulk Actions

```php
public function bulkApprove(Request $request): JsonResponse
{
    $validated = $request->validate([
        'ids' => 'required|array|min:1',
        'ids.*' => 'integer|exists:your_entities,id',
    ]);

    try {
        $count = $this->service->bulkApprove($validated['ids']);

        return $this->sendSuccessResponse(
            ['approved_count' => $count],
            __(':count items approved', ['count' => $count])
        );
    } catch (\Exception $e) {
        return $this->sendFailedResponse($e->getMessage());
    }
}
```

### Export Action

```php
public function export(Request $request): JsonResponse
{
    $validated = $request->validate([
        'format' => 'required|in:xlsx,csv,pdf',
        'filters' => 'nullable|array',
    ]);

    try {
        $url = $this->service->export(
            $validated['format'],
            $validated['filters'] ?? []
        );

        return $this->sendSuccessResponse(
            ['download_url' => $url],
            __('Export ready')
        );
    } catch (\Exception $e) {
        return $this->sendFailedResponse($e->getMessage());
    }
}
```

### Conditional Response Types

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

    // Handle both paginated and non-paginated results
    if ($result instanceof \Illuminate\Pagination\LengthAwarePaginator) {
        return $this->sendPaginatedResponse(
            YourEntityResource::collection($result)
        );
    }

    return $this->sendSuccessResponse($result);
}
```

### File Upload

```php
public function uploadAttachment(Request $request, YourEntity $yourEntity): JsonResponse
{
    $validated = $request->validate([
        'file' => 'required|file|mimes:pdf,doc,docx|max:10240',
    ]);

    try {
        $entity = $this->service->addAttachment(
            $yourEntity,
            $validated['file']
        );

        return $this->sendSuccessResponse(
            new YourEntityResource($entity),
            __('Attachment uploaded')
        );
    } catch (\Exception $e) {
        return $this->sendFailedResponse($e->getMessage());
    }
}
```

## Error Handling

### Standard Pattern

```php
public function store(StoreYourEntityRequest $request): JsonResponse
{
    try {
        $dto = YourEntityDTO::fromRequest($request->validated());
        $entity = $this->service->store($dto);

        return $this->sendSuccessResponse(
            new YourEntityResource($entity),
            __('Created successfully'),
            201
        );
    } catch (\InvalidArgumentException $e) {
        // Validation errors - 422
        return $this->sendFailedResponse($e->getMessage(), 422);
    } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
        // Not found - 404
        return $this->sendFailedResponse(__('Resource not found'), 404);
    } catch (\Exception $e) {
        // General errors - 400
        \Log::error('Store failed', ['error' => $e->getMessage()]);
        return $this->sendFailedResponse($e->getMessage());
    }
}
```

## HTTP Status Codes

| Code | Use Case |
|------|----------|
| 200 | Success (GET, PUT, DELETE) |
| 201 | Created (POST) |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 422 | Validation Error |
| 500 | Server Error |

## Best Practices

### DO:

1. **Use FormRequest for validation** - Never validate in controller
2. **Use DTOs for data transfer** - Never pass raw arrays to services
3. **Use Resources for responses** - Consistent API format
4. **Handle exceptions** - Wrap service calls in try-catch
5. **Use route model binding** - Let Laravel find models

### DON'T:

1. **Don't put business logic** - Use services
2. **Don't access database directly** - Use services
3. **Don't skip validation** - Always validate input
4. **Don't return raw arrays** - Use Resources
5. **Don't hardcode messages** - Use `__()` for translations

## Controller Method Summary

| Method | HTTP | Purpose | Returns |
|--------|------|---------|---------|
| `index` | GET | List entities | Paginated collection |
| `show` | GET | Get single entity | Single resource |
| `store` | POST | Create entity | Created resource |
| `update` | PUT/PATCH | Update entity | Updated resource |
| `destroy` | DELETE | Delete entity | Success message |
| `approve` | POST | Change status | Updated resource |
| `export` | GET | Export data | Download URL |
