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:
php artisan make:model Product -mcr2. Migración
database/migrations/xxxx_create_products_table.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
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
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
use App\Http\Controllers\ProductController;
use Illuminate\Support\Facades\Route;
Route::resource('products', ProductController::class);routes/api.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:
@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>
@endsectionresources/views/products/create.blade.php:
@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>
@endsection7. Ejecutar Migración
php artisan migrate8. Probar las Rutas
Web:
GET /products- Lista de productosGET /products/create- Formulario de creaciónPOST /products- Crear productoGET /products/{id}- Ver productoGET /products/{id}/edit- Editar productoPUT/PATCH /products/{id}- Actualizar productoDELETE /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
# 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/1Este 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
Publicar un comentario