optimizations
This commit is contained in:
169
app/Services/CollectionBulkActionService.php
Normal file
169
app/Services/CollectionBulkActionService.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Collection;
|
||||
use App\Models\User;
|
||||
use App\Services\CollectionBackgroundJobService;
|
||||
use App\Services\CollectionCampaignService;
|
||||
use App\Services\CollectionService;
|
||||
use App\Services\CollectionWorkflowService;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class CollectionBulkActionService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CollectionService $collections,
|
||||
private readonly CollectionCampaignService $campaigns,
|
||||
private readonly CollectionWorkflowService $workflow,
|
||||
private readonly CollectionBackgroundJobService $backgroundJobs,
|
||||
) {
|
||||
}
|
||||
|
||||
public function apply(User $user, array $payload): array
|
||||
{
|
||||
$action = (string) $payload['action'];
|
||||
$collectionIds = collect($payload['collection_ids'] ?? [])
|
||||
->map(static fn ($id): int => (int) $id)
|
||||
->filter(static fn (int $id): bool => $id > 0)
|
||||
->unique()
|
||||
->values();
|
||||
|
||||
if ($collectionIds->isEmpty()) {
|
||||
throw ValidationException::withMessages([
|
||||
'collection_ids' => 'Select at least one collection.',
|
||||
]);
|
||||
}
|
||||
|
||||
$collections = Collection::query()
|
||||
->ownedBy((int) $user->id)
|
||||
->whereIn('id', $collectionIds->all())
|
||||
->get()
|
||||
->keyBy(fn (Collection $collection): int => (int) $collection->id);
|
||||
|
||||
if ($collections->count() !== $collectionIds->count()) {
|
||||
throw ValidationException::withMessages([
|
||||
'collection_ids' => 'One or more selected collections are unavailable.',
|
||||
]);
|
||||
}
|
||||
|
||||
$items = [];
|
||||
$updatedCollections = $collectionIds->map(function (int $collectionId) use ($collections, $user, $payload, $action, &$items): Collection {
|
||||
$collection = $collections->get($collectionId);
|
||||
|
||||
if (! $collection instanceof Collection) {
|
||||
throw ValidationException::withMessages([
|
||||
'collection_ids' => 'One or more selected collections are unavailable.',
|
||||
]);
|
||||
}
|
||||
|
||||
$updated = match ($action) {
|
||||
'archive' => $this->archive($collection->loadMissing('user'), $user),
|
||||
'assign_campaign' => $this->assignCampaign($collection->loadMissing('user'), $payload, $user),
|
||||
'update_lifecycle' => $this->updateLifecycle($collection->loadMissing('user'), $payload, $user),
|
||||
'request_ai_review' => $this->requestAiReview($collection->loadMissing('user'), $user, $items),
|
||||
'mark_editorial_review' => $this->markEditorialReview($collection->loadMissing('user'), $user),
|
||||
default => throw ValidationException::withMessages([
|
||||
'action' => 'Unsupported bulk action.',
|
||||
]),
|
||||
};
|
||||
if ($action !== 'request_ai_review') {
|
||||
$items[] = [
|
||||
'collection_id' => (int) $updated->id,
|
||||
'action' => $action,
|
||||
];
|
||||
}
|
||||
|
||||
return $updated;
|
||||
});
|
||||
|
||||
return [
|
||||
'action' => $action,
|
||||
'count' => $updatedCollections->count(),
|
||||
'items' => $items,
|
||||
'collections' => $updatedCollections,
|
||||
'message' => $this->messageFor($action, $updatedCollections->count()),
|
||||
];
|
||||
}
|
||||
|
||||
private function archive(Collection $collection, User $user): Collection
|
||||
{
|
||||
return $this->collections->updateCollection($collection, [
|
||||
'lifecycle_state' => Collection::LIFECYCLE_ARCHIVED,
|
||||
'archived_at' => now(),
|
||||
], $user);
|
||||
}
|
||||
|
||||
private function assignCampaign(Collection $collection, array $payload, User $user): Collection
|
||||
{
|
||||
return $this->campaigns->updateCampaign($collection, [
|
||||
'campaign_key' => (string) $payload['campaign_key'],
|
||||
'campaign_label' => $payload['campaign_label'] ?? null,
|
||||
], $user);
|
||||
}
|
||||
|
||||
private function updateLifecycle(Collection $collection, array $payload, User $user): Collection
|
||||
{
|
||||
$lifecycleState = (string) $payload['lifecycle_state'];
|
||||
$attributes = [
|
||||
'lifecycle_state' => $lifecycleState,
|
||||
];
|
||||
|
||||
if ($lifecycleState === Collection::LIFECYCLE_ARCHIVED) {
|
||||
$attributes['archived_at'] = now();
|
||||
} else {
|
||||
$attributes['archived_at'] = null;
|
||||
}
|
||||
|
||||
if ($lifecycleState === Collection::LIFECYCLE_PUBLISHED) {
|
||||
$attributes['visibility'] = Collection::VISIBILITY_PUBLIC;
|
||||
$attributes['published_at'] = $collection->published_at ?? now();
|
||||
$attributes['expired_at'] = null;
|
||||
}
|
||||
|
||||
if ($lifecycleState === Collection::LIFECYCLE_DRAFT) {
|
||||
$attributes['visibility'] = Collection::VISIBILITY_PRIVATE;
|
||||
$attributes['expired_at'] = null;
|
||||
$attributes['unpublished_at'] = null;
|
||||
}
|
||||
|
||||
return $this->collections->updateCollection($collection, $attributes, $user);
|
||||
}
|
||||
|
||||
private function requestAiReview(Collection $collection, User $user, array &$items): Collection
|
||||
{
|
||||
$result = $this->backgroundJobs->dispatchQualityRefresh($collection, $user);
|
||||
$items[] = [
|
||||
'collection_id' => (int) $collection->id,
|
||||
'job' => $result['job'] ?? 'quality_refresh',
|
||||
'status' => $result['status'] ?? 'queued',
|
||||
];
|
||||
|
||||
return $collection->fresh()->loadMissing('user');
|
||||
}
|
||||
|
||||
private function markEditorialReview(Collection $collection, User $user): Collection
|
||||
{
|
||||
if ($collection->workflow_state === Collection::WORKFLOW_IN_REVIEW) {
|
||||
return $collection->fresh()->loadMissing('user');
|
||||
}
|
||||
|
||||
return $this->workflow->update($collection, [
|
||||
'workflow_state' => Collection::WORKFLOW_IN_REVIEW,
|
||||
], $user);
|
||||
}
|
||||
|
||||
private function messageFor(string $action, int $count): string
|
||||
{
|
||||
return match ($action) {
|
||||
'archive' => $count === 1 ? 'Collection archived.' : sprintf('%d collections archived.', $count),
|
||||
'assign_campaign' => $count === 1 ? 'Campaign assigned.' : sprintf('Campaign assigned to %d collections.', $count),
|
||||
'update_lifecycle' => $count === 1 ? 'Collection lifecycle updated.' : sprintf('Lifecycle updated for %d collections.', $count),
|
||||
'request_ai_review' => $count === 1 ? 'AI review requested.' : sprintf('AI review requested for %d collections.', $count),
|
||||
'mark_editorial_review' => $count === 1 ? 'Collection marked for editorial review.' : sprintf('%d collections marked for editorial review.', $count),
|
||||
default => 'Bulk action completed.',
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user