272 lines
7.7 KiB
PHP
272 lines
7.7 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Illuminate\Notifications\Notifiable;
|
|
|
|
class Worker extends Model
|
|
{
|
|
use HasFactory, SoftDeletes, Notifiable;
|
|
|
|
protected static function booted()
|
|
{
|
|
static::updated(function ($worker) {
|
|
if ($worker->wasChanged('status')) {
|
|
$oldStatus = $worker->getOriginal('status');
|
|
$newStatus = $worker->status;
|
|
|
|
$worker->notify(new \App\Notifications\GenericNotification(
|
|
"Profile Status Update",
|
|
"Your profile status has changed from '" . ucfirst($oldStatus) . "' to '" . ucfirst($newStatus) . "'.",
|
|
[
|
|
'type' => 'worker_status_change',
|
|
'worker_id' => $worker->id,
|
|
'old_status' => $oldStatus,
|
|
'new_status' => $newStatus,
|
|
]
|
|
));
|
|
}
|
|
});
|
|
}
|
|
|
|
protected $fillable = [
|
|
'name',
|
|
'email',
|
|
'phone',
|
|
'language',
|
|
'password',
|
|
'nationality',
|
|
'country',
|
|
'city',
|
|
'area',
|
|
'preferred_location',
|
|
'live_in_out',
|
|
'age',
|
|
'gender',
|
|
'salary',
|
|
'availability',
|
|
'experience',
|
|
'religion',
|
|
'bio',
|
|
'verified',
|
|
'status',
|
|
'api_token',
|
|
'in_country',
|
|
'visa_status',
|
|
'preferred_job_type',
|
|
'main_profession',
|
|
'fcm_token',
|
|
];
|
|
|
|
protected $hidden = [
|
|
'password',
|
|
'api_token', // Hide from public serialization, returned only during auth endpoints specifically
|
|
];
|
|
|
|
protected $casts = [
|
|
'verified' => 'boolean',
|
|
'in_country' => 'boolean',
|
|
'salary' => 'decimal:2',
|
|
'password' => 'hashed',
|
|
];
|
|
|
|
protected $appends = [
|
|
'passport_status',
|
|
'visa_status',
|
|
'emirates_id_status',
|
|
'document_expiry_days',
|
|
'document_expiry_status',
|
|
'visa_expiry_date',
|
|
'rating',
|
|
'reviews_count',
|
|
];
|
|
|
|
public function getPassportStatusAttribute()
|
|
{
|
|
$passport = $this->documents->where('type', 'passport')->first();
|
|
if (!$passport) {
|
|
return 'Passport Pending';
|
|
}
|
|
return $this->verified ? 'Passport Verified' : 'Passport Pending';
|
|
}
|
|
|
|
public function getVisaStatusAttribute()
|
|
{
|
|
if ($this->attributes['visa_status'] ?? null) {
|
|
$val = $this->attributes['visa_status'];
|
|
$normalized = strtolower(trim($val));
|
|
if (str_contains($normalized, 'residence')) {
|
|
return 'Residence Visa';
|
|
} elseif (str_contains($normalized, 'employment')) {
|
|
return 'Employment Visa';
|
|
} else {
|
|
return 'Tourist Visa';
|
|
}
|
|
}
|
|
$visa = $this->documents->where('type', 'visa')->first();
|
|
if (!$visa) {
|
|
return 'Residence Visa';
|
|
}
|
|
$ocrData = $visa->ocr_data;
|
|
$vType = $ocrData['visa_type'] ?? null;
|
|
if ($vType) {
|
|
$normalized = strtolower(trim($vType));
|
|
if (str_contains($normalized, 'residence')) {
|
|
return 'Residence Visa';
|
|
} elseif (str_contains($normalized, 'employment')) {
|
|
return 'Employment Visa';
|
|
}
|
|
}
|
|
return 'Tourist Visa';
|
|
}
|
|
|
|
public function setVisaStatusAttribute($value)
|
|
{
|
|
if (empty($value)) {
|
|
$this->attributes['visa_status'] = null;
|
|
return;
|
|
}
|
|
|
|
$normalized = strtolower(trim($value));
|
|
if (str_contains($normalized, 'residence')) {
|
|
$this->attributes['visa_status'] = 'Residence Visa';
|
|
} elseif (str_contains($normalized, 'employment')) {
|
|
$this->attributes['visa_status'] = 'Employment Visa';
|
|
} else {
|
|
$this->attributes['visa_status'] = 'Tourist Visa';
|
|
}
|
|
}
|
|
|
|
public function getEmiratesIdStatusAttribute()
|
|
{
|
|
return $this->passport_status;
|
|
}
|
|
|
|
public function getVisaExpiryDateAttribute()
|
|
{
|
|
$visa = $this->documents->where('type', 'visa')->first();
|
|
return $visa ? $visa->expiry_date : null;
|
|
}
|
|
|
|
public function getDocumentExpiryDaysAttribute()
|
|
{
|
|
$visa = $this->documents->where('type', 'visa')->first();
|
|
$doc = $visa;
|
|
if (!$doc || !$doc->expiry_date) {
|
|
$passport = $this->documents->where('type', 'passport')->first();
|
|
$doc = $passport;
|
|
}
|
|
|
|
if ($doc && $doc->expiry_date) {
|
|
$expiry = \Carbon\Carbon::parse($doc->expiry_date);
|
|
return (int)now()->startOfDay()->diffInDays(\Carbon\Carbon::parse($expiry)->startOfDay(), false);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function getDocumentExpiryStatusAttribute()
|
|
{
|
|
$visa = $this->documents->where('type', 'visa')->first();
|
|
$doc = $visa;
|
|
$typeLabel = 'Visa';
|
|
if (!$doc || !$doc->expiry_date) {
|
|
$passport = $this->documents->where('type', 'passport')->first();
|
|
$doc = $passport;
|
|
$typeLabel = 'Passport';
|
|
}
|
|
|
|
if ($doc && $doc->expiry_date) {
|
|
$expiry = \Carbon\Carbon::parse($doc->expiry_date);
|
|
$days = (int)now()->startOfDay()->diffInDays(\Carbon\Carbon::parse($expiry)->startOfDay(), false);
|
|
if ($days < 0) {
|
|
return "$typeLabel Expired";
|
|
}
|
|
return "$typeLabel expires in $days days";
|
|
}
|
|
|
|
return 'No Documents';
|
|
}
|
|
|
|
|
|
public function skills()
|
|
{
|
|
return $this->belongsToMany(Skill::class, 'worker_skills');
|
|
}
|
|
|
|
public function documents()
|
|
{
|
|
return $this->hasMany(WorkerDocument::class);
|
|
}
|
|
|
|
// Relationship for job offers received
|
|
public function offers()
|
|
{
|
|
return $this->hasMany(JobOffer::class, 'worker_id');
|
|
}
|
|
|
|
public function reviews()
|
|
{
|
|
return $this->hasMany(Review::class);
|
|
}
|
|
|
|
public function employerReviews()
|
|
{
|
|
return $this->hasMany(EmployerReview::class, 'worker_id');
|
|
}
|
|
|
|
public function getRatingAttribute()
|
|
{
|
|
// Prevent infinite recursion by not using relationships if we want to avoid eager load loops,
|
|
// but typically $this->reviews is safe as long as we don't eager load recursively.
|
|
$dbReviews = $this->reviews;
|
|
$count = $dbReviews->count();
|
|
return $count > 0 ? round($dbReviews->avg('rating'), 1) : 0.0;
|
|
}
|
|
|
|
public function getReviewsCountAttribute()
|
|
{
|
|
return $this->reviews()->count();
|
|
}
|
|
|
|
public function profileViews()
|
|
{
|
|
return $this->hasMany(ProfileView::class);
|
|
}
|
|
|
|
public static function findByPhone($phone)
|
|
{
|
|
if (empty($phone)) {
|
|
return null;
|
|
}
|
|
|
|
$cleanInput = ltrim(preg_replace('/\D/', '', $phone), '0');
|
|
if (empty($cleanInput)) {
|
|
return null;
|
|
}
|
|
|
|
// If input is short, fall back to direct query
|
|
if (strlen($cleanInput) < 7) {
|
|
return self::where('phone', $phone)->first();
|
|
}
|
|
|
|
$suffix = substr($cleanInput, -7);
|
|
$candidates = self::whereRaw("REPLACE(REPLACE(phone, ' ', ''), '+', '') LIKE ?", ['%' . $suffix])->get();
|
|
|
|
foreach ($candidates as $candidate) {
|
|
$cleanCandidate = ltrim(preg_replace('/\D/', '', $candidate->phone), '0');
|
|
if ($cleanInput === $cleanCandidate ||
|
|
str_ends_with($cleanInput, $cleanCandidate) ||
|
|
str_ends_with($cleanCandidate, $cleanInput)) {
|
|
return $candidate;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|