Save workspace changes

This commit is contained in:
2026-04-18 17:02:56 +02:00
parent f02ea9a711
commit 87d60af5a9
4220 changed files with 1388603 additions and 1554 deletions

View File

@@ -0,0 +1,148 @@
<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\Artwork;
use App\Models\Group;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
class ArtworkAttributionService
{
public function __construct(
private readonly GroupMembershipService $groupMembers,
private readonly GroupService $groups,
) {
}
public function apply(Artwork $artwork, User $actor, array $attributes, bool $requireDirectPublish = true): Artwork
{
$previousGroupId = (int) ($artwork->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();
}
}