## Laravel RESTful Controller Conventions

### MANDATORY: Follow Standard Laravel Resource Controller Pattern

All controllers managing resources (models) **MUST** follow Laravel's RESTful conventions. This ensures consistency, predictability, and maintainability across the codebase.

#### Standard Resource Controller Methods

| HTTP Verb | URI                 | Action  | Route Name       | Purpose                           |
|-----------|---------------------|---------|------------------|-----------------------------------|
| GET       | /resource           | `index`  | resource.index   | Display listing of resources      |
| GET       | /resource/create    | `create` | resource.create  | Show form for creating resource   |
| POST      | /resource           | `store`  | resource.store   | Store new resource in storage     |
| GET       | /resource/{id}      | `show`   | resource.show    | Display a specific resource       |
| GET       | /resource/{id}/edit | `edit`   | resource.edit    | Show form for editing resource    |
| PUT/PATCH | /resource/{id}      | `update` | resource.update  | Update specific resource          |
| DELETE    | /resource/{id}      | `destroy`| resource.destroy | Remove specific resource          |

#### ✅ Correct Implementation Example (FlowsController)

```php
namespace App\Http\Controllers\Ipaas\flows;

use Domain\Ipaas\Flows\Actions\CreateFlow;
use Domain\Ipaas\Flows\Actions\UpdateFlow;
use App\Http\Requests\Ipaas\Flow\FlowRequest;

class FlowsController extends Controller
{
    /**
     * Display a listing of resources.
     */
    public function index()
    {
        $flows = Flow::all();
        return view('tenant.ipaas.flows.index', compact('flows'));
    }

    /**
     * Show the form for creating a new resource.
     * NOTE: Does NOT create the resource - only shows form.
     */
    public function create()
    {
        // In this case, we auto-create a blank Flow to enable visual builder
        // This is an exception to the rule - normally create() only shows a form
        return redirect()->route('ipaas.flows.store');
    }

    /**
     * Store a newly created resource in storage.
     * Handles actual creation logic.
     */
    public function store(FlowRequest $request)
    {
        try {
            // Use Domain Action (follows DDD pattern)
            $flow = CreateFlow::create($request->validated());
            
            Log::info('Flow created successfully', [
                'flow_id' => $flow->id,
                'user_id' => auth()->id(),
            ]);
            
            // Redirect to edit page
            return redirect()->route('ipaas.flows.edit', $flow->id)
                ->with('success', 'Flow created successfully!');
                
        } catch (\Exception $e) {
            Log::error('Flow creation exception', [
                'error' => $e->getMessage(),
            ]);
            
            return redirect()->route('ipaas.flows.index')
                ->with('error', 'An error occurred');
        }
    }

    /**
     * Show the form for editing the specified resource.
     * NOTE: Does NOT update the resource - only shows form with data.
     */
    public function edit(string $id)
    {
        $flow = Flow::findOrFail($id);
        return view('tenant.ipaas.flows.edit', compact('flow'));
    }

    /**
     * Update the specified resource in storage.
     * Handles actual update logic.
     */
    public function update(FlowRequest $request, $id)
    {
        try {
            // Use Domain Action (follows DDD pattern)
            $flow = UpdateFlow::update($request->validated(), $id);
            
            Log::info('Flow updated successfully', [
                'flow_id' => $flow->id,
                'user_id' => auth()->id(),
            ]);
            
            // Return JSON for AJAX calls
            return response()->json([
                'success' => true,
                'record' => $flow,
            ], 200);
            
        } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
            return response()->json([
                'success' => false,
                'message' => 'Flow not found'
            ], 404);
        }
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy($id)
    {
        $flow = Flow::findOrFail($id);
        DeleteFlow::delete($flow);
        
        return redirect()->route('ipaas.flows.index')
            ->with('success', 'Flow deleted successfully');
    }
}
```

#### ❌ ANTI-PATTERN: Dual-Behavior Methods

**DO NOT** create methods that have different behavior based on parameters:

```php
// ❌ BAD: create() does two different things
public function create($id = null)
{
    if ($id) {
        // Shows edit form (should be edit() method)
        $flow = Flow::findOrFail($id);
        return view('flows.edit', compact('flow'));
    } else {
        // Creates resource (should be store() method)
        $flow = Flow::create(['name' => 'New Flow']);
        return redirect()->route('flows.create', ['id' => $flow->id]);
    }
}
```

**Why it's bad:**
1. Violates Single Responsibility Principle
2. Confuses developers (especially new team members)
3. Makes testing more complex
4. Breaks Laravel conventions
5. Difficult to maintain routes

#### Refactoring Dual-Behavior Methods

When you encounter a dual-behavior method:

**Step 1**: Extract creation logic to `store()`
```php
public function store(FlowRequest $request)
{
    $flow = CreateFlow::create($request->validated());
    return redirect()->route('flows.edit', $flow->id);
}
```

**Step 2**: Extract edit form logic to `edit()`
```php
public function edit($id)
{
    $flow = Flow::findOrFail($id);
    return view('flows.edit', compact('flow'));
}
```

**Step 3**: Update routes with backward compatibility
```php
// New standard routes (via paramResource)
Route::paramResource('flows', FlowsController::class);

// Backward compatibility route (temporary)
Route::get('flows/create/{id?}', function($id = null) {
    if ($id) {
        return app()->make(FlowsController::class)->edit($id);
    }
    return app()->make(FlowsController::class)->create();
})->name('flows.create.legacy');
```

**Step 4**: Plan frontend migration and document
```php
// TODO: Migrate frontend to use standard routes:
// - GET /flows/create → empty form
// - POST /flows → create flow
// - GET /flows/{id}/edit → edit form
// - PUT/PATCH /flows/{id} → update flow
```

#### Route Registration

Use `Route::paramResource()` for automatic RESTful route generation:

```php
// routes/tenant.php
Route::paramResource('flows', FlowsController::class);
// This creates all 7 standard RESTful routes automatically
```

For custom routes, group them separately with clear comments:

```php
Route::prefix('flows')->group(function () {
    // ⚠️ BACKWARD COMPATIBILITY (deprecated)
    Route::get('create/{id?}', [FlowsController::class, 'legacyCreate'])
        ->name('flows.create.legacy');
    
    // ✅ Custom business logic routes
    Route::post('{id}/duplicate', [FlowsController::class, 'duplicate'])
        ->name('flows.duplicate');
});
```

#### FormRequest Integration

All `store()` and `update()` methods **MUST** use FormRequests:

```php
// ✅ CORRECT
public function store(FlowRequest $request)
{
    $validated = $request->validated();
    $flow = CreateFlow::create($validated);
    // ...
}

// ❌ WRONG
public function store(Request $request)
{
    $flow = CreateFlow::create($request->all()); // Mass assignment vulnerability!
    // ...
}
```

#### Response Patterns

**Web Routes (HTML responses):**
```php
// Success with redirect
return redirect()->route('flows.index')
    ->with('success', 'Flow created successfully');

// Error with redirect
return redirect()->back()
    ->with('error', 'Validation failed')
    ->withInput();
```

**API Routes (JSON responses):**
```php
// Success
return response()->json([
    'success' => true,
    'record' => $flow,
    'message' => 'Flow updated successfully'
], 200);

// Error
return response()->json([
    'success' => false,
    'message' => 'Flow not found'
], 404);
```

#### Integration with DDD

RESTful Controllers are part of the **Application Layer** in DDD:

```
┌─────────────────────────────────────────────┐
│ Application Layer (Controllers)             │
│ - Handles HTTP concerns                     │
│ - Uses FormRequests for validation          │
│ - Calls Domain Actions                      │
│ - Transforms models → HTTP responses        │
└──────────────────┬──────────────────────────┘
                   │ calls
                   ▼
┌─────────────────────────────────────────────┐
│ Domain Layer (Actions)                      │
│ - Pure business logic                       │
│ - Returns models or throws exceptions       │
│ - No HTTP knowledge                         │
└─────────────────────────────────────────────┘
```

**Key Principles:**
1. **Separation of Concerns**: HTTP logic stays in controllers, business logic in Domain Actions
2. **Testability**: Domain Actions can be tested without HTTP context
3. **Reusability**: Same Action works in controllers, commands, jobs, etc.
4. **Consistency**: Standard Laravel patterns make codebase predictable

#### Exceptions to the Rule

**Only acceptable exceptions:**
1. **Visual builders**: Auto-creating blank resources to enable drag-and-drop interfaces
2. **Wizards**: Multi-step forms where initial state must be persisted
3. **Drafts**: Systems that auto-save incomplete data

**In these cases:**
- Document the exception clearly in comments
- Explain why standard RESTful pattern doesn't fit
- Provide migration path to standard pattern if possible

---

