'datetime', 'last_login_at' => 'datetime', 'options' => 'array', 'deleted_at' => 'datetime', 'password' => 'hashed', // ← 이걸 쓰면 자동 해싱 ]; protected $hidden = [ 'password', 'remember_token', 'two_factor_secret', 'two_factor_recovery_codes', 'two_factor_confirmed_at', 'deleted_at', ]; protected $appends = [ 'has_account', ]; /** * 시스템 계정(비밀번호) 보유 여부 */ public function getHasAccountAttribute(): bool { return ! empty($this->password); } public function userTenants() { return $this->hasMany(UserTenant::class); } public function userTenant() { return $this->hasOne(UserTenant::class)->where('is_default', 1); } public function userRoles() { return $this->hasMany(UserRole::class); } public function userTenantById($tenantId) { return $this->hasOne(UserTenant::class)->where('tenant_id', $tenantId); } public function files() { return $this->morphMany(File::class, 'fileable'); } public function tenantsMembership(): BelongsToMany { return $this->belongsToMany(Tenant::class, 'user_tenants', 'user_id', 'tenant_id') ->as('membership') // pivot 대신 membership으로 표기 ->withPivot(['is_active', 'is_default', 'joined_at', 'left_at', 'deleted_at']) ->wherePivotNull('deleted_at'); // 소프트삭제 제외 } /** * 테넌트별 사용자 프로필 (전체) */ public function tenantProfiles() { return $this->hasMany(TenantUserProfile::class); } /** * 현재 테넌트의 사용자 프로필 * 주의: 조회 시 tenant_id 조건 추가 필요 */ public function tenantProfile() { return $this->hasOne(TenantUserProfile::class); } }