group_id ?? 0); $group = $this->resolveGroup($actor, $attributes, $requireDirectPublish); $allowedContributorIds = $group ? $this->groupMembers->activeContributorIds($group) : [(int) $actor->id]; $primaryAuthorId = $this->resolvePrimaryAuthorId($actor, $attributes, $allowedContributorIds); $contributorCredits = $this->normalizeContributorCredits($attributes, $allowedContributorIds, $primaryAuthorId); DB::transaction(function () use ($artwork, $actor, $group, $primaryAuthorId, $contributorCredits): void { $artwork->group()->associate($group); $artwork->uploadedBy()->associate($actor); $artwork->primaryAuthor()->associate(User::query()->findOrFail($primaryAuthorId)); $artwork->published_as_type = $group ? Artwork::PUBLISHED_AS_GROUP : Artwork::PUBLISHED_AS_USER; $artwork->published_as_id = $group?->id ?: (int) $artwork->user_id; $artwork->save(); $artwork->contributors()->delete(); foreach ($contributorCredits as $index => $contributorCredit) { $artwork->contributors()->create([ 'user_id' => $contributorCredit['user_id'], 'credit_role' => $contributorCredit['credit_role'], 'is_primary' => $contributorCredit['is_primary'], 'sort_order' => $index, ]); } }); $artwork->loadMissing(['group.members', 'primaryAuthor.profile', 'contributors.user.profile', 'uploadedBy.profile']); $newGroupId = (int) ($artwork->group_id ?? 0); if ($previousGroupId > 0 && $previousGroupId !== $newGroupId) { $previousGroup = Group::query()->find($previousGroupId); if ($previousGroup) { $this->groups->syncArtworkCount($previousGroup); } } if ($newGroupId > 0) { $this->groups->syncArtworkCount($artwork->group); } return $artwork; } private function resolveGroup(User $actor, array $attributes, bool $requireDirectPublish = true): ?Group { $groupIdentifier = $attributes['group'] ?? $attributes['group_id'] ?? null; if ($groupIdentifier === null || $groupIdentifier === '') { return null; } $group = is_numeric($groupIdentifier) ? Group::query()->with('members')->findOrFail((int) $groupIdentifier) : Group::query()->with('members')->where('slug', (string) $groupIdentifier)->firstOrFail(); $canUseGroup = $requireDirectPublish ? $group->canPublishArtworks($actor) : $group->canCreateArtworkDrafts($actor); if (! $canUseGroup && ! $actor->isAdmin()) { throw ValidationException::withMessages([ 'group' => $requireDirectPublish ? 'You are not allowed to publish as this group.' : 'You are not allowed to submit artwork for this group.', ]); } return $group; } private function resolvePrimaryAuthorId(User $actor, array $attributes, array $allowedContributorIds): int { $primaryAuthorId = isset($attributes['primary_author_user_id']) && is_numeric($attributes['primary_author_user_id']) ? (int) $attributes['primary_author_user_id'] : (int) $actor->id; if (! in_array($primaryAuthorId, $allowedContributorIds, true)) { throw ValidationException::withMessages([ 'primary_author_user_id' => 'The selected primary author is not available for this publishing context.', ]); } return $primaryAuthorId; } private function normalizeContributorCredits(array $attributes, array $allowedContributorIds, int $primaryAuthorId): array { $structuredCredits = collect($attributes['contributor_credits'] ?? []) ->filter(fn ($credit): bool => is_array($credit) && is_numeric($credit['user_id'] ?? null)) ->map(function (array $credit): array { $creditRole = trim((string) ($credit['credit_role'] ?? '')); return [ 'user_id' => (int) $credit['user_id'], 'credit_role' => $creditRole !== '' ? $creditRole : null, 'is_primary' => (bool) ($credit['is_primary'] ?? false), ]; }) ->filter(fn (array $credit): bool => in_array($credit['user_id'], $allowedContributorIds, true)) ->reject(fn (array $credit): bool => $credit['user_id'] === $primaryAuthorId) ->unique('user_id') ->values(); if ($structuredCredits->isEmpty()) { $structuredCredits = collect($attributes['contributor_user_ids'] ?? []) ->filter(fn ($id): bool => is_numeric($id)) ->map(fn ($id): array => [ 'user_id' => (int) $id, 'credit_role' => null, 'is_primary' => false, ]) ->filter(fn (array $credit): bool => in_array($credit['user_id'], $allowedContributorIds, true)) ->reject(fn (array $credit): bool => $credit['user_id'] === $primaryAuthorId) ->unique('user_id') ->values(); } if ($structuredCredits->where('is_primary', true)->count() > 1) { throw ValidationException::withMessages([ 'contributor_credits' => 'Only one contributor can be marked as the lead supporting credit.', ]); } return $structuredCredits->all(); } }