$groups * @property Collection $images * @property string|null $information * @property Collection $members * @property string $name * @property string|null $name_native * @property Collection $performances * @property Collection $resources * @property string $slug * @property Collection $songs * @property Collection $synonyms * * @method static ArtistFactory factory(...$parameters) */ #[Table(Artist::TABLE, Artist::ATTRIBUTE_ID)] class Artist extends BaseModel implements Auditable, HasImages, HasResources, HasSynonyms, SoftDeletable { use HasAudits; use HasFactory; use HasRelationships; use Searchable; use SoftDeletes; final public const string TABLE = 'artists'; final public const string ATTRIBUTE_ID = 'artist_id'; final public const string ATTRIBUTE_NAME = 'name'; final public const string ATTRIBUTE_NAME_NATIVE = 'name_native'; final public const string ATTRIBUTE_SLUG = 'slug'; final public const string ATTRIBUTE_INFORMATION = 'information'; final public const string RELATION_ANIME = 'songs.animethemes.anime'; final public const string RELATION_ANIMETHEMES = 'songs.animethemes'; final public const string RELATION_GROUPS = 'groups'; final public const string RELATION_IMAGES = 'images'; final public const string RELATION_MEMBERS = 'members'; final public const string RELATION_MEMBER_PERFORMANCES = 'memberPerformances'; final public const string RELATION_MEMBER_SONG_STAFF = 'memberSongStaff'; final public const string RELATION_MEMBER_PERFORMANCES_SONG = 'memberPerformances.song'; final public const string RELATION_PERFORMANCES = 'performances'; final public const string RELATION_PERFORMANCES_SONG = 'performances.song'; final public const string RELATION_RESOURCES = 'resources'; final public const string RELATION_SONGS = 'songs'; final public const string RELATION_SONG_STAFF = 'songStaff'; final public const string RELATION_SYNONYMS = 'synonyms'; final public const string RELATION_THEME_GROUPS = 'songs.animethemes.group'; /** * The event map for the model. * * Allows for object-based events for native Eloquent events. * * @var array */ protected $dispatchesEvents = [ 'created' => ArtistCreated::class, 'deleted' => ArtistDeleted::class, 'forceDeleted' => ArtistForceDeleted::class, 'restored' => ArtistRestored::class, 'updated' => ArtistUpdated::class, ]; /** * The attributes that are mass assignable. * * @var list */ protected $fillable = [ Artist::ATTRIBUTE_NAME, Artist::ATTRIBUTE_NAME_NATIVE, Artist::ATTRIBUTE_SLUG, Artist::ATTRIBUTE_INFORMATION, ]; /** * Get the attributes that should be cast. * * @return array */ protected function casts(): array { return [ Artist::ATTRIBUTE_INFORMATION => 'string', Artist::ATTRIBUTE_NAME => 'string', Artist::ATTRIBUTE_NAME_NATIVE => 'string', Artist::ATTRIBUTE_SLUG => 'string', ]; } /** * Modify the query used to retrieve models when making all of the models searchable. */ protected function makeAllSearchableUsing(Builder $query): Builder { return $query->with([ Artist::RELATION_PERFORMANCES, Artist::RELATION_MEMBER_PERFORMANCES, Artist::RELATION_SYNONYMS, ]); } /** * Get the indexable data array for the model. */ public function toSearchableArray(): array { return match ($driver = Config::get('scout.driver')) { 'collection', 'typesense' => $this->toTypesenseArray(), default => throw new RuntimeException("Unsupported {$driver} search driver configured."), }; } /** * @return array */ protected function toTypesenseArray(): array { return [ 'id' => (string) $this->getKey(), 'name' => $this->name, // So TypeSense does not boost when alternative names are the same. 'name_native' => $this->name_native !== $this->name ? $this->name_native : null, 'created_at' => $this->created_at->timestamp, 'synonyms' => $synonyms = $this->synonyms->map(fn (Synonym $synonym) => $synonym->text)->all(), 'as' => $as = $this->performances->map(fn (Performance $performance) => $performance->as) ->toBase() ->concat($this->memberPerformances->map(fn (Performance $performance) => $performance->member_as)) ->filter() ->unique() ->values() ->all(), 'search_text' => implode(' ', [ $this->name, $this->name_native, ...$synonyms, // ...$as, ]), ]; } /** * Get the route key for the model. * * @noinspection PhpMissingParentCallCommonInspection */ public function getRouteKeyName(): string { return Artist::ATTRIBUTE_SLUG; } public function getName(): string { return $this->name; } public function getSubtitle(): string { $displayName = $this->name_native ?? $this->slug; return "({$this->getKey()}) {$displayName}"; } #[Deprecated] public function songs(): BelongsToMany { $sub = Performance::query() ->selectRaw('MIN(id) as song_staff_id') ->groupBy(Performance::ATTRIBUTE_ARTIST, Performance::ATTRIBUTE_SONG); return $this->belongsToMany(Song::class, Performance::TABLE, Performance::ATTRIBUTE_ARTIST, Performance::ATTRIBUTE_SONG) ->joinSub($sub, 'unique_performances', function ($join): void { $join->on('song_staff.id', '=', 'unique_performances.song_staff_id'); }) ->as('artistsong') ->withPivot([ Performance::ATTRIBUTE_ID, Performance::ATTRIBUTE_ALIAS, Performance::ATTRIBUTE_AS, Performance::ATTRIBUTE_ROLE, ]) ->withTimestamps(); } /** * Get the synonyms for the owner model. * * @return MorphMany */ public function synonyms(): MorphMany { return $this->morphMany(Synonym::class, Synonym::RELATION_SYNONYMABLE); } /** * @return HasMany */ public function performances(): HasMany { return $this->hasMany(Performance::class, Performance::ATTRIBUTE_ARTIST); } /** * @return HasMany */ public function songStaff(): HasMany { return $this->hasMany(SongStaff::class, SongStaff::ATTRIBUTE_ARTIST); } /** * @return HasMany */ public function memberPerformances(): HasMany { return $this->hasMany(Performance::class, Performance::ATTRIBUTE_MEMBER); } /** * @return HasMany */ public function memberSongStaff(): HasMany { return $this->hasMany(SongStaff::class, SongStaff::ATTRIBUTE_MEMBER); } /** * @return BelongsToMany */ public function members(): BelongsToMany { return $this->belongsToMany(Artist::class, ArtistMember::TABLE, ArtistMember::ATTRIBUTE_ARTIST, ArtistMember::ATTRIBUTE_MEMBER) ->using(ArtistMember::class) ->as(ArtistMemberJsonResource::$wrap) ->withPivot([ ArtistMember::ATTRIBUTE_ID, ArtistMember::ATTRIBUTE_ALIAS, ArtistMember::ATTRIBUTE_AS, ArtistMember::ATTRIBUTE_NOTES, ArtistMember::ATTRIBUTE_RELEVANCE, ]) ->withTimestamps(); } /** * @return BelongsToMany */ public function groups(): BelongsToMany { return $this->belongsToMany(Artist::class, ArtistMember::TABLE, ArtistMember::ATTRIBUTE_MEMBER, ArtistMember::ATTRIBUTE_ARTIST) ->using(ArtistMember::class) ->as(ArtistMemberJsonResource::$wrap) ->withPivot([ ArtistMember::ATTRIBUTE_ID, ArtistMember::ATTRIBUTE_ALIAS, ArtistMember::ATTRIBUTE_AS, ArtistMember::ATTRIBUTE_NOTES, ArtistMember::ATTRIBUTE_RELEVANCE, ]) ->withTimestamps(); } /** * @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('artistresource') ->withPivot([Resourceable::ATTRIBUTE_ID, Resourceable::ATTRIBUTE_AS]) ->withTimestamps(); } /** * @return MorphToMany */ public function images(): MorphToMany { return $this->morphToMany(Image::class, Imageable::RELATION_IMAGEABLE, Imageable::TABLE, Imageable::ATTRIBUTE_IMAGEABLE_ID, Imageable::ATTRIBUTE_IMAGE) ->using(Imageable::class) ->as('artistimage') ->withPivot([Imageable::ATTRIBUTE_ID, Imageable::ATTRIBUTE_DEPTH]) ->withTimestamps(); } }