'datetime', 'password' => 'hashed', 'two_factor_enabled' => 'boolean', 'notification_preferences' => 'array', ]; // Defaults for each preference key (true = on, false = off) private const NOTIF_DEFAULTS = [ 'notif_new_comment' => true, 'notif_new_reply' => true, 'notif_comment_like' => true, 'notif_video_like' => true, 'notif_new_subscriber' => true, 'notif_new_video_from_sub' => true, 'notif_new_post_from_sub' => true, 'notif_new_user_reg' => true, 'email_new_comment' => true, 'email_new_reply' => true, 'email_comment_like' => false, 'email_video_like' => false, 'email_new_subscriber' => true, 'email_new_video_from_sub' => true, 'email_new_post_from_sub' => false, 'email_video_processed' => true, 'email_new_user_reg' => true, 'email_weekly_digest' => true, ]; public function notificationPref(string $key): bool { $prefs = $this->notification_preferences ?? []; $default = self::NOTIF_DEFAULTS[$key] ?? true; return isset($prefs[$key]) ? (bool) $prefs[$key] : $default; } public static function notifDefaults(): array { return self::NOTIF_DEFAULTS; } protected $appends = ['avatar_url', 'banner_url']; // Auto-generate a unique slug-based username when creating a user without one protected static function boot(): void { parent::boot(); static::creating(function (User $user) { if (empty($user->username)) { $user->username = static::generateUniqueUsername($user->name); } }); } public static function generateUniqueUsername(string $name): string { $base = substr(Str::slug(trim($name)), 0, 18); if ($base === '') { $base = 'user'; } do { $slug = $base . '-' . Str::lower(Str::random(6)); } while (static::where('username', $slug)->exists()); return $slug; } // Returns the channel URL slug — the username, auto-generated and saved if missing public function getChannelAttribute(): string { if (empty($this->username)) { $this->username = static::generateUniqueUsername($this->name); $this->saveQuietly(); } return $this->username; } // Relationships public function videos() { return $this->hasMany(Video::class); } public function likes() { return $this->belongsToMany(Video::class, 'video_likes')->withTimestamps(); } public function views() { return $this->belongsToMany(Video::class, 'video_views')->withTimestamps(); } public function comments() { return $this->hasMany(Comment::class); } public function playlists() { return $this->hasMany(Playlist::class); } public function posts() { return $this->hasMany(\App\Models\Post::class); } public function getAvatarUrlAttribute(): string { if ($this->avatar) { return route('media.avatar', $this->avatar); } return 'https://i.pravatar.cc/150?u='.$this->id; } public function getBannerUrlAttribute(): ?string { if ($this->banner) { return route('media.banner', $this->banner); } return null; } public function sendEmailVerificationNotification(): void { $this->notify(new VerifyEmail); } // Role helper methods public function isSuperAdmin() { return $this->role === 'super_admin'; } public function isAdmin() { return in_array($this->role, ['admin', 'super_admin']); } public function isUser() { return $this->role === 'user' || $this->role === null; } // Users who subscribe TO this channel public function subscribers() { return $this->belongsToMany( User::class, 'user_subscriptions', 'channel_id', 'subscriber_id' )->withPivot(['created_at', 'source_video_id']); } // Channels this user subscribes to public function subscriptions() { return $this->belongsToMany( User::class, 'user_subscriptions', 'subscriber_id', 'channel_id' )->withPivot(['created_at', 'source_video_id']); } public function isSubscribedTo(User $channel): bool { return $this->subscriptions()->where('channel_id', $channel->id)->exists(); } public function getSubscriberCountAttribute(): int { return $this->subscribers()->count(); } // Check if user has any social links public function socialLinks() { return $this->hasMany(UserSocialLink::class)->orderBy('sort_order'); } public function hasSocialLinks() { return $this->socialLinks()->exists(); } // Get formatted website URL public function getWebsiteUrlAttribute() { if (! $this->website) { return null; } // Add https:// if no protocol is specified if (! preg_match('/^https?:\/\//', $this->website)) { return 'https://'.$this->website; } return $this->website; } }