12-crear controladores separados para API y Web

 crear controladores separados para API y Web. Te explico por qué y te muestro cómo hacerlo:

¿Por qué separar los controladores?

  1. Separación de responsabilidades: Cada controlador tiene un propósito específico

  2. Mantenimiento más fácil: Cambios en API no afectan la web y viceversa

  3. Diferentes lógicas: API necesita manejo de JSON, autenticación diferente, etc.

  4. Mejor organización: Código más limpio y escalable

Estructura recomendada

bash
# Controlador para Web
php artisan make:controller Web/ProductController --resource --model=Product

# Controlador para API  
php artisan make:controller Api/ProductController --resource --model=Product

1. Controlador para API

app/Http/Controllers/Api/ProductController.php:

php
<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Product;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class ProductController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index(): JsonResponse
    {
        $products = Product::all();
        
        return response()->json([
            'success' => true,
            'data' => $products,
            'message' => 'Products retrieved successfully'
        ]);
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'description' => 'nullable|string',
            'price' => 'required|numeric|min:0',
            'stock' => 'required|integer|min:0'
        ]);

        $product = Product::create($validated);

        return response()->json([
            'success' => true,
            'data' => $product,
            'message' => 'Product created successfully'
        ], 201);
    }

    /**
     * Display the specified resource.
     */
    public function show(Product $product): JsonResponse
    {
        return response()->json([
            'success' => true,
            'data' => $product,
            'message' => 'Product retrieved successfully'
        ]);
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, Product $product): JsonResponse
    {
        $validated = $request->validate([
            'name' => 'sometimes|required|string|max:255',
            'description' => 'nullable|string',
            'price' => 'sometimes|required|numeric|min:0',
            'stock' => 'sometimes|required|integer|min:0'
        ]);

        $product->update($validated);

        return response()->json([
            'success' => true,
            'data' => $product,
            'message' => 'Product updated successfully'
        ]);
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(Product $product): JsonResponse
    {
        $product->delete();

        return response()->json([
            'success' => true,
            'message' => 'Product deleted successfully'
        ], 204);
    }
}

2. Controlador para Web

app/Http/Controllers/Web/ProductController.php:

php
<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\Product;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class ProductController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index(): View
    {
        $products = Product::latest()->paginate(10);
        return view('products.index', compact('products'));
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create(): View
    {
        return view('products.create');
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'description' => 'nullable|string',
            'price' => 'required|numeric|min:0',
            'stock' => 'required|integer|min:0'
        ]);

        Product::create($validated);

        return redirect()->route('products.index')
            ->with('success', 'Producto creado exitosamente.');
    }

    /**
     * Display the specified resource.
     */
    public function show(Product $product): View
    {
        return view('products.show', compact('product'));
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(Product $product): View
    {
        return view('products.edit', compact('product'));
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, Product $product): RedirectResponse
    {
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'description' => 'nullable|string',
            'price' => 'required|numeric|min:0',
            'stock' => 'required|integer|min:0'
        ]);

        $product->update($validated);

        return redirect()->route('products.index')
            ->with('success', 'Producto actualizado exitosamente.');
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(Product $product): RedirectResponse
    {
        $product->delete();

        return redirect()->route('products.index')
            ->with('success', 'Producto eliminado exitosamente.');
    }
}

3. Rutas separadas

routes/web.php:

php
<?php

use App\Http\Controllers\Web\ProductController;
use Illuminate\Support\Facades\Route;

Route::resource('products', ProductController::class);

routes/api.php:

php
<?php

use App\Http\Controllers\Api\ProductController;
use Illuminate\Support\Facades\Route;

Route::apiResource('products', ProductController::class);

4. Form Request para validación (Opcional pero recomendado)

Para mejor organización, puedes crear Form Requests:

bash
php artisan make:request Api/StoreProductRequest
php artisan make:request Api/UpdateProductRequest

app/Http/Requests/Api/StoreProductRequest.php:

php
<?php

namespace App\Http\Requests\Api;

use Illuminate\Foundation\Http\FormRequest;

class StoreProductRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'description' => 'nullable|string',
            'price' => 'required|numeric|min:0',
            'stock' => 'required|integer|min:0'
        ];
    }
}

Y luego usarlo en el controlador API:

php
public function store(StoreProductRequest $request): JsonResponse
{
    $validated = $request->validated();
    $product = Product::create($validated);
    
    return response()->json([
        'success' => true,
        'data' => $product,
        'message' => 'Product created successfully'
    ], 201);
}

5. Ventajas de esta estructura

  1. Separación clara: API y Web completamente independientes

  2. Mantenimiento fácil: Cambios en uno no afectan al otro

  3. Escalabilidad: Puedes agregar características específicas para cada uno

  4. Testing más fácil: Tests unitarios separados

  5. Equipos paralelos: Diferentes equipos pueden trabajar simultáneamente

6. Ejemplo de uso con API

bash
# Crear producto
curl -X POST http://localhost:8000/api/products \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "name": "Laptop Gaming",
    "price": 1299.99,
    "stock": 15,
    "description": "High performance gaming laptop"
  }'

# Respuesta esperada:
{
  "success": true,
  "data": {
    "id": 1,
    "name": "Laptop Gaming",
    "description": "High performance gaming laptop",
    "price": "1299.99",
    "stock": 15,
    "created_at": "2023-12-20T10:00:00.000000Z",
    "updated_at": "2023-12-20T10:00:00.000000Z"
  },
  "message": "Product created successfully"
}

Esta estructura es la mejor práctica para proyectos Laravel que necesitan tanto API como interfaz web

Comentarios

Entradas más populares de este blog

8-Creación de una API RESTful con Laravel

02 -Rutas en Laravel

3-Rutas