'float', 'mtime' => 'int', 'track' => 'int', 'disc' => 'int', ]; protected $keyType = 'string'; public function artist(): BelongsTo { return $this->belongsTo(Artist::class); } public function album(): BelongsTo { return $this->belongsTo(Album::class); } public function playlists(): BelongsToMany { return $this->belongsToMany(Playlist::class); } public function interactions(): HasMany { return $this->hasMany(Interaction::class); } /** * Scope a query to only include songs in a given directory. */ public function scopeInDirectory(Builder $query, string $path): Builder { // Make sure the path ends with a directory separator. $path = rtrim(trim($path), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; return $query->where('path', 'LIKE', "$path%"); } protected function title(): Attribute { return new Attribute( get: fn (?string $value) => $value ?: pathinfo($this->path, PATHINFO_FILENAME), set: static fn (string $value) => html_entity_decode($value) ); } public static function withMeta(User $scopedUser, ?Builder $query = null): Builder { $query ??= static::query(); return $query ->with('artist', 'album', 'album.artist') ->leftJoin('interactions', static function (JoinClause $join) use ($scopedUser): void { $join->on('interactions.song_id', '=', 'songs.id') ->where('interactions.user_id', $scopedUser->id); }) ->join('albums', 'songs.album_id', '=', 'albums.id') ->join('artists', 'songs.artist_id', '=', 'artists.id') ->select( 'songs.*', 'albums.name', 'artists.name', 'interactions.liked', 'interactions.play_count' ); } public function scopeWithMeta(Builder $query, User $scopedUser): Builder { return static::withMeta($scopedUser, $query); } /** @return array */ public function toSearchableArray(): array { $array = [ 'id' => $this->id, 'title' => $this->title, ]; if (!$this->artist->is_unknown && !$this->artist->is_various) { $array['artist'] = $this->artist->name; } return $array; } public function __toString(): string { return $this->id; } }