'boolean', 'date_of_birth' => 'date', 'email_verified_at' => 'datetime', 'inactive_at' => 'datetime', 'deleted_at' => 'datetime', ]; protected $guard_name = 'web'; /** * Get the index name for the model. * * @return string */ public function searchableAs() { return 'banks_index'; } /** * Convert this model to a searchable array. * * @return array */ public function toSearchableArray() { // Prepare eager loaded relationships $this->load( 'languages', 'locations.district.translations', 'locations.city.translations', 'locations.division.translations', 'locations.country.translations', 'tags.contexts.tags', 'tags.contexts.tags.locale', 'tags.contexts.category.ancestorsAndSelf', ); return [ 'id' => $this->id, 'name' => $this->name, //TODO: Update to multilang database structure in future 'about_nl' => $this->about, 'about_en' => $this->about, 'about_fr' => $this->about, 'about_de' => $this->about, 'about_es' => $this->about, 'about_short_nl' => $this->about_short, 'about_short_en' => $this->about_short, 'about_short_fr' => $this->about_short, 'about_short_de' => $this->about_short, 'about_short_es' => $this->about_short, 'motivation_nl' => $this->motivation, 'motivation_en' => $this->motivation, 'motivation_fr' => $this->motivation, 'motivation_de' => $this->motivation, 'motivation_es' => $this->motivation, 'website' => $this->website, 'last_login_at' => $this->last_login_at, 'lang_preference' => $this->lang_preference, 'languages' => $this->languages->map(function ($language) { // map() as languages is a collection return [ 'id' => $language->id, 'name' => $language->name, 'lang_code' => $language->lang_code, ]; }), 'locations' => $this->locations->map(function ($location) { // map() as locations is a collection return [ 'id' => $location->id, 'district' => $location->district ? $location->district->translations->map(function ($translation) { // map() as translations is a collection return $translation->name; })->toArray() : [], 'city' => $location->city ? $location->city->translations->map(function ($translation) { // map() as translations is a collection return $translation->name; })->toArray() : [], 'division' => $location->division ? $location->division->translations->map(function ($translation) { // map() as translations is a collection return $translation->name; })->toArray() : [], 'country' => $location->country ? $location->country->translations->map(function ($translation) { // map() as translations is a collection return $translation->name; })->toArray() : [], ]; }), 'tags' => $this->tags->map(function ($tag) { return [ 'contexts' => $tag->contexts ->map(function ($context) { return [ 'tags' => $context->tags->map(function ($tag) { // Include the locale in the field name for tags return [ 'name_' . $tag->locale->locale => StringHelper::DutchTitleCase($tag->normalized), ]; }), 'categories' => Category::with(['translations' => function ($query) { $query->select('category_id', 'locale', 'name');}]) ->find([ $context->category->ancestorsAndSelf()->get()->flatMap(function ($related) { $categoryPath = explode('.', $related->path); return $categoryPath; }) ->unique()->values()->toArray() ])->map(function ($category) { // Include the locale in the field name for categories return $category->translations->mapWithKeys(function ($translation) { return ['name_' . $translation->locale => StringHelper::DutchTitleCase($translation->name)]; }); }), ]; }), ]; }) ]; } /** * Get the bank's related admin's. * Many-to-many. */ public function admins() { return $this->belongsToMany(Admin::class); } /** * Get all of the bank's accounts. * One-to-many polymorphic. * * @return void */ public function accounts() { return $this->morphMany(Account::class, 'accountable'); } /** * Get the bank's manager(s) that can manage bank profiles. * Many-to-many. */ public function managers() { return $this->belongsToMany(User::class, 'bank_user', 'bank_id', 'user_id'); } /** * Retrieve all related bank clients. * * @return \Illuminate\Support\Collection */ public function clients() { return $this->userClients->merge($this->organizationClients); } public function userClients() { return $this->morphedByMany(User::class, 'client', 'bank_clients') ->withPivot('relationship_type'); } public function organizationClients() { return $this->morphedByMany(Organization::class, 'client', 'bank_clients') ->withPivot('relationship_type'); } /** * Get all related the locations of the bank. * One-to-many polymorph. */ public function locations() { return $this->morphMany(Location::class, 'locatable'); } /** * Get all of the languages for the organization. * Many-to-many polymorphic. */ public function languages() { return $this->morphToMany(Language::class, 'languagable')->withPivot('competence'); } /** * Get all of the social for the organization. * Many-to-many polymorphic. */ public function socials() { return $this->morphToMany(Social::class, 'sociable')->withPivot('id', 'user_on_social', 'server_of_social'); } /** * Get all of the bank's message settings. */ public function message_settings() { return $this->morphMany(MessageSetting::class, 'message_settingable'); } public function sendEmailVerificationNotification() { \Mail::to($this->email)->send(new \App\Mail\VerifyProfileEmailMailable($this)); } /** * Send the password reset notification. * * @param string $token * @return void */ public function sendPasswordResetNotification($token) { // Construct the full reset URL using the named route // The route 'non-user.password.reset' expects 'profileType' and 'token' parameters. $resetUrl = route('non-user.password.reset', [ 'profileType' => 'bank', // Hardcode 'admin' for the Admin model 'token' => $token, 'email' => $this->getEmailForPasswordReset(), // Method from CanResetPassword trait ]); // Pass the fully constructed URL to your notification $this->notify(new NonUserPasswordResetNotification($resetUrl, $this)); } /** * Configure the activity log options for the User model. * * This method sets up the logging to: * - Only log changes to the 'name', 'password', and 'last_login_ip' attributes. * - Log only when these attributes have been modified (dirty). * - Prevent submission of empty logs. * - Use 'user' as the log name. * * @return \Spatie\Activitylog\LogOptions */ public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() ->logOnly([ 'name', 'last_login_ip', 'inactive_at', 'deleted_at', ]) ->logOnlyDirty() // Only log attributes that have been changed ->dontSubmitEmptyLogs() ->useLogName('Bank'); } /** * Wirechat: Returns the URL for the user's cover image (avatar). * Adjust the 'avatar_url' field to your database setup. */ public function getCoverUrlAttribute(): ?string { return Storage::url($this->profile_photo_path) ?? null; } /** * Wirechat:Returns the URL for the user's profile page. * Adjust the 'profile' route as needed for your setup. */ public function getProfileUrlAttribute(): ?string { return route('profile.show_by_type_and_id', ['type' => __('bank'), 'id' => $this->id]); } /** * Wirechat: Returns the display name for the user. * Modify this to use your preferred name field. */ public function getDisplayNameAttribute(): ?string { if ($this->full_name !== $this->name) { return $this->full_name . ' (' . $this->name . ')'; } elseif ($this->name) { return $this->name; } else { return __('Bank'); } } /** * Wirechat: Search for users when creating a new chat or adding members to a group. * Customize the search logic to limit results, such as restricting to friends or eligible users only. */ public function searchChatables(string $query): ?\Illuminate\Support\Collection { $searchableFields = ['name', 'full_name']; // Search across all profile types $users = \App\Models\User::where(function ($queryBuilder) use ($searchableFields, $query) { foreach ($searchableFields as $field) { $queryBuilder->orWhere($field, 'LIKE', '%'.$query.'%'); } })->limit(6)->get(); $organizations = \App\Models\Organization::where(function ($queryBuilder) use ($searchableFields, $query) { foreach ($searchableFields as $field) { $queryBuilder->orWhere($field, 'LIKE', '%'.$query.'%'); } })->limit(6)->get(); $banks = Bank::where(function ($queryBuilder) use ($searchableFields, $query) { foreach ($searchableFields as $field) { $queryBuilder->orWhere($field, 'LIKE', '%'.$query.'%'); } })->limit(6)->get(); $admins = \App\Models\Admin::where(function ($queryBuilder) use ($searchableFields, $query) { foreach ($searchableFields as $field) { $queryBuilder->orWhere($field, 'LIKE', '%'.$query.'%'); } })->limit(6)->get(); // Combine all results into a base Collection to avoid serialization issues $results = collect() ->merge($users->all()) ->merge($organizations->all()) ->merge($banks->all()) ->merge($admins->all()); // Filter out profiles based on configuration return $results->filter(function ($profile) { // Check inactive profiles if (timebank_config('profile_inactive.messenger_hidden') && !$profile->isActive()) { return false; } // Check email unverified profiles if (timebank_config('profile_email_unverified.messenger_hidden') && !$profile->isEmailVerified()) { return false; } // Check incomplete profiles if ( timebank_config('profile_incomplete.messenger_hidden') && method_exists($profile, 'hasIncompleteProfile') && $profile->hasIncompleteProfile($profile) ) { return false; } return true; })->take(6)->values(); } /** * Wirechat: Determine if the user can create new groups. */ public function canCreateGroups(): bool { return timebank_config('profiles.bank.messenger_can_create_groups'); } /** * Check if the Bank has any transactions with another model (User, Organization, Bank). * * @param \Illuminate\Database\Eloquent\Model $model * @return bool */ public function hasTransactionsWith($model): bool { $modelClass = get_class($model); $modelId = $model->id; // Check if this Bank is involved in any transaction where the other model is either the sender or receiver return Transaction::where(function ($query) use ($modelClass, $modelId) { $query->whereHas('accountFrom.accountable', function ($q) use ($modelClass, $modelId) { $q->where('accountable_type', $modelClass) ->where('accountable_id', $modelId); }) ->orWhereHas('accountTo.accountable', function ($q) use ($modelClass, $modelId) { $q->where('accountable_type', $modelClass) ->where('accountable_id', $modelId); }); }) ->where(function ($query) { $query->whereHas('accountFrom.accountable', function ($q) { $q->where('accountable_type', Bank::class) ->where('accountable_id', $this->id); }) ->orWhereHas('accountTo.accountable', function ($q) { $q->where('accountable_type', Bank::class) ->where('accountable_id', $this->id); }); }) ->exists(); } }