trim($name) ?: self::UNKNOWN_NAME]); } public function albums(): HasMany { return $this->hasMany(Album::class); } public function songs(): HasMany { return $this->hasMany(Song::class); } protected function isUnknown(): Attribute { return Attribute::get(fn (): bool => $this->id === self::UNKNOWN_ID); } protected function isVarious(): Attribute { return Attribute::get(fn (): bool => $this->id === self::VARIOUS_ID); } /** * Sometimes the tags extracted from getID3 are HTML entity encoded. * This makes sure they are always sane. */ protected function name(): Attribute { return Attribute::get(static fn (string $value): string => html_entity_decode($value) ?: self::UNKNOWN_NAME); } /** * Turn the image name into its absolute URL. */ protected function image(): Attribute { return Attribute::get(static fn (?string $value): ?string => artist_image_url($value)); } protected function imagePath(): Attribute { return Attribute::get(fn (): ?string => artist_image_path(Arr::get($this->attributes, 'image'))); } protected function hasImage(): Attribute { return Attribute::get(function (): bool { $image = Arr::get($this->attributes, 'image'); return $image && file_exists(artist_image_path($image)); }); } public function scopeIsStandard(Builder $query): Builder { return $query->whereNotIn('artists.id', [self::UNKNOWN_ID, self::VARIOUS_ID]); } public static function withMeta(User $scopedUser): BuilderContract { return static::query() ->leftJoin('songs', 'artists.id', '=', 'songs.artist_id') ->leftJoin('interactions', static function (JoinClause $join) use ($scopedUser): void { $join->on('interactions.song_id', '=', 'songs.id') ->where('interactions.user_id', $scopedUser->id); }) ->groupBy('artists.id') ->select(['artists.*', DB::raw('CAST(SUM(interactions.play_count) AS UNSIGNED) AS play_count')]) ->withCount('albums AS album_count', 'songs AS song_count') ->withSum('songs AS length', 'length'); } /** @return array */ public function toSearchableArray(): array { return [ 'id' => $this->id, 'name' => $this->name, ]; } }