* @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 */ 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 */ 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(); } }); } }