Tutorial CRUD en Laravel 12 - Sistema de Pasteles-index
馃幆 Objetivo
Crear un sistema CRUD completo para una tabla de pasteles en Laravel 12, usando las mejores pr谩cticas y herramientas modernas.
Nota importante sobre Namespaces: En Laravel, cada clase vive en un namespace (una "carpeta l贸gica"). Para usar una clase de otro namespace, debes importarla con use al inicio del archivo. Esto NO es include ni require; solo crea un alias. Si olvidas el use, PHP buscar谩 la clase en el namespace actual y lanzar谩 "Class not found". Lo iremos se帽alando en cada paso.
馃搵 脥ndice del Tutorial
Configuraci贸n inicial del proyecto
Creaci贸n de la migraci贸n
Creaci贸n del modelo (¡PASO CLAVE!)
Creaci贸n del Factory
Creaci贸n del Seeder
Ejecutar migraciones y seeders
Probar con Laravel Tinker
Creaci贸n del controlador
Creaci贸n de las vistas
Definici贸n de rutas
Pruebas finales
脥ndice
1️⃣ Configuraci贸n inicial
Requisitos previos
bash
# Verifica PHP (m铆nimo 8.2)
php -v
# Verifica Composer
composer -V
# Crear proyecto
composer create-project laravel/laravel pasteles-app
cd pasteles-app
# Configurar .env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=pasteles_db
DB_USERNAME=root
DB_PASSWORD=
Si tu servicio PHP se llama app, entonces ejecuta:
text
docker compose exec app php artisan make:controller PastelController
Esto hace exactamente lo que quer铆as, pero ejecuta PHP dentro del contenedor.
Si tu contenedor realmente se llama laravel-docker, puedes hacer:
text
docker exec -it laravel-app-1 /bin/bash
docker exec -it laravel-docker /bin/bash
Una vez dentro, ver谩s algo parecido a:
text
root@abc123:/var/www/html#
Entonces ya puedes ejecutar:
text
php artisan make:controller PastelController
Con esto ya tienes el flujo completo: crear la migraci贸n → ejecutarla → y saber c贸mo deshacer o reiniciar cuando sea necesario
2️⃣ Creaci贸n de la migraci贸n
Las migraciones en Laravel son como un control de versiones para tu base de datos. Permiten definir y modificar la estructura de las tablas (crear, alterar o eliminar columnas) usando c贸digo PHP en lugar de SQL manual.
bash
php artisan make:migration create_pasteles_table
Edita el archivo creado en database/migrations/:
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);
$table->boolean('disponible')->default(true);
$table->text('descripcion')->nullable();
$table->timestamps();
$table->softDeletes(); // Eliminaci贸n l贸gica
});
}
public function down(): void
{
Schema::dropIfExists('pasteles');
}
};
馃 Namespaces aqu铆: f铆jate que Migration, Blueprint y Schema est谩n en namespaces distintos (Illuminate\Database\Migrations, Illuminate\Database\Schema, Illuminate\Support\Facades). Por eso van los tres use arriba. Si quitas uno, ver谩s: "Class 'Blueprint' not found". Las clases Migration y Blueprint no van con use solo por est茅tica: son obligatorios.
Comando para ejecutar las migraciones
php artisan migrateFlujo recomendado completo
# 1. Crear la migraci贸n
php artisan make:migration create_pasteles_table
# 2. Editar el archivo en database/migrations/ (lo que ya hiciste)
# 3. Ejecutar
php artisan migrate
# 4. (Opcional) Verificar en tu BD o con:
php artisan migrate:status
# 1. Crear la migraci贸n php artisan make:migration create_pasteles_table # 2. Editar el archivo en database/migrations/ (lo que ya hiciste) # 3. Ejecutar php artisan migrate # 4. (Opcional) Verificar en tu BD o con: php artisan migrate:status
Ejecuci贸n de las migraciones
Una vez definida la migraci贸n, estos son los comandos que usar谩s en el d铆a a d铆a:
Probar con Laravel Tinker (¡MANUAL DETALLADO!)
馃敼 Abrir Tinker
bash
php artisan tinker
馃敼 Verificar que la tabla existe
php
>>> Schema::hasTable('pasteles')
=> true
馃敼 Ver los campos de la tabla
php
>>> Schema::getColumnListing('pasteles')
=> [
"id",
"nombre",
"sabor",
"precio",
"disponible",
"descripcion",
"created_at",
"updated_at",
"deleted_at",
]
// 4. Salir de Tinker
>>> exit
3️⃣ Creaci贸n del modelo (¡PASO CLAVE!)
El modelo es lo que conecta tu c贸digo con la base de datos.
php artisan make:model Pastel
Edita app/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'
];
// Esto es 煤til para que Laravel trate los tipos de datos correctamente
protected $casts = [
'precio' => 'decimal:2',
'disponible' => 'boolean',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
}
¿Por qu茅 es importante tener el modelo primero?
Laravel necesita saber qu茅 campos tiene tu tabla
Los factories y seeders dependen del modelo
Sin modelo, no puedes usar Tinker para probar
馃 Dos tipos de use (¡no los confundas!):
use Illuminate\...\Model;→ importa una clase. Va antes declass.use HasFactory, SoftDeletes;→ aplica traits dentro de la clase. Va despu茅s declass.
Si pones un trait arriba, error. Si pones una clase abajo, error.
4️⃣ Creaci贸n del Factory
Factory: es la receta/plantilla para generar datos falsos de un modelo (ej. usuarios, posts). Define c贸mo se crean esos datos.
User::factory()->create(); // crea 1 usuario
User::factory()->count(50)->create(); // crea 50 usuarios
Ventajas:
Ahorran tiempo al escribir pruebas.
Pueblan la BD con datos realistas sin escribirlos a mano.
Se combinan con los Seeders para llenar la base de datos de desarrollo.
bash
php artisan make:factory PastelFactory
Edita database/factories/PastelFactory.php:
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\Pastel;
class PastelFactory extends Factory
{
protected $model = Pastel::class;
public function definition(): array
{
$sabores = ['chocolate', 'vainilla', 'cheesecake', 'fresa', 'coco'];
return [
'nombre' => 'Pastel ' . $this->faker->firstName() . ' ' . $this->faker->lastName(),
'sabor' => $this->faker->randomElement($sabores),
'precio' => $this->faker->randomFloat(2, 50, 500),
'disponible' => $this->faker->boolean(80),
'descripcion' => $this->faker->paragraph(2),
'created_at' => $this->faker->dateTimeBetween('-1 year', 'now'),
'updated_at' => $this->faker->dateTimeBetween('-1 month', 'now'),
];
}}
Namespaces aqu铆: el factory vive en Database\Factories, pero el modelo vive en App\Models. Son namespaces distintos → necesitas use App\Models\Pastel;. Si lo olvidas, PHP buscar铆a Database\Factories\Pastel y fallar铆a.
5️⃣ Creaci贸n del Seeder
Los Seeder es el ejecutable que llena la base de datos con datos (iniciales, de prueba o demo). Puede usar factories o datos fijos .
Insertan registros en las tablas (usuarios, roles, categor铆as, etc.).
Se ejecutan con php artisan db:seed.
Suelen usar Factories para generar datos falsos.
bash
php artisan make:seeder PastelSeeder
Edita database/seeders/PastelSeeder.php:
php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\Pastel;
class PastelSeeder extends Seeder
{
public function run(): void
{
// Crear 30 pasteles falsos usando el factory
Pastel::factory(30)->create();
// Tambi茅n podemos crear uno espec铆fico
// Pastel::create([
// 'nombre' => 'Pastel Especial de Chocolate',
// 'sabor' => 'chocolate',
// 'precio' => 350.00,
// 'disponible' => true,
// 'descripcion' => 'Nuestro pastel m谩s popular, hecho con //chocolate belga'
// ]);
}
}
namespace de tu archivo, necesitas use. Aqu铆 Seeder est谩 en Illuminate\Database\Seeder (importado) y Pastel est谩 en App\Models (importado).6️⃣ Registrar el Seeder en DatabaseSeeder
Edita database/seeders/DatabaseSeeder.php:
Opci贸n 1: Registrarlo junto con lo que ya tienes
<?php
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
use WithoutModelEvents;
/**
* Seed the application's database.
*/
public function run(): void
{
// User::factory(10)->create();
User::factory()->create([
'name' => 'Test User',
'email' => 'test@example.com',
]);
// 馃憞 Registras tu seeder aqu铆
$this->call([
PastelSeeder::class,
]);
}
}馃挕 Como DatabaseSeeder y PastelSeeder est谩n en el mismo namespace (Database\Seeders), no necesitas importarlo con use. Solo escr铆belo tal cual: PastelSeeder::class.
<?php
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
use WithoutModelEvents;
/**
* Seed the application's database.
*/
public function run(): void
{
// User::factory(10)->create();
User::factory()->create([
'name' => 'Test User',
'email' => 'test@example.com',
]);
// 馃憞 Registras tu seeder aqu铆
$this->call([
PastelSeeder::class,
]);
}
}馃挕 Como DatabaseSeeder y PastelSeeder est谩n en el mismo namespace (Database\Seeders), no necesitas importarlo con use. Solo escr铆belo tal cual: PastelSeeder::class.
Opci贸n 2: Si tienes varios seeders
$this->call([
UserSeeder::class,
PastelSeeder::class,
ClienteSeeder::class,
]);
$this->call([
UserSeeder::class,
PastelSeeder::class,
ClienteSeeder::class,
]);El orden importa: se ejecutan en el orden en que los listas. 脷til si un seeder depende de otro (por ejemplo, si PastelSeeder necesitara usuarios ya creados).
Esto es lo m谩s com煤n cuando tienes varios seeders.Ojo: el orden importa si hay relaciones. Por ejemplo, si PastelSeeder depende de usuarios creados por UserSeeder, primero debe ir UserSeeder.
7️⃣ Ejecutar migraciones y seeders
bash
# Luego ejecuta los seeders para llenar con datos falsos
php artisan db:seed
# Si quieres hacer todo de una vez (reinicia desde cero)
php artisan migrate:fresh --seedphp artisan migrate:fresh --seed
8️⃣ Probar con Laravel Tinker (¡MANUAL DETALLADO!)
馃敼 Abrir Tinker
bash
php artisan tinker
馃敼 Ver todos los pasteles creados
php
>>> use App\Models\Pastel;
>>> $pasteles = Pastel::all()
>>> $pasteles->count()
=> 31 // 30 del factory + 1 creado manualmente
// 4. Salir de Tinker
>>> exit
9️⃣ Creaci贸n del controlador
bash
php artisan make:controller PastelController
Temporalmente en tu controlador:
php
<?php
namespace App\Http\Controllers;
use App\Models\Pastel;
use Illuminate\Http\Request;
class PastelController extends Controller
{
public function index()
{
$pasteles = Pastel::all();
// Verificar que los datos existen antes de pasar a la vista
dd($pasteles); // Esto mostrar谩 los datos y se detendr谩
return view('pasteles.index', compact('pasteles'));
}}
Y creas resources/views/pasteles/index.blade.php:
use App\Http\Controllers\PastelController;
Route::get('/pasteles', [PastelController::class, 'index'])->name('pasteles.index');
Definici贸n de rutas
Edita routes/web.php:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PastelController; // ← ¡IMPORTANTE! Sin esto: Class not found
Route::get('/pasteles', [PastelController::class, 'index'])->name('pasteles.index');
馃 Namespaces en rutas: routes/web.php no tiene namespace (est谩 en el namespace global \). Por eso PHP no encuentra PastelController a menos que lo importes con use, o uses el FQCN: \App\Http\Controllers\PastelController::class.
Regla: archivo sin namespace → siempre necesitas use para clases de otros namespaces.
Verifica:
php artisan route:list
routes/web.php:<?php use Illuminate\Support\Facades\Route; use App\Http\Controllers\PastelController; // ← ¡IMPORTANTE! Sin esto: Class not found Route::get('/pasteles', [PastelController::class, 'index'])->name('pasteles.index');
馃 Namespaces en rutas: routes/web.php no tiene namespace (est谩 en el namespace global \). Por eso PHP no encuentra PastelController a menos que lo importes con use, o uses el FQCN: \App\Http\Controllers\PastelController::class.
Regla: archivo sin namespace → siempre necesitas use para clases de otros namespaces.
php artisan route:list
1. dd() — Dump and Die (muestra y detiene)
blade
{{-- Opci贸n A: sintaxis Blade --}}
<pre>
@dd($pasteles)
</pre>
{{-- Opci贸n B: sintaxis PHP directa --}}
<?php dd($pasteles); ?>
⚠️ Nota: dd() detiene la ejecuci贸n, as铆 que todo lo que pongas despu茅s no se ver谩. Es para verificar y salir.
2. dump() — Muestra pero NO detiene ⭐ (la mejor para vistas)
blade
{{-- Opci贸n A: directiva Blade --}}
@dump($pasteles)
{{-- Opci贸n B: PHP directo --}}
<?php dump($pasteles); ?>
Recomendaci贸n
Para tu archivo views/pasteles/debug.blade.php:
Si quieres ver y detenerte → @dd($pasteles)
Si quieres ver y seguir → @dump($pasteles) ← la m谩s c贸moda
Si quieres el estilo cl谩sico PHP → <?php var_dump($pasteles->toArray()); ?>
馃 Este es EL error m谩s com煤n que describes:
Sin use App\Models\Pastel;, cuando escribes Pastel::all() PHP busca la clase en App\Http\Controllers\Pastel (el namespace actual) y lanza:
Class "App\Http\Controllers\Pastel" not foundSoluci贸n: a帽adir el use arriba. No es include ni require, es importar el namespace.
馃敓 Creaci贸n de vistas
Crea manualmente en resources/views/:
index.blade.php - Listar pasteles
create.blade.php - Formulario de creaci贸n
edit.blade.php - Formulario de edici贸n
show.blade.php - Ver detalle
Verificar rutas:
bash
php artisan route:list
⚡ Bonus: Soluci贸n de problemas comunes
❌ "Class 'App\Models\Pastel' not found"
Soluci贸n: Reinicia Tinker con php artisan tinker despu茅s de crear el modelo.
❌ "No such file or directory"
Soluci贸n: Verifica que la base de datos est茅 creada y corriendo.
❌ Error de conexi贸n en Tinker
Soluci贸n: Verifica tu archivo .env y ejecuta:
bash
php artisan config:clear
❌ Los seeders no crean registros
Soluci贸n: Ejecuta en orden:
bash
php artisan migrate:fresh
php artisan db:seed
Debug en vistas
{{-- Muestra y DETIENE --}}
@dd($pasteles)
{{-- Muestra y SIGUE (la m谩s c贸moda en vistas) --}}
@dump($pasteles)
{{-- PHP cl谩sico --}}
<?php dump($pasteles); ?>
{{-- Muestra y DETIENE --}}
@dd($pasteles)
{{-- Muestra y SIGUE (la m谩s c贸moda en vistas) --}}
@dump($pasteles)
{{-- PHP cl谩sico --}}
<?php dump($pasteles); ?>馃殌 Resumen de comandos esenciales
bash
# Crear todo desde cero
php artisan migrate:fresh --seed
# Abrir Tinker para probar
php artisan tinker
# Verificar datos creados
DB::table('pasteles')->get()
# Salir de Tinker
exit
# Listar rutas
php artisan route:list
# Limpiar cach茅 si hay problemas
php artisan optimize:clear
API sencilla al CRUD de Pasteles
API sin autenticaci贸n, lista para integrarse despu茅s del controlador web.
1️⃣1️⃣ Preparar Laravel para API
En Laravel 11/12 el archivo routes/api.php ya no viene por defecto. Hay que generarlo con:
bash
php artisan install:api
Esto hace tres cosas:
Crea el archivo routes/api.php
Instala Laravel Sanctum (lo usaremos despu茅s si quieres a帽adir autenticaci贸n)
Registra el grupo de middleware api en bootstrap/app.php
馃挕 Si te pregunta si deseas ejecutar las migraciones de Sanctum, puedes responder No por ahora, ya que no usaremos autenticaci贸n.
Verifica que ahora exista:
bash
ls routes/
# api.php console.php web.php
1️⃣2️⃣ Creaci贸n del controlador API
Igual que hicimos con el controlador web, ahora creamos uno dedicado a la API:
bash
php artisan make:controller PastelApiController
Edita app/Http/Controllers/PastelApiController.php:
php
<?php
namespace App\Http\Controllers;
use App\Models\Pastel;
use Illuminate\Http\Request;
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);
}
/**
* 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);
}
}
⚠️ Observa que no usamos Pastel $pastel con Route Model Binding, porque si el pastel no existe Laravel devuelve un 404 HTML en lugar de JSON. Usando find($id) controlamos la respuesta.
1️⃣3️⃣ Definici贸n de rutas API
Edita routes/api.php:
php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PastelApiController;
/*
|--------------------------------------------------------------------------
| Rutas API - Pasteles
|--------------------------------------------------------------------------
| Prefijo autom谩tico: /api
| Sin autenticaci贸n (por ahora)
*/
Route::get('/pasteles', [PastelApiController::class, 'index']); // GET /api/pasteles
Route::get('/pasteles/{id}', [PastelApiController::class, 'show']); // GET /api/pasteles/{id}
Route::post('/pasteles', [PastelApiController::class, 'store']); // POST /api/pasteles
Route::put('/pasteles/{id}', [PastelApiController::class, 'update']); // PUT /api/pasteles/{id}
Route::delete('/pasteles/{id}',[PastelApiController::class, 'destroy']); // DELETE /api/pasteles/{id}
Verifica que las rutas est茅n registradas:
bash
php artisan route:list
Deber铆as ver algo como:
text
GET|HEAD api/pasteles ............... PastelApiController@index
POST api/pasteles ............... PastelApiController@store
GET|HEAD api/pasteles/{id} .......... PastelApiController@show
PUT api/pasteles/{id} .......... PastelApiController@update
DELETE api/pasteles/{id} .......... PastelApiController@destroy
1️⃣4️⃣ Probar la API con cURL
Primero levanta el servidor:
bash
php artisan serve
馃敼 Obtener todos los pasteles
bash
curl http://localhost:8000/api/pasteles
Comentarios
Publicar un comentario