song = $song instanceof Song ? $song->getKey() : $song; return $this; } public function addSingleArtist(int $artist, ?string $alias = null, ?string $as = null): static { $this->performances[] = [ Performance::ATTRIBUTE_ARTIST_TYPE => Relation::getMorphAlias(Artist::class), Performance::ATTRIBUTE_ARTIST_ID => $artist, Performance::ATTRIBUTE_ALIAS => filled($alias) ? trim($alias) : null, Performance::ATTRIBUTE_AS => filled($as) ? trim($as) : null, ]; return $this; } public function addGroupData(int $group, ?string $alias = null, ?string $as = null): static { $this->groups[$group] = [ Performance::ATTRIBUTE_ALIAS => filled($alias) ? trim($alias) : null, Performance::ATTRIBUTE_AS => filled($as) ? trim($as) : null, ]; return $this; } public function addMembership(int $group, int $member, ?string $alias = null, ?string $as = null): static { $this->performances[] = [ Performance::ATTRIBUTE_ARTIST_TYPE => Relation::getMorphAlias(Membership::class), Performance::ATTRIBUTE_ALIAS => Arr::get($this->groups, "{$group}.".Performance::ATTRIBUTE_ALIAS), Performance::ATTRIBUTE_AS => Arr::get($this->groups, "{$group}.".Performance::ATTRIBUTE_AS), Performance::RELATION_MEMBERSHIP => [ Membership::ATTRIBUTE_ARTIST => $group, Membership::ATTRIBUTE_MEMBER => $member, Membership::ATTRIBUTE_ALIAS => filled($alias) ? trim($alias) : null, Membership::ATTRIBUTE_AS => filled($as) ? trim($as) : null, ], ]; return $this; } public function commit(): static { try { DB::beginTransaction(); $performancesToCreate = []; $memberships = []; foreach ($this->performances as $performance) { $membershipData = Arr::get($performance, Performance::RELATION_MEMBERSHIP); $data = [ ...$performance, Performance::ATTRIBUTE_SONG => $this->song, ]; if ($membershipData) { $membership = Membership::query()->firstOrCreate($membershipData); $memberships[] = $membershipData; $data = [ ...Arr::except($performance, Performance::RELATION_MEMBERSHIP), Performance::ATTRIBUTE_SONG => $this->song, Performance::ATTRIBUTE_ARTIST_TYPE => Relation::getMorphAlias(Membership::class), Performance::ATTRIBUTE_ARTIST_ID => $membership->getKey(), ]; } $performancesToCreate[] = $data; } // Multiple queries because upsert does not dispatch an event. foreach ($performancesToCreate as $performance) { Performance::query()->updateOrCreate( Arr::only($performance, [Performance::ATTRIBUTE_SONG, Performance::ATTRIBUTE_ARTIST_TYPE, Performance::ATTRIBUTE_ARTIST_ID]), [ Performance::ATTRIBUTE_ALIAS => Arr::get($performance, Performance::ATTRIBUTE_ALIAS), Performance::ATTRIBUTE_AS => Arr::get($performance, Performance::ATTRIBUTE_AS), ] ); } // Delete membership performances that are not in the new list. $membershipPerformances = Performance::query() ->where(Performance::ATTRIBUTE_SONG, $this->song) ->where(Performance::ATTRIBUTE_ARTIST_TYPE, Relation::getMorphAlias(Membership::class)) ->whereNotIn( Performance::ATTRIBUTE_ARTIST_ID, Arr::map( Arr::where($performancesToCreate, fn ($performance): bool => $performance[Performance::ATTRIBUTE_ARTIST_TYPE] === Relation::getMorphAlias(Membership::class)), fn ($performanceMembership) => Arr::get($performanceMembership, Performance::ATTRIBUTE_ARTIST_ID) ) ); $membershipPerformances->each(fn (Performance $performance) => $performance->forceDelete()); // Delete solo performances that are not in the new list. $soloPerformances = Performance::query() ->where(Performance::ATTRIBUTE_SONG, $this->song) ->where(Performance::ATTRIBUTE_ARTIST_TYPE, Relation::getMorphAlias(Artist::class)) ->whereNotIn( Performance::ATTRIBUTE_ARTIST_ID, Arr::map( Arr::where($performancesToCreate, fn ($performance): bool => $performance[Performance::ATTRIBUTE_ARTIST_TYPE] === Relation::getMorphAlias(Artist::class)), fn ($solo) => Arr::get($solo, Performance::ATTRIBUTE_ARTIST_ID) ) ); $soloPerformances->each(fn (Performance $performance) => $performance->forceDelete()); Performance::withoutEvents(function () use ($performancesToCreate): void { foreach ($performancesToCreate as $index => $performanceToSort) { Performance::query() ->where(Performance::ATTRIBUTE_SONG, $this->song) ->where(Performance::ATTRIBUTE_ARTIST_TYPE, Arr::get($performanceToSort, Performance::ATTRIBUTE_ARTIST_TYPE)) ->where(Performance::ATTRIBUTE_ARTIST_ID, Arr::get($performanceToSort, Performance::ATTRIBUTE_ARTIST_ID)) ->update([Performance::ATTRIBUTE_RELEVANCE => $index + 1]); } }); // Update artist_member table to match memberships ArtistMember::query()->upsert( $memberships, [ArtistMember::ATTRIBUTE_ARTIST, ArtistMember::ATTRIBUTE_MEMBER], [ArtistMember::ATTRIBUTE_ALIAS, ArtistMember::ATTRIBUTE_AS], ); $this->syncSongArtist(); DB::commit(); } catch (Exception $e) { Log::error($e->getMessage()); DB::rollBack(); throw $e; } return $this; } /** * Temporary function where the performances feature synchronizes with the artist_song pivot table. */ public function syncSongArtist(): void { /** @var Song $song */ $song = Song::query() ->whereKey($this->song) ->first(); $songArtists = []; foreach ($this->performances as $performance) { $groupOrArtist = Arr::get($performance, Performance::RELATION_MEMBERSHIP.'.'.Membership::ATTRIBUTE_ARTIST) ?? Arr::get($performance, Performance::ATTRIBUTE_ARTIST_ID); $songArtists[$groupOrArtist] = [ ArtistSong::ATTRIBUTE_ALIAS => Arr::get($performance, Performance::ATTRIBUTE_ALIAS), ArtistSong::ATTRIBUTE_AS => Arr::get($performance, Performance::ATTRIBUTE_AS), ]; } ArtistSong::withoutEvents(function () use ($song, $songArtists): void { $song->artists()->sync($songArtists); }); } }