useLogName('user-management') ->logOnly(['name', 'username', 'email', 'phone_number', 'is_active']) ->logOnlyDirty() ->dontSubmitEmptyLogs(); } /** * The attributes that are mass assignable. */ protected $fillable = [ 'name', 'username', 'email', 'phone_number', 'password', 'google_id', 'facebook_id', 'github_id', 'is_active', 'password_changed_at', 'last_session_id', 'created_by', 'updated_by', ]; /** * Hidden fields */ protected $hidden = [ 'password', 'remember_token', 'media', // Hide raw media relation ]; /** * Appended attributes */ protected $appends = ['avatar']; /** * Avatar Accessor (using Media Library) */ public function getAvatarAttribute(): ?string { return $this->getFirstMediaUrl('avatar') ?: null; } /** * The attributes that should be cast. */ protected $casts = [ 'email_verified_at' => 'datetime', 'password' => 'hashed', 'is_active' => 'boolean', 'password_changed_at' => 'datetime', ]; /** * Password History Relation */ public function passwordHistories() { return $this->hasMany(PasswordHistory::class); } /** * Trusted Devices Relation */ public function trustedDevices() { return $this->hasMany(UserTrustedDevice::class); } /** * ========================================== * DISABLE RESET PASSWORD FOR SOCIAL USERS * ========================================== */ public function sendPasswordResetNotification($token) { // ❌ OAuth user → BLOCK reset password if ($this->google_id || $this->facebook_id || $this->github_id) { return; } // ✅ User manual → normal $this->notify(new ResetPasswordNotification($token)); } /** * Use our branded email verification template. */ public function sendEmailVerificationNotification() { $this->notify(new VerifyEmailNotification); } /** * Helpers */ public function isSocialUser(): bool { return ! is_null($this->google_id) || ! is_null($this->facebook_id) || ! is_null($this->github_id); } /** * Consents Relation */ public function consents() { return $this->hasMany(UserConsent::class); } /** * Check if user has agreed to the current version of a document. */ public function hasAgreedToCurrentLegal(string $type): bool { // Settings use 'pdp' prefix for Privacy Policy, while code uses 'privacy' type $keyPrefix = ($type === 'privacy' || $type === 'pdp') ? 'pdp' : $type; $currentVersion = (int) get_setting("{$keyPrefix}_document_version", 1); return $this->consents() ->where(function ($q) use ($type) { $q->where('consent_type', $type) ->orWhere('consent_type', 'privacy') ->orWhere('consent_type', 'pdp'); }) ->where('version_id', '>=', $currentVersion) ->exists(); } /** * Notifications this user has interacted with (including personal read/deleted status). */ public function broadcastNotifications() { return $this->belongsToMany(Notification::class, 'system_notification_user') ->withPivot('read_at', 'deleted_at') ->withTimestamps(); } /** * Get the user who created this record. */ public function creator() { return $this->belongsTo(User::class, 'created_by'); } /** * Get the user who last updated this record. */ public function updater() { return $this->belongsTo(User::class, 'updated_by'); } }