$animethemes * @property Collection $artists * @property Collection $performances * @property Collection $resources * @property int $song_id * @property string|null $title * @property string|null $title_native * * @method static SongFactory factory(...$parameters) */ #[Table(Song::TABLE, Song::ATTRIBUTE_ID)] class Song extends BaseModel implements Auditable, HasResources, SoftDeletable { use HasAudits; use HasFactory; use Searchable; use SoftDeletes; final public const string TABLE = 'songs'; final public const string ATTRIBUTE_ID = 'song_id'; final public const string ATTRIBUTE_TITLE = 'title'; final public const string ATTRIBUTE_TITLE_NATIVE = 'title_native'; final public const string RELATION_ANIME = 'animethemes.anime'; final public const string RELATION_ANIMETHEMES = 'animethemes'; final public const string RELATION_ARTISTS = 'artists'; final public const string RELATION_PERFORMANCES = 'performances'; final public const string RELATION_PERFORMANCES_ARTIST = 'performances.artist'; final public const string RELATION_PERFORMANCES_MEMBER = 'performances.member'; final public const string RELATION_RESOURCES = 'resources'; final public const string RELATION_THEME_GROUPS = 'animethemes.group'; final public const string RELATION_VIDEOS = 'animethemes.animethemeentries.videos'; /** * The event map for the model. * * Allows for object-based events for native Eloquent events. * * @var array */ protected $dispatchesEvents = [ 'created' => SongCreated::class, 'deleted' => SongDeleted::class, 'forceDeleting' => SongForceDeleting::class, 'restored' => SongRestored::class, 'updated' => SongUpdated::class, ]; /** * The attributes that are mass assignable. * * @var list */ protected $fillable = [ Song::ATTRIBUTE_TITLE, Song::ATTRIBUTE_TITLE_NATIVE, ]; /** * Get the attributes that should be cast. * * @return array */ protected function casts(): array { return [ Song::ATTRIBUTE_TITLE => 'string', Song::ATTRIBUTE_TITLE_NATIVE => 'string', ]; } /** * Get the indexable data array for the model. */ public function toSearchableArray(): array { return match ($driver = Config::get('scout.driver')) { 'collection', 'elastic' => SongElasticModel::toSearchableArray($this), 'typesense' => SongTypesenseModel::toSearchableArray($this), default => throw new RuntimeException("Unsupported {$driver} search driver configured."), }; } public function getName(): string { if (blank($this->title)) { return strval($this->getKey()); } return $this->title; } public function getSubtitle(): string { if ($this->animethemes()->count() !== 0 && $this->animethemes->first()->anime !== null) { return "{$this->animethemes->first()->anime->getName()} {$this->animethemes->first()->slug}"; } return $this->title_native ?? strval($this->getKey()); } /** * @return HasMany */ public function animethemes(): HasMany { return $this->hasMany(AnimeTheme::class, AnimeTheme::ATTRIBUTE_SONG); } #[Deprecated] public function artists(): BelongsToMany { $sub = Performance::query() ->selectRaw('MIN(performance_id) as performance_id') ->groupBy(Performance::ATTRIBUTE_SONG, Performance::ATTRIBUTE_ARTIST); return $this->belongsToMany(Artist::class, Performance::TABLE, Performance::ATTRIBUTE_SONG, Performance::ATTRIBUTE_ARTIST) ->joinSub($sub, 'unique_performances', function ($join): void { $join->on('performances.performance_id', '=', 'unique_performances.performance_id'); }) ->as('artistsong') ->withPivot([Performance::ATTRIBUTE_ID, Performance::ATTRIBUTE_ALIAS, Performance::ATTRIBUTE_AS]) ->withTimestamps(); } /** * @return HasMany */ public function performances(): HasMany { return $this->hasMany(Performance::class, Performance::ATTRIBUTE_SONG); } /** * @return MorphToMany */ public function resources(): MorphToMany { return $this->morphToMany(ExternalResource::class, Resourceable::RELATION_RESOURCEABLE, Resourceable::TABLE, Resourceable::ATTRIBUTE_RESOURCEABLE_ID, Resourceable::ATTRIBUTE_RESOURCE) ->using(Resourceable::class) ->as('songresource') ->withPivot([Resourceable::ATTRIBUTE_ID, Resourceable::ATTRIBUTE_AS]) ->withTimestamps(); } }