Allow artworks to have one primary category and extra secondaries.
Keep artwork_category as membership, store a deterministic primary_category_id, and let upload plus studio pick up to five additional same-type categories without changing canonical URLs.
This commit is contained in:
@@ -201,7 +201,7 @@ final class ArtworkStudioProvider implements CreatorStudioProvider
|
||||
? 'scheduled'
|
||||
: ((bool) $artwork->is_public ? 'published' : 'draft'));
|
||||
|
||||
$category = $artwork->categories->first();
|
||||
$category = $artwork->resolvedPrimaryCategory();
|
||||
$visibility = $artwork->visibility ?: ((bool) $artwork->is_public ? Artwork::VISIBILITY_PUBLIC : Artwork::VISIBILITY_PRIVATE);
|
||||
|
||||
return [
|
||||
|
||||
@@ -211,9 +211,13 @@ final class StudioAiAssistService
|
||||
}
|
||||
|
||||
$categoryId = $this->resolveCategoryId($payload);
|
||||
$secondaryCategoryIds = array_key_exists('secondary_category_ids', $payload)
|
||||
? array_values(array_map('intval', (array) $payload['secondary_category_ids']))
|
||||
: null;
|
||||
|
||||
if ($categoryId !== null) {
|
||||
$artwork->categories()->sync([$categoryId]);
|
||||
app(\App\Services\Artworks\ArtworkCategoryService::class)
|
||||
->sync($artwork, $categoryId, $secondaryCategoryIds, 'ai_applied', false);
|
||||
$artwork->category_source = 'ai_applied';
|
||||
$updated = true;
|
||||
$applied[] = 'category';
|
||||
@@ -294,10 +298,10 @@ final class StudioAiAssistService
|
||||
*/
|
||||
public function payloadFor(Artwork $artwork): array
|
||||
{
|
||||
$artwork->loadMissing(['artworkAiAssist', 'tags', 'categories.contentType']);
|
||||
$artwork->loadMissing(['artworkAiAssist', 'tags', 'categories.contentType', 'primaryCategory.contentType']);
|
||||
$assist = $artwork->artworkAiAssist;
|
||||
|
||||
$primaryCategory = $artwork->categories->first();
|
||||
$primaryCategory = $artwork->resolvedPrimaryCategory();
|
||||
|
||||
if (! $assist) {
|
||||
return [
|
||||
@@ -515,6 +519,13 @@ final class StudioAiAssistService
|
||||
'description' => (string) ($artwork->description ?? ''),
|
||||
'tags' => $artwork->tags->pluck('slug')->values()->all(),
|
||||
'category_id' => $primaryCategory?->id,
|
||||
'primary_category_id' => $primaryCategory?->id,
|
||||
'secondary_category_ids' => $artwork->categories
|
||||
->reject(fn ($category): bool => (int) $category->id === (int) ($primaryCategory?->id ?? 0))
|
||||
->pluck('id')
|
||||
->map(fn ($id): int => (int) $id)
|
||||
->values()
|
||||
->all(),
|
||||
'content_type_id' => $primaryCategory?->contentType?->id,
|
||||
'sources' => [
|
||||
'title' => $artwork->title_source ?: 'manual',
|
||||
@@ -530,6 +541,10 @@ final class StudioAiAssistService
|
||||
*/
|
||||
private function resolveCategoryId(array $payload): ?int
|
||||
{
|
||||
if (isset($payload['primary_category_id']) && $payload['primary_category_id'] !== null) {
|
||||
return (int) $payload['primary_category_id'];
|
||||
}
|
||||
|
||||
if (isset($payload['category_id']) && $payload['category_id'] !== null) {
|
||||
return (int) $payload['category_id'];
|
||||
}
|
||||
|
||||
@@ -49,8 +49,9 @@ final class StudioAiCategoryMapper
|
||||
$selectedCategory,
|
||||
$this->confidenceForModel($categoryScores, $selectedCategory->id),
|
||||
$categoryScores
|
||||
->reject(fn (array $row): bool => (int) $row['model']->id === (int) $selectedCategory->id)
|
||||
->take(3)
|
||||
->reject(fn (array $row): bool => (int) $row['model']->id === (int) $selectedCategory->id
|
||||
|| (int) $row['model']->content_type_id !== (int) $selectedCategory->content_type_id)
|
||||
->take(max(0, (int) config('categories.max_secondary_categories', 5)))
|
||||
->map(fn (array $row): array => $this->serializeCategory($row['model'], $row['confidence']))
|
||||
->all()
|
||||
) : null,
|
||||
@@ -245,6 +246,7 @@ final class StudioAiCategoryMapper
|
||||
'root_category_id' => (int) $rootCategory->id,
|
||||
'sub_category_id' => $category->parent_id ? (int) $category->id : null,
|
||||
'alternatives' => array_values($alternatives),
|
||||
'secondary_categories' => array_values($alternatives),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,8 @@ final class StudioBulkActionService
|
||||
throw new \InvalidArgumentException('category_id required for change_category');
|
||||
}
|
||||
|
||||
$artwork->categories()->sync([(int) $params['category_id']]);
|
||||
app(\App\Services\Artworks\ArtworkCategoryService::class)
|
||||
->sync($artwork, (int) $params['category_id'], [], 'moderator');
|
||||
}
|
||||
|
||||
private function addTags(Artwork $artwork, array $params): void
|
||||
|
||||
Reference in New Issue
Block a user