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

  1. Proyectos: Información sobre los trabajos realizados

  2. Habilidades: Tecnologías y competencias profesionales

  3. 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:

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:

bash
php artisan make:migration create_projects_table

Abre el archivo generado en database/migrations/ y define la estructura:

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('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:

bash
php artisan make:migration create_skills_table

Define la estructura:

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('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:

bash
php artisan make:migration create_experiences_table

Define la estructura:

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('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:

bash
php artisan make:model Project

Abre app/Models/Project.php y define las relaciones y atributos:

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

bash
php artisan make:model Skill

Define app/Models/Skill.php:

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:

bash
php artisan make:model Experience

Define app/Models/Experience.php:

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

bash
php artisan make:controller ProjectController --resource

Define app/Http/Controllers/ProjectController.php:

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

bash
php artisan make:controller SkillController

Define app/Http/Controllers/SkillController.php:

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

bash
php artisan make:controller ExperienceController

Define app/Http/Controllers/ExperienceController.php:

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

bash
php artisan migrate

Si todo funciona correctamente, verás un mensaje similar a:

text
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_table

Paso 9: Poblar la base de datos (Seeders)

Crea seeders para tener datos de ejemplo:

bash
php artisan make:seeder ProjectSeeder
php artisan make:seeder SkillSeeder
php artisan make:seeder ExperienceSeeder

Define los seeders con datos de ejemplo y ejecútalos:

bash
php artisan db:seed --class=ProjectSeeder
php artisan db:seed --class=SkillSeeder
php artisan db:seed --class=ExperienceSeeder

Verificación final

Para confirmar que todo está funcionando:

  1. Verifica las tablas: Conecta a tu base de datos y confirma que existen las tablas projects, skills y experiences.

  2. Prueba las rutas: Accede a /projects, /skills y /experience en tu navegador.

  3. Inserta datos: Crea algunos registros de prueba usando Tinker:

bash
php artisan tinker
php
Project::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

Entradas más populares de este blog

Tareas con Laravel + Inertia + Jetstream

HACER CAMBIOS-AL EJEMPLO POR DEFECTO

Laravel + Inertia - ejemplo