Sistema CRUD con API REST y Web en Laravel 12
Sistema CRUD con API REST y Web en Laravel 12
Basado en el tutorial que me pasaste, aquí tienes una versión extendida que incluye tanto la interfaz web como una API REST en el mismo proyecto.
📚 Índice
Configuración inicial
Migración, Seeder y Modelo
Creación de Controladores
Definición de Rutas
Creación de Vistas Web
Pruebas de la API
Comandos Útiles
1️⃣ Configuración inicial
# Verifica tu versión de PHP (mínimo 8.2 para Laravel 12)
php -v
# Verifica tu versión de Composer
composer -V
# Crea un nuevo proyecto Laravel
composer create-project laravel/laravel pasteles-app
cd pasteles-app
# Configura tu archivo .env con los datos de tu base de datos
# .env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=pasteles_db
DB_USERNAME=root
DB_PASSWORD=2️⃣ Migración, Seeder y Modelo
2.1 Crear la migración
php artisan make:migration create_pasteles_tabledatabase/migrations/[timestamp]_create_pasteles_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('pasteles', function (Blueprint $table) {
$table->id();
$table->string('nombre', 100);
$table->enum('sabor', ['chocolate', 'vainilla', 'cheesecake', 'fresa', 'coco']);
$table->decimal('precio', 8, 2)->nullable();
$table->boolean('disponible')->default(true);
$table->text('descripcion')->nullable();
$table->timestamps();
$table->softDeletes(); // Eliminación lógica
});
}
public function down(): void
{
Schema::dropIfExists('pasteles');
}
};2.2 Crear el Modelo
php artisan make:model Pastelapp/Models/Pastel.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Pastel extends Model
{
use HasFactory, SoftDeletes;
protected $table = 'pasteles';
protected $fillable = [
'nombre',
'sabor',
'precio',
'disponible',
'descripcion'
];
protected $casts = [
'precio' => 'decimal:2',
'disponible' => 'boolean',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
// Scopes para consultas reutilizables
public function scopeDisponibles($query)
{
return $query->where('disponible', true);
}
public function scopeBySabor($query, $sabor)
{
return $query->where('sabor', $sabor);
}
}2.3 Crear el Factory (para datos de prueba)
php artisan make:factory PastelFactorydatabase/factories/PastelFactory.php
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class PastelFactory extends Factory
{
public function definition(): array
{
$sabores = ['chocolate', 'vainilla', 'cheesecake', 'fresa', 'coco'];
return [
'nombre' => 'Pastel ' . $this->faker->unique()->firstName() . ' ' . $this->faker->lastName(),
'sabor' => $this->faker->randomElement($sabores),
'precio' => $this->faker->randomFloat(2, 50, 500),
'disponible' => $this->faker->boolean(80),
'descripcion' => $this->faker->sentence(10),
'created_at' => $this->faker->dateTimeBetween('-1 year', 'now'),
'updated_at' => $this->faker->dateTimeBetween('-1 month', 'now'),
];
}
}2.4 Crear el Seeder
php artisan make:seeder PastelSeederdatabase/seeders/PastelSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\Pastel;
class PastelSeeder extends Seeder
{
public function run(): void
{
Pastel::factory(50)->create();
}
}database/seeders/DatabaseSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
$this->call([
PastelSeeder::class,
]);
}
}2.5 Ejecutar migraciones y seeders
php artisan migrate --seed3️⃣ Creación de Controladores
🎯 ¡IMPORTANTE! Vamos a crear DOS controladores:
PastelController - Para la WEB (vistas HTML)
PastelApiController - Para la API REST (devuelve JSON)
# Controlador para la WEB
php artisan make:controller PastelController --resource
# Controlador para la API
php artisan make:controller PastelApiController3.1 Controlador WEB (PastelController)
app/Http/Controllers/PastelController.php
<?php
namespace App\Http\Controllers;
use App\Models\Pastel;
use Illuminate\Http\Request;
class PastelController extends Controller
{
/**
* Muestra la lista de pasteles (Vista web)
*/
public function index()
{
$pasteles = Pastel::all();
return view('pasteles.index', compact('pasteles'));
}
/**
* Muestra el formulario para crear un nuevo pastel
*/
public function create()
{
return view('pasteles.create');
}
/**
* Almacena un nuevo pastel (Desde el formulario web)
*/
public function store(Request $request)
{
$validated = $request->validate([
'nombre' => 'required|string|max:100',
'sabor' => 'required|string|max:50',
'precio' => 'required|numeric|min:0',
'disponible' => 'nullable|boolean',
'descripcion' => 'nullable|string|max:1000',
]);
Pastel::create($validated);
return redirect()
->route('pasteles.index')
->with('success', '¡Pastel creado exitosamente!');
}
/**
* Muestra un pastel específico (Vista web)
*/
public function show(Pastel $pastel)
{
return view('pasteles.show', compact('pastel'));
}
/**
* Muestra el formulario para editar un pastel
*/
public function edit(Pastel $pastel)
{
return view('pasteles.edit', compact('pastel'));
}
/**
* Actualiza un pastel (Desde el formulario web)
*/
public function update(Request $request, Pastel $pastel)
{
$validated = $request->validate([
'nombre' => 'required|string|max:100',
'sabor' => 'required|string|max:50',
'precio' => 'required|numeric|min:0',
'disponible' => 'nullable|boolean',
'descripcion' => 'nullable|string|max:1000',
]);
$pastel->update($validated);
return redirect()
->route('pasteles.index')
->with('success', '¡Pastel actualizado exitosamente!');
}
/**
* Elimina un pastel (Eliminación lógica)
*/
public function destroy(Pastel $pastel)
{
$pastel->delete();
return redirect()
->route('pasteles.index')
->with('success', '¡Pastel eliminado exitosamente!');
}
}3.2 Controlador API (PastelApiController)
app/Http/Controllers/PastelApiController.php
<?php
namespace App\Http\Controllers;
use App\Models\Pastel;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
class PastelApiController extends Controller
{
/**
* GET /api/pasteles
* Lista todos los pasteles (Formato JSON)
*/
public function index()
{
$pasteles = Pastel::all();
return response()->json([
'success' => true,
'data' => $pasteles,
'message' => 'Lista de pasteles obtenida exitosamente'
], 200);
}
/**
* POST /api/pasteles
* Crea un nuevo pastel
*/
public function store(Request $request)
{
// Validación
$validated = $request->validate([
'nombre' => 'required|string|max:100',
'sabor' => ['required', Rule::in(['chocolate', 'vainilla', 'cheesecake', 'fresa', 'coco'])],
'precio' => 'required|numeric|min:0',
'disponible' => 'sometimes|boolean',
'descripcion' => 'nullable|string|max:1000',
]);
// Si no viene 'disponible', lo ponemos en true por defecto
if (!isset($validated['disponible'])) {
$validated['disponible'] = true;
}
$pastel = Pastel::create($validated);
return response()->json([
'success' => true,
'data' => $pastel,
'message' => 'Pastel creado exitosamente'
], 201);
}
/**
* GET /api/pasteles/{id}
* Muestra un pastel específico
*/
public function show($id)
{
$pastel = Pastel::find($id);
if (!$pastel) {
return response()->json([
'success' => false,
'message' => 'Pastel no encontrado'
], 404);
}
return response()->json([
'success' => true,
'data' => $pastel,
'message' => 'Pastel encontrado'
], 200);
}
/**
* PUT /api/pasteles/{id}
* Actualiza un pastel completo
*/
public function update(Request $request, $id)
{
$pastel = Pastel::find($id);
if (!$pastel) {
return response()->json([
'success' => false,
'message' => 'Pastel no encontrado'
], 404);
}
$validated = $request->validate([
'nombre' => 'sometimes|string|max:100',
'sabor' => ['sometimes', Rule::in(['chocolate', 'vainilla', 'cheesecake', 'fresa', 'coco'])],
'precio' => 'sometimes|numeric|min:0',
'disponible' => 'sometimes|boolean',
'descripcion' => 'nullable|string|max:1000',
]);
$pastel->update($validated);
return response()->json([
'success' => true,
'data' => $pastel->fresh(),
'message' => 'Pastel actualizado exitosamente'
], 200);
}
/**
* DELETE /api/pasteles/{id}
* Elimina un pastel (Eliminación lógica)
*/
public function destroy($id)
{
$pastel = Pastel::find($id);
if (!$pastel) {
return response()->json([
'success' => false,
'message' => 'Pastel no encontrado'
], 404);
}
$pastel->delete();
return response()->json([
'success' => true,
'message' => 'Pastel eliminado exitosamente'
], 200);
}
/**
* GET /api/pasteles/disponibles
* Obtiene solo los pasteles disponibles
*/
public function disponibles()
{
$pasteles = Pastel::disponibles()->get();
return response()->json([
'success' => true,
'data' => $pasteles,
'message' => 'Pasteles disponibles obtenidos'
], 200);
}
/**
* GET /api/pasteles/sabor/{sabor}
* Obtiene pasteles por sabor
*/
public function bySabor($sabor)
{
$pasteles = Pastel::bySabor($sabor)->get();
if ($pasteles->isEmpty()) {
return response()->json([
'success' => false,
'message' => 'No se encontraron pasteles del sabor ' . $sabor
], 404);
}
return response()->json([
'success' => true,
'data' => $pasteles,
'message' => 'Pasteles del sabor ' . $sabor . ' obtenidos'
], 200);
}
}4️⃣ Definición de Rutas
routes/web.php - Rutas para la WEB
<?php
use App\Http\Controllers\PastelController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return redirect()->route('pasteles.index');
});
// Rutas para la WEB - CRUD completo
Route::resource('pasteles', PastelController::class);routes/api.php - Rutas para la API REST
<?php
use App\Http\Controllers\PastelApiController;
use Illuminate\Support\Facades\Route;
// Todas las rutas de la API tienen el prefijo /api
// y devuelven respuestas en formato JSON
Route::get('/pasteles', [PastelApiController::class, 'index']); // GET /api/pasteles
Route::post('/pasteles', [PastelApiController::class, 'store']); // POST /api/pasteles
Route::get('/pasteles/{id}', [PastelApiController::class, 'show']); // GET /api/pasteles/1
Route::put('/pasteles/{id}', [PastelApiController::class, 'update']); // PUT /api/pasteles/1
Route::delete('/pasteles/{id}', [PastelApiController::class, 'destroy']); // DELETE /api/pasteles/1
// Rutas adicionales de la API
Route::get('/pasteles/disponibles', [PastelApiController::class, 'disponibles']); // GET /api/pasteles/disponibles
Route::get('/pasteles/sabor/{sabor}', [PastelApiController::class, 'bySabor']); // GET /api/pasteles/sabor/chocolate📌 Ver todas las rutas
php artisan route:list5️⃣ Creación de Vistas Web
5.1 Layout principal
resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@yield('title', 'Pastelería')</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="{{ route('pasteles.index') }}">🍰 Pastelería</a>
<a href="{{ route('pasteles.create') }}" class="btn btn-primary">+ Agregar Pastel</a>
</div>
</nav>
<div class="container mt-4">
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
{{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
@endif
@yield('content')
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>5.2 Vista Index (Lista de pasteles)
resources/views/pasteles/index.blade.php
@extends('layouts.app')
@section('title', 'Lista de Pasteles')
@section('content')
<h1 class="mb-4">📋 Lista de Pasteles</h1>
<div class="row">
@foreach($pasteles as $pastel)
<div class="col-md-4 mb-4">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">{{ $pastel->nombre }}</h5>
<p class="card-text">
<strong>Sabor:</strong> {{ $pastel->sabor }}<br>
<strong>Precio:</strong> ${{ number_format($pastel->precio, 2) }}<br>
<strong>Disponible:</strong>
<span class="badge {{ $pastel->disponible ? 'bg-success' : 'bg-danger' }}">
{{ $pastel->disponible ? 'Sí' : 'No' }}
</span>
</p>
<p class="card-text small">{{ Str::limit($pastel->descripcion, 100) }}</p>
</div>
<div class="card-footer bg-transparent">
<div class="btn-group w-100" role="group">
<a href="{{ route('pasteles.show', $pastel) }}" class="btn btn-info btn-sm">Ver</a>
<a href="{{ route('pasteles.edit', $pastel) }}" class="btn btn-warning btn-sm">Editar</a>
<form action="{{ route('pasteles.destroy', $pastel) }}" method="POST" class="d-inline">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('¿Eliminar este pastel?')">
Eliminar
</button>
</form>
</div>
</div>
</div>
</div>
@endforeach
</div>
@if($pasteles->isEmpty())
<div class="alert alert-info">
No hay pasteles registrados. <a href="{{ route('pasteles.create') }}">¡Crea el primero!</a>
</div>
@endif
@endsection5.3 Vista Create (Crear pastel)
resources/views/pasteles/create.blade.php
@extends('layouts.app')
@section('title', 'Crear Pastel')
@section('content')
<h1 class="mb-4">🍰 Crear Nuevo Pastel</h1>
<div class="card">
<div class="card-body">
<form action="{{ route('pasteles.store') }}" method="POST">
@csrf
<div class="mb-3">
<label for="nombre" class="form-label">Nombre</label>
<input type="text" class="form-control @error('nombre') is-invalid @enderror"
id="nombre" name="nombre" value="{{ old('nombre') }}" required>
@error('nombre')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="sabor" class="form-label">Sabor</label>
<select class="form-select @error('sabor') is-invalid @enderror"
id="sabor" name="sabor" required>
<option value="">Selecciona un sabor</option>
<option value="chocolate" {{ old('sabor') == 'chocolate' ? 'selected' : '' }}>Chocolate</option>
<option value="vainilla" {{ old('sabor') == 'vainilla' ? 'selected' : '' }}>Vainilla</option>
<option value="cheesecake" {{ old('sabor') == 'cheesecake' ? 'selected' : '' }}>Cheesecake</option>
<option value="fresa" {{ old('sabor') == 'fresa' ? 'selected' : '' }}>Fresa</option>
<option value="coco" {{ old('sabor') == 'coco' ? 'selected' : '' }}>Coco</option>
</select>
@error('sabor')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="precio" class="form-label">Precio</label>
<input type="number" step="0.01" class="form-control @error('precio') is-invalid @enderror"
id="precio" name="precio" value="{{ old('precio') }}" required>
@error('precio')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="disponible"
name="disponible" value="1" {{ old('disponible') ? 'checked' : '' }}>
<label class="form-check-label" for="disponible">Disponible</label>
</div>
</div>
<div class="mb-3">
<label for="descripcion" class="form-label">Descripción</label>
<textarea class="form-control @error('descripcion') is-invalid @enderror"
id="descripcion" name="descripcion" rows="3">{{ old('descripcion') }}</textarea>
@error('descripcion')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary">Guardar Pastel</button>
<a href="{{ route('pasteles.index') }}" class="btn btn-secondary">Cancelar</a>
</form>
</div>
</div>
@endsection5.4 Vista Show (Ver pastel)
resources/views/pasteles/show.blade.php
@extends('layouts.app')
@section('title', $pastel->nombre)
@section('content')
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>{{ $pastel->nombre }}</h1>
<div>
<a href="{{ route('pasteles.edit', $pastel) }}" class="btn btn-warning">Editar</a>
<a href="{{ route('pasteles.index') }}" class="btn btn-secondary">Volver</a>
</div>
</div>
<div class="card">
<div class="card-body">
<dl class="row">
<dt class="col-sm-3">ID</dt>
<dd class="col-sm-9">{{ $pastel->id }}</dd>
<dt class="col-sm-3">Nombre</dt>
<dd class="col-sm-9">{{ $pastel->nombre }}</dd>
<dt class="col-sm-3">Sabor</dt>
<dd class="col-sm-9">{{ $pastel->sabor }}</dd>
<dt class="col-sm-3">Precio</dt>
<dd class="col-sm-9">${{ number_format($pastel->precio, 2) }}</dd>
<dt class="col-sm-3">Disponible</dt>
<dd class="col-sm-9">
<span class="badge {{ $pastel->disponible ? 'bg-success' : 'bg-danger' }}">
{{ $pastel->disponible ? 'Sí' : 'No' }}
</span>
</dd>
<dt class="col-sm-3">Descripción</dt>
<dd class="col-sm-9">{{ $pastel->descripcion ?: 'Sin descripción' }}</dd>
<dt class="col-sm-3">Creado</dt>
<dd class="col-sm-9">{{ $pastel->created_at->format('d/m/Y H:i') }}</dd>
<dt class="col-sm-3">Actualizado</dt>
<dd class="col-sm-9">{{ $pastel->updated_at->format('d/m/Y H:i') }}</dd>
</dl>
</div>
</div>
@endsection5.5 Vista Edit (Editar pastel)
resources/views/pasteles/edit.blade.php
@extends('layouts.app')
@section('title', 'Editar ' . $pastel->nombre)
@section('content')
<h1 class="mb-4">✏️ Editar Pastel: {{ $pastel->nombre }}</h1>
<div class="card">
<div class="card-body">
<form action="{{ route('pasteles.update', $pastel) }}" method="POST">
@csrf
@method('PUT')
<div class="mb-3">
<label for="nombre" class="form-label">Nombre</label>
<input type="text" class="form-control @error('nombre') is-invalid @enderror"
id="nombre" name="nombre" value="{{ old('nombre', $pastel->nombre) }}" required>
@error('nombre')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="sabor" class="form-label">Sabor</label>
<select class="form-select @error('sabor') is-invalid @enderror"
id="sabor" name="sabor" required>
<option value="">Selecciona un sabor</option>
<option value="chocolate" {{ old('sabor', $pastel->sabor) == 'chocolate' ? 'selected' : '' }}>Chocolate</option>
<option value="vainilla" {{ old('sabor', $pastel->sabor) == 'vainilla' ? 'selected' : '' }}>Vainilla</option>
<option value="cheesecake" {{ old('sabor', $pastel->sabor) == 'cheesecake' ? 'selected' : '' }}>Cheesecake</option>
<option value="fresa" {{ old('sabor', $pastel->sabor) == 'fresa' ? 'selected' : '' }}>Fresa</option>
<option value="coco" {{ old('sabor', $pastel->sabor) == 'coco' ? 'selected' : '' }}>Coco</option>
</select>
@error('sabor')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="precio" class="form-label">Precio</label>
<input type="number" step="0.01" class="form-control @error('precio') is-invalid @enderror"
id="precio" name="precio" value="{{ old('precio', $pastel->precio) }}" required>
@error('precio')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="disponible"
name="disponible" value="1" {{ old('disponible', $pastel->disponible) ? 'checked' : '' }}>
<label class="form-check-label" for="disponible">Disponible</label>
</div>
</div>
<div class="mb-3">
<label for="descripcion" class="form-label">Descripción</label>
<textarea class="form-control @error('descripcion') is-invalid @enderror"
id="descripcion" name="descripcion" rows="3">{{ old('descripcion', $pastel->descripcion) }}</textarea>
@error('descripcion')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary">Actualizar Pastel</button>
<a href="{{ route('pasteles.index') }}" class="btn btn-secondary">Cancelar</a>
</form>
</div>
</div>
@endsection6️⃣ Pruebas de la API
6.1 Usando Postman o Insomnia
| Método | URL | Descripción |
|---|---|---|
| GET | http://localhost:8000/api/pasteles | Obtener todos los pasteles |
| GET | http://localhost:8000/api/pasteles/1 | Obtener un pastel específico |
| POST | http://localhost:8000/api/pasteles | Crear un pastel |
| PUT | http://localhost:8000/api/pasteles/1 | Actualizar un pastel |
| DELETE | http://localhost:8000/api/pasteles/1 | Eliminar un pastel |
| GET | http://localhost:8000/api/pasteles/disponibles | Obtener pasteles disponibles |
| GET | http://localhost:8000/api/pasteles/sabor/chocolate | Obtener pasteles por sabor |
6.2 Ejemplo de POST (Crear pastel)
Body (JSON):
{
"nombre": "Pastel de Chocolate",
"sabor": "chocolate",
"precio": 250.50,
"disponible": true,
"descripcion": "Delicioso pastel de chocolate con cobertura"
}Respuesta:
{
"success": true,
"data": {
"id": 51,
"nombre": "Pastel de Chocolate",
"sabor": "chocolate",
"precio": "250.50",
"disponible": true,
"descripcion": "Delicioso pastel de chocolate con cobertura",
"created_at": "2026-09-03T12:00:00.000000Z",
"updated_at": "2026-09-03T12:00:00.000000Z"
},
"message": "Pastel creado exitosamente"
}6.3 Probar la API desde la terminal con cURL
# Obtener todos los pasteles
curl http://localhost:8000/api/pasteles
# Obtener un pastel específico
curl http://localhost:8000/api/pasteles/1
# Crear un pastel
curl -X POST http://localhost:8000/api/pasteles \
-H "Content-Type: application/json" \
-d '{"nombre":"Pastel de Fresa","sabor":"fresa","precio":180.00,"disponible":true,"descripcion":"Delicioso pastel de fresa"}'
# Actualizar un pastel
curl -X PUT http://localhost:8000/api/pasteles/1 \
-H "Content-Type: application/json" \
-d '{"precio":300.00}'
# Eliminar un pastel
curl -X DELETE http://localhost:8000/api/pasteles/1
# Obtener pasteles disponibles
curl http://localhost:8000/api/pasteles/disponibles
# Obtener pasteles por sabor
curl http://localhost:8000/api/pasteles/sabor/chocolate7️⃣ Comandos Útiles
7.1 Comandos de migración
| Comando | ¿Qué hace? |
|---|---|
php artisan migrate | Ejecuta todas las migraciones pendientes |
php artisan migrate:fresh | Elimina todas las tablas y vuelve a ejecutar migraciones |
php artisan migrate:fresh --seed | Reinicia y ejecuta los seeders |
php artisan migrate:rollback | Revierte el último lote de migraciones |
php artisan migrate:status | Muestra el estado de todas las migraciones |
7.2 Comandos de limpieza y optimización
# Limpiar caché
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
# Optimizar para producción
php artisan optimize
# Ver todas las rutas
php artisan route:list7.3 Iniciar el servidor
# Iniciar servidor de desarrollo
php artisan serve
# El servidor estará disponible en:
# http://localhost:8000/pasteles (WEB)
# http://localhost:8000/api/pasteles (API)🎯 Resumen de URLs
| URL | Tipo | Descripción |
|---|---|---|
/pasteles | WEB | Lista de pasteles |
/pasteles/create | WEB | Formulario para crear |
/pasteles/{id} | WEB | Ver detalle |
/pasteles/{id}/edit | WEB | Formulario para editar |
/api/pasteles | API | Lista de pasteles (JSON) |
/api/pasteles | API | Crear pastel (POST) |
/api/pasteles/{id} | API | Ver/Actualizar/Eliminar |
/api/pasteles/disponibles | API | Pasteles disponibles |
/api/pasteles/sabor/{sabor} | API | Pasteles por sabor |
📌 Commits para Git
# Crear modelo y migración
git add database/migrations/ app/Models/Pastel.php
git commit -m "feat: crear modelo y migración de pasteles"
# Crear factory y seeder
git add database/factories/ database/seeders/
git commit -m "feat: crear factory y seeder para pasteles"
# Crear controladores
git add app/Http/Controllers/PastelController.php
git add app/Http/Controllers/PastelApiController.php
git commit -m "feat: crear controladores web y api para pasteles"
# Crear rutas
git add routes/web.php routes/api.php
git commit -m "feat: definir rutas web y api para pasteles"
# Crear vistas
git add resources/views/pasteles/
git commit -m "feat: crear vistas para CRUD de pasteles"¡Y eso es todo! Ahora tienes un sistema completo que funciona tanto como aplicación web como API REST en Laravel 12
Comentarios
Publicar un comentario