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

  1. Configuraci贸n inicial del proyecto

  2. Creaci贸n de la migraci贸n

  3. Creaci贸n del modelo (¡PASO CLAVE!)

  4. Creaci贸n del Factory

  5. Creaci贸n del Seeder

  6. Ejecutar migraciones y seeders

  7. Probar con Laravel Tinker

  8. Creaci贸n del controlador

  9. Creaci贸n de las vistas

  10. Definici贸n de rutas

  11. Pruebas finales

脥ndice

Prop贸sito

Comando

1-Configuraci贸n inicial

composer create-project laravel/laravel nombre-proyecto

2-Creaci贸n de la migraci贸n

php artisan make:migration create_pasteles_table

3-Creaci贸n del modelo

php artisan make:model Pastel

4-Creaci贸n del seeder

php artisan make:seeder PastelSeeder

5-Creaci贸n del controlador

php artisan make:controller PastelController

6-Definici贸n de rutas

php artisan route:list

7-Creaci贸n de vistas

(Crear manualmente en resources/views/)

8-Mejores pr谩cticas y optimizaciones

php artisan optimize


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

bash
php artisan migrate

Flujo recomendado completo

bash
# 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:

Comando

¿Qu茅 hace?

php artisan migrate

Ejecuta todas las migraciones pendientes (crea la tabla en la BD).

php artisan migrate:fresh

Elimina todas las tablas y vuelve a ejecutar todas las migraciones desde cero. 脷til para reiniciar el proyecto en desarrollo.

php artisan migrate:refresh

Hace un rollback (reversi贸n) de todas las migraciones y luego ejecuta migrate de nuevo. Es como un "reinicio suave" que conserva los datos de los seeders si los tienes.

php artisan migrate:rollback

Revierte solo el 煤ltimo lote de migraciones (las que se ejecutaron en la 煤ltima tanda).

php artisan migrate:status

Muestra el estado de todas las migraciones (cu谩les est谩n ejecutadas y cu谩les pendientes).

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 de class.

  • use HasFactory, SoftDeletes; → aplica traits dentro de la clase. Va despu茅s de class.

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'

 //       ]);

    }

}


Regla de oro: si el namespace de la clase que quieres usar no es el mismo que el 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
<?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

php
$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 --seed


php 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
<?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:

bash
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:

text
Class "App\Http\Controllers\Pastel" not found

Soluci贸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

blade
{{-- 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

Entradas m谩s populares de este blog

Tutorial CRUD con Laravel 12-Pastel::factory(50)->create();

CRUD Completo con Laravel 12 - Gu铆a Pr谩ctica Paso a Paso-productos

CRUD en Laravel 12 con Git - Gu铆a para Principiantes