init commit

This commit is contained in:
David Melendez
2026-01-14 22:38:44 +01:00
parent 4e0c415f0b
commit e25d53d054
124 changed files with 21653 additions and 1 deletions

260
app/Models/Resume.php Normal file
View File

@@ -0,0 +1,260 @@
<?php
/**
* Resume Model
* Professional Resume Builder - Resume Entity
*
* @author David Valera Melendez <david@valera-melendez.de>
* @created 2025-08-08
* @location Made in Germany 🇩🇪
*/
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Resume Model
* Represents user resumes with all personal and professional information
*/
class Resume extends Model
{
use HasFactory, SoftDeletes;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'user_id',
'title',
'template_id',
'slug',
'personal_info',
'professional_summary',
'work_experiences',
'education',
'skills',
'languages',
'certifications',
'projects',
'references',
'custom_sections',
'settings',
'is_active',
'is_completed',
'is_public',
'public_url',
'created_at',
'updated_at'
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'personal_info' => 'array',
'work_experiences' => 'array',
'education' => 'array',
'skills' => 'array',
'languages' => 'array',
'certifications' => 'array',
'projects' => 'array',
'references' => 'array',
'custom_sections' => 'array',
'settings' => 'array',
'is_active' => 'boolean',
'is_completed' => 'boolean',
'is_public' => 'boolean',
];
/**
* Get the user that owns the resume.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Get the resume's public URL.
*/
public function getPublicUrlAttribute(): ?string
{
if ($this->is_public && $this->public_url) {
return route('public.resume', $this->public_url);
}
return null;
}
/**
* Get the resume's preview URL.
*/
public function getPreviewUrlAttribute(): string
{
return route('resume-builder.preview', $this);
}
/**
* Get the resume's edit URL.
*/
public function getEditUrlAttribute(): string
{
return route('resume-builder.edit', $this);
}
/**
* Get the resume's PDF download URL.
*/
public function getPdfUrlAttribute(): string
{
return route('resume-builder.download-pdf', $this);
}
/**
* Calculate completion percentage of the resume.
*/
public function getCompletionPercentageAttribute(): int
{
$sections = [
'personal_info' => 25,
'professional_summary' => 10,
'work_experiences' => 30,
'education' => 15,
'skills' => 10,
'languages' => 5,
'certifications' => 5,
];
$completed = 0;
foreach ($sections as $section => $weight) {
if ($this->isSectionCompleted($section)) {
$completed += $weight;
}
}
return min(100, $completed);
}
/**
* Check if a specific section is completed.
*/
public function isSectionCompleted(string $section): bool
{
$data = $this->$section;
if (empty($data)) {
return false;
}
switch ($section) {
case 'personal_info':
$required = ['first_name', 'last_name', 'email', 'phone'];
foreach ($required as $field) {
if (empty($data[$field] ?? null)) {
return false;
}
}
return true;
case 'professional_summary':
return !empty($data) && strlen($data) >= 50;
case 'work_experiences':
return is_array($data) && count($data) > 0 && !empty($data[0]['company'] ?? null);
case 'education':
return is_array($data) && count($data) > 0 && !empty($data[0]['institution'] ?? null);
case 'skills':
return is_array($data) && count($data) > 0;
default:
return !empty($data);
}
}
/**
* Generate a unique slug for the resume.
*/
public function generateSlug(): string
{
$baseSlug = str()->slug($this->title);
$slug = $baseSlug;
$counter = 1;
while (static::where('slug', $slug)->where('id', '!=', $this->id)->exists()) {
$slug = $baseSlug . '-' . $counter;
$counter++;
}
return $slug;
}
/**
* Generate a unique public URL for the resume.
*/
public function generatePublicUrl(): string
{
return str()->random(32);
}
/**
* Mark the resume as completed.
*/
public function markAsCompleted(): void
{
$this->update(['is_completed' => true]);
}
/**
* Scope a query to only include active resumes.
*/
public function scopeActive($query)
{
return $query->where('is_active', true);
}
/**
* Scope a query to only include completed resumes.
*/
public function scopeCompleted($query)
{
return $query->where('is_completed', true);
}
/**
* Scope a query to only include public resumes.
*/
public function scopePublic($query)
{
return $query->where('is_public', true);
}
/**
* Boot the model.
*/
protected static function boot()
{
parent::boot();
static::creating(function ($resume) {
if (empty($resume->slug)) {
$resume->slug = $resume->generateSlug();
}
});
static::updating(function ($resume) {
if ($resume->isDirty('title')) {
$resume->slug = $resume->generateSlug();
}
});
}
}