11-Laravel tanto para web como para API al mismo tiempo.

 Te explico cómo crear un CRUD completo en Laravel tanto para web como para API al mismo tiempo.

1. Configuración Inicial

Primero, crea el modelo con migración y controlador:

bash
php artisan make:model Product -mcr

2. Migración

database/migrations/xxxx_create_products_table.php:

php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->text('description')->nullable();
            $table->decimal('price', 10, 2);
            $table->integer('stock');
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};

3. Modelo

app/Models/Product.php:

php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use HasFactory;

    protected $fillable = [
        'name',
        'description',
        'price',
        'stock'
    ];

    protected $casts = [
        'price' => 'decimal:2',
    ];
}

4. Controlador Unificado (Web + API)

app/Http/Controllers/ProductController.php:

php
<?php

namespace App\Http\Controllers;

use App\Models\Product;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\View\View;

class ProductController extends Controller
{
    // WEB: Mostrar lista de productos
    public function index(): View
    {
        $products = Product::latest()->paginate(10);
        return view('products.index', compact('products'));
    }

    // WEB: Mostrar formulario de creación
    public function create(): View
    {
        return view('products.create');
    }

    // WEB/API: Almacenar nuevo producto
    public function store(Request $request)
    {
        $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);

        if ($request->wantsJson()) {
            return response()->json($product, 201);
        }

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

    // WEB: Mostrar producto específico
    public function show(Product $product): View
    {
        return view('products.show', compact('product'));
    }

    // WEB: Mostrar formulario de edición
    public function edit(Product $product): View
    {
        return view('products.edit', compact('product'));
    }

    // WEB/API: Actualizar producto
    public function update(Request $request, Product $product)
    {
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'description' => 'nullable|string',
            'price' => 'required|numeric|min:0',
            'stock' => 'required|integer|min:0'
        ]);

        $product->update($validated);

        if ($request->wantsJson()) {
            return response()->json($product);
        }

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

    // WEB/API: Eliminar producto
    public function destroy(Request $request, Product $product)
    {
        $product->delete();

        if ($request->wantsJson()) {
            return response()->json(null, 204);
        }

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

    // API: Métodos específicos para API
    public function apiIndex(): JsonResponse
    {
        $products = Product::all();
        return response()->json($products);
    }

    public function apiShow(Product $product): JsonResponse
    {
        return response()->json($product);
    }
}

5. Rutas

routes/web.php:

php
<?php

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

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

routes/api.php:

php
<?php

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

Route::apiResource('products', ProductController::class);
Route::get('products', [ProductController::class, 'apiIndex']);
Route::get('products/{product}', [ProductController::class, 'apiShow']);

6. Vistas (Web)

resources/views/products/index.blade.php:

blade
@extends('layouts.app')

@section('content')
<div class="container">
    <h1>Productos</h1>
    <a href="{{ route('products.create') }}" class="btn btn-primary mb-3">Nuevo Producto</a>
    
    @if(session('success'))
        <div class="alert alert-success">{{ session('success') }}</div>
    @endif

    <table class="table table-striped">
        <thead>
            <tr>
                <th>ID</th>
                <th>Nombre</th>
                <th>Precio</th>
                <th>Stock</th>
                <th>Acciones</th>
            </tr>
        </thead>
        <tbody>
            @foreach($products as $product)
            <tr>
                <td>{{ $product->id }}</td>
                <td>{{ $product->name }}</td>
                <td>${{ number_format($product->price, 2) }}</td>
                <td>{{ $product->stock }}</td>
                <td>
                    <a href="{{ route('products.show', $product) }}" class="btn btn-info btn-sm">Ver</a>
                    <a href="{{ route('products.edit', $product) }}" class="btn btn-warning btn-sm">Editar</a>
                    <form action="{{ route('products.destroy', $product) }}" method="POST" class="d-inline">
                        @csrf
                        @method('DELETE')
                        <button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('¿Eliminar?')">Eliminar</button>
                    </form>
                </td>
            </tr>
            @endforeach
        </tbody>
    </table>
    
    {{ $products->links() }}
</div>
@endsection

resources/views/products/create.blade.php:

blade
@extends('layouts.app')

@section('content')
<div class="container">
    <h1>Crear Producto</h1>
    <form action="{{ route('products.store') }}" method="POST">
        @csrf
        <div class="mb-3">
            <label for="name" class="form-label">Nombre</label>
            <input type="text" class="form-control" id="name" name="name" required>
        </div>
        <div class="mb-3">
            <label for="description" class="form-label">Descripción</label>
            <textarea class="form-control" id="description" name="description" rows="3"></textarea>
        </div>
        <div class="mb-3">
            <label for="price" class="form-label">Precio</label>
            <input type="number" step="0.01" class="form-control" id="price" name="price" required>
        </div>
        <div class="mb-3">
            <label for="stock" class="form-label">Stock</label>
            <input type="number" class="form-control" id="stock" name="stock" required>
        </div>
        <button type="submit" class="btn btn-primary">Crear</button>
        <a href="{{ route('products.index') }}" class="btn btn-secondary">Cancelar</a>
    </form>
</div>
@endsection

7. Ejecutar Migración

bash
php artisan migrate

8. Probar las Rutas

Web:

  • GET /products - Lista de productos

  • GET /products/create - Formulario de creación

  • POST /products - Crear producto

  • GET /products/{id} - Ver producto

  • GET /products/{id}/edit - Editar producto

  • PUT/PATCH /products/{id} - Actualizar producto

  • DELETE /products/{id} - Eliminar producto

API:

  • GET /api/products - Lista de productos (JSON)

  • POST /api/products - Crear producto (JSON)

  • GET /api/products/{id} - Ver producto (JSON)

  • PUT/PATCH /api/products/{id} - Actualizar producto (JSON)

  • DELETE /api/products/{id} - Eliminar producto

9. Ejemplo de Uso con API

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

# Listar productos
curl http://localhost:8000/api/products

# Actualizar producto
curl -X PUT http://localhost:8000/api/products/1 \
  -H "Content-Type: application/json" \
  -d '{"name": "Laptop Pro", "price": 1299.99, "stock": 5}'

# Eliminar producto
curl -X DELETE http://localhost:8000/api/products/1

Este enfoque te permite tener un CRUD completo tanto para interfaz web como para API RESTful usando el mismo controlador, optimizando el código y manteniendo la consistencia entre ambas interfaces

Comentarios

Entradas más populares de este blog

8-Creación de una API RESTful con Laravel

02 -Rutas en Laravel

3-Rutas