Save workspace changes
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user