8: Crea los componentes para almacenar la información de tu portafolio con Laravel
8: Crea los componentes para almacenar la información de tu portafolio con Laravel
Introducción
Ahora que tenemos nuestro proyecto Laravel configurado con Jetstream y el stack Bolt (Vue + Inertia + Tailwind), es momento de comenzar a construir la estructura de datos que almacenará la información de nuestro portafolio. En este tutorial crearemos los modelos, migraciones y controladores necesarios para gestionar los proyectos, habilidades y experiencia profesional.
¿Qué vamos a construir?
Crearemos un sistema completo para gestionar el contenido de un portafolio profesional que incluirá:
Proyectos: Información sobre los trabajos realizados
Habilidades: Tecnologías y competencias profesionales
Experiencia: Historial laboral y educativo
Paso 1: Configurar la base de datos
Antes de comenzar, asegúrate de que tu base de datos esté configurada correctamente en el archivo .env:
# Para SQLite (recomendado para desarrollo)
DB_CONNECTION=sqlite
# Para MySQL
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=portfolio_cpa
DB_USERNAME=root
DB_PASSWORD=Si usas SQLite, asegúrate de que el archivo database/database.sqlite exista.
Paso 2: Crear la migración para Proyectos
Ejecuta el siguiente comando para crear la migración de la tabla projects:
php artisan make:migration create_projects_tableAbre el archivo generado en database/migrations/ y define la estructura:
<?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('projects', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('slug')->unique();
$table->text('description');
$table->text('content')->nullable();
$table->string('image_url')->nullable();
$table->string('github_url')->nullable();
$table->string('demo_url')->nullable();
$table->json('technologies')->nullable();
$table->date('start_date')->nullable();
$table->date('end_date')->nullable();
$table->boolean('is_featured')->default(false);
$table->boolean('is_published')->default(true);
$table->integer('order')->default(0);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('projects');
}
};Paso 3: Crear la migración para Habilidades
Crea la migración para la tabla skills:
php artisan make:migration create_skills_tableDefine la estructura:
<?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('skills', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('icon')->nullable();
$table->string('category')->nullable();
$table->integer('level')->default(0); // 0-100
$table->string('color')->nullable();
$table->boolean('is_featured')->default(false);
$table->integer('order')->default(0);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('skills');
}
};Paso 4: Crear la migración para Experiencia
Crea la migración para la tabla experiences:
php artisan make:migration create_experiences_tableDefine la estructura:
<?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('experiences', function (Blueprint $table) {
$table->id();
$table->string('company');
$table->string('position');
$table->string('logo_url')->nullable();
$table->text('description')->nullable();
$table->date('start_date');
$table->date('end_date')->nullable();
$table->boolean('is_current')->default(false);
$table->string('location')->nullable();
$table->json('achievements')->nullable();
$table->integer('order')->default(0);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('experiences');
}
};Paso 5: Crear los Modelos
Modelo de Proyecto
Crea el modelo Project:
php artisan make:model ProjectAbre app/Models/Project.php y define las relaciones y atributos:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class Project extends Model
{
use HasFactory;
protected $fillable = [
'title',
'slug',
'description',
'content',
'image_url',
'github_url',
'demo_url',
'technologies',
'start_date',
'end_date',
'is_featured',
'is_published',
'order'
];
protected $casts = [
'technologies' => 'array',
'is_featured' => 'boolean',
'is_published' => 'boolean',
'start_date' => 'date',
'end_date' => 'date',
];
// Generar slug automáticamente
public static function boot()
{
parent::boot();
static::creating(function ($project) {
$project->slug = Str::slug($project->title);
});
static::updating(function ($project) {
if ($project->isDirty('title')) {
$project->slug = Str::slug($project->title);
}
});
}
// Scope para proyectos publicados
public function scopePublished($query)
{
return $query->where('is_published', true);
}
// Scope para proyectos destacados
public function scopeFeatured($query)
{
return $query->where('is_featured', true);
}
}Modelo de Habilidad
Crea el modelo Skill:
php artisan make:model SkillDefine app/Models/Skill.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Skill extends Model
{
use HasFactory;
protected $fillable = [
'name',
'icon',
'category',
'level',
'color',
'is_featured',
'order'
];
protected $casts = [
'is_featured' => 'boolean',
'level' => 'integer',
];
// Scope para habilidades destacadas
public function scopeFeatured($query)
{
return $query->where('is_featured', true);
}
// Scope por categoría
public function scopeCategory($query, $category)
{
return $query->where('category', $category);
}
}Modelo de Experiencia
Crea el modelo Experience:
php artisan make:model ExperienceDefine app/Models/Experience.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Experience extends Model
{
use HasFactory;
protected $fillable = [
'company',
'position',
'logo_url',
'description',
'start_date',
'end_date',
'is_current',
'location',
'achievements',
'order'
];
protected $casts = [
'achievements' => 'array',
'is_current' => 'boolean',
'start_date' => 'date',
'end_date' => 'date',
];
// Scope para experiencias actuales
public function scopeCurrent($query)
{
return $query->where('is_current', true);
}
// Obtener duración formateada
public function getDurationAttribute()
{
$start = $this->start_date->format('M Y');
$end = $this->is_current ? 'Present' : $this->end_date->format('M Y');
return $start . ' - ' . $end;
}
}Paso 6: Crear los Controladores
Controlador de Proyectos
php artisan make:controller ProjectController --resourceDefine app/Http/Controllers/ProjectController.php:
<?php
namespace App\Http\Controllers;
use App\Models\Project;
use Illuminate\Http\Request;
class ProjectController extends Controller
{
public function index()
{
$projects = Project::published()
->orderBy('order')
->get();
return inertia('Projects/Index', [
'projects' => $projects
]);
}
public function show($slug)
{
$project = Project::where('slug', $slug)
->published()
->firstOrFail();
return inertia('Projects/Show', [
'project' => $project
]);
}
}Controlador de Habilidades
php artisan make:controller SkillControllerDefine app/Http/Controllers/SkillController.php:
<?php
namespace App\Http\Controllers;
use App\Models\Skill;
use Illuminate\Http\Request;
class SkillController extends Controller
{
public function index()
{
$skills = Skill::orderBy('order')
->get()
->groupBy('category');
return inertia('Skills/Index', [
'skills' => $skills
]);
}
}Controlador de Experiencia
php artisan make:controller ExperienceControllerDefine app/Http/Controllers/ExperienceController.php:
<?php
namespace App\Http\Controllers;
use App\Models\Experience;
use Illuminate\Http\Request;
class ExperienceController extends Controller
{
public function index()
{
$experiences = Experience::orderBy('order')
->orderBy('start_date', 'desc')
->get();
return inertia('Experience/Index', [
'experiences' => $experiences
]);
}
}Paso 7: Definir las Rutas
Actualiza routes/web.php para incluir las nuevas rutas:
<?php
use App\Http\Controllers\ExperienceController;
use App\Http\Controllers\ProjectController;
use App\Http\Controllers\SkillController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return inertia('Home');
})->name('home');
// Rutas de Proyectos
Route::get('/projects', [ProjectController::class, 'index'])->name('projects.index');
Route::get('/projects/{slug}', [ProjectController::class, 'show'])->name('projects.show');
// Rutas de Habilidades
Route::get('/skills', [SkillController::class, 'index'])->name('skills.index');
// Rutas de Experiencia
Route::get('/experience', [ExperienceController::class, 'index'])->name('experience.index');
// Dashboard (protegido por autenticación)
Route::middleware(['auth:sanctum', 'verified'])->group(function () {
Route::get('/dashboard', function () {
return inertia('Dashboard');
})->name('dashboard');
});
// Rutas de perfil de Jetstream
Route::group(['middleware' => 'auth'], function () {
Route::get('/user/profile', [ProfileController::class, 'show'])->name('profile.show');
});Paso 8: Ejecutar las Migraciones
Ahora ejecuta todas las migraciones para crear las tablas en tu base de datos:
php artisan migrateSi todo funciona correctamente, verás un mensaje similar a:
Migration table created successfully.
Migrating: 2024_01_01_000000_create_projects_table
Migrated: 2024_01_01_000000_create_projects_table
Migrating: 2024_01_01_000001_create_skills_table
Migrated: 2024_01_01_000001_create_skills_table
Migrating: 2024_01_01_000002_create_experiences_table
Migrated: 2024_01_01_000002_create_experiences_tablePaso 9: Poblar la base de datos (Seeders)
Crea seeders para tener datos de ejemplo:
php artisan make:seeder ProjectSeeder
php artisan make:seeder SkillSeeder
php artisan make:seeder ExperienceSeederDefine los seeders con datos de ejemplo y ejecútalos:
php artisan db:seed --class=ProjectSeeder
php artisan db:seed --class=SkillSeeder
php artisan db:seed --class=ExperienceSeederVerificación final
Para confirmar que todo está funcionando:
Verifica las tablas: Conecta a tu base de datos y confirma que existen las tablas
projects,skillsyexperiences.Prueba las rutas: Accede a
/projects,/skillsy/experienceen tu navegador.Inserta datos: Crea algunos registros de prueba usando Tinker:
php artisan tinkerProject::create([
'title' => 'Mi Portafolio',
'description' => 'Un sitio web para mostrar mi trabajo',
'github_url' => 'https://github.com/tu-usuario/portfolio',
'is_featured' => true
]);
Skill::create([
'name' => 'Laravel',
'category' => 'Backend',
'level' => 85
]);
Experience::create([
'company' => 'Tech Corp',
'position' => 'Desarrollador Full Stack',
'start_date' => '2020-01-01',
'is_current' => true
]);Resumen
✅ Hemos creado las migraciones para las tablas del portafolio
✅ Definimos los modelos con sus relaciones y scopes
✅ Configuramos los controladores para manejar la lógica de negocio
✅ Definimos las rutas para acceder a cada sección
✅ Ejecutamos las migraciones para crear las tablas en la base de datos
¡Excelente trabajo! Ahora tenemos una estructura sólida para almacenar y gestionar toda la información de nuestro portafolio. En la próxima sección comenzaremos a construir las interfaces de usuario con Vue.js e Inertia.
Recursos adicionales
¿Tienes preguntas sobre la estructura de datos? Recuerda que puedes consultar la documentación oficial de Laravel o revisar los videos del curso para obtener explicaciones más detalladas
Comentarios
Publicar un comentario