162 lines
5.7 KiB
PHP
162 lines
5.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\NovaCards;
|
|
|
|
use App\Jobs\UpdateNovaCardStatsJob;
|
|
use App\Models\NovaCard;
|
|
use App\Models\NovaCardCollection;
|
|
use App\Models\NovaCardCollectionItem;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Str;
|
|
|
|
class NovaCardCollectionService
|
|
{
|
|
public function createCollection(User $user, array $attributes): NovaCardCollection
|
|
{
|
|
$name = trim((string) ($attributes['name'] ?? 'Saved Cards'));
|
|
$slug = $this->uniqueSlug($user, Str::slug($attributes['slug'] ?? $name) ?: 'saved-cards');
|
|
|
|
return NovaCardCollection::query()->create([
|
|
'user_id' => $user->id,
|
|
'slug' => $slug,
|
|
'name' => $name,
|
|
'description' => $attributes['description'] ?? null,
|
|
'visibility' => $attributes['visibility'] ?? NovaCardCollection::VISIBILITY_PRIVATE,
|
|
'official' => false,
|
|
]);
|
|
}
|
|
|
|
public function createManagedCollection(array $attributes): NovaCardCollection
|
|
{
|
|
$owner = User::query()->findOrFail((int) $attributes['user_id']);
|
|
$name = trim((string) ($attributes['name'] ?? 'Untitled Collection'));
|
|
$slug = $this->uniqueSlug($owner, Str::slug($attributes['slug'] ?? $name) ?: 'nova-collection');
|
|
|
|
return NovaCardCollection::query()->create([
|
|
'user_id' => $owner->id,
|
|
'slug' => $slug,
|
|
'name' => $name,
|
|
'description' => $attributes['description'] ?? null,
|
|
'visibility' => $attributes['visibility'] ?? NovaCardCollection::VISIBILITY_PUBLIC,
|
|
'official' => (bool) ($attributes['official'] ?? false),
|
|
'featured' => (bool) ($attributes['featured'] ?? false),
|
|
]);
|
|
}
|
|
|
|
public function listCollections(User $user): array
|
|
{
|
|
return NovaCardCollection::query()
|
|
->withCount('items')
|
|
->where('user_id', $user->id)
|
|
->orderByDesc('updated_at')
|
|
->get()
|
|
->map(fn (NovaCardCollection $collection): array => [
|
|
'id' => (int) $collection->id,
|
|
'slug' => (string) $collection->slug,
|
|
'name' => (string) $collection->name,
|
|
'description' => $collection->description,
|
|
'visibility' => (string) $collection->visibility,
|
|
'featured' => (bool) $collection->featured,
|
|
'cards_count' => (int) $collection->cards_count,
|
|
'items_count' => (int) $collection->items_count,
|
|
])
|
|
->values()
|
|
->all();
|
|
}
|
|
|
|
public function saveCard(User $user, NovaCard $card, ?int $collectionId = null, ?string $note = null): NovaCardCollection
|
|
{
|
|
$collection = $collectionId
|
|
? NovaCardCollection::query()->where('user_id', $user->id)->findOrFail($collectionId)
|
|
: $this->defaultCollection($user);
|
|
|
|
$this->addCardToCollection($collection, $card, $note);
|
|
|
|
return $collection->refresh();
|
|
}
|
|
|
|
public function addCardToCollection(NovaCardCollection $collection, NovaCard $card, ?string $note = null, ?int $sortOrder = null): NovaCardCollectionItem
|
|
{
|
|
$sortOrder ??= (int) NovaCardCollectionItem::query()->where('collection_id', $collection->id)->max('sort_order') + 1;
|
|
|
|
$item = NovaCardCollectionItem::query()->updateOrCreate([
|
|
'collection_id' => $collection->id,
|
|
'card_id' => $card->id,
|
|
], [
|
|
'note' => $note,
|
|
'sort_order' => $sortOrder,
|
|
]);
|
|
|
|
$this->refreshCounts($collection, $card);
|
|
|
|
return $item;
|
|
}
|
|
|
|
public function unsaveCard(User $user, NovaCard $card, ?int $collectionId = null): void
|
|
{
|
|
$query = NovaCardCollectionItem::query()
|
|
->where('card_id', $card->id)
|
|
->whereHas('collection', fn ($builder) => $builder->where('user_id', $user->id));
|
|
|
|
if ($collectionId !== null) {
|
|
$query->where('collection_id', $collectionId);
|
|
}
|
|
|
|
$collectionIds = $query->pluck('collection_id')->unique()->all();
|
|
$query->delete();
|
|
|
|
foreach ($collectionIds as $id) {
|
|
$collection = NovaCardCollection::query()->find($id);
|
|
if ($collection) {
|
|
$this->refreshCounts($collection, $card);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function removeCardFromCollection(NovaCardCollection $collection, NovaCard $card): void
|
|
{
|
|
NovaCardCollectionItem::query()
|
|
->where('collection_id', $collection->id)
|
|
->where('card_id', $card->id)
|
|
->delete();
|
|
|
|
$this->refreshCounts($collection, $card);
|
|
}
|
|
|
|
public function defaultCollection(User $user): NovaCardCollection
|
|
{
|
|
return NovaCardCollection::query()->firstOrCreate([
|
|
'user_id' => $user->id,
|
|
'slug' => 'saved-cards',
|
|
], [
|
|
'name' => 'Saved Cards',
|
|
'description' => 'Private library of Nova Cards saved for remixing, referencing, and future collections.',
|
|
'visibility' => NovaCardCollection::VISIBILITY_PRIVATE,
|
|
'official' => false,
|
|
]);
|
|
}
|
|
|
|
private function refreshCounts(NovaCardCollection $collection, NovaCard $card): void
|
|
{
|
|
$collection->forceFill([
|
|
'cards_count' => NovaCardCollectionItem::query()->where('collection_id', $collection->id)->count(),
|
|
])->save();
|
|
|
|
UpdateNovaCardStatsJob::dispatch($card->id);
|
|
}
|
|
|
|
private function uniqueSlug(User $user, string $base): string
|
|
{
|
|
$slug = $base;
|
|
$suffix = 2;
|
|
|
|
while (NovaCardCollection::query()->where('user_id', $user->id)->where('slug', $slug)->exists()) {
|
|
$slug = $base . '-' . $suffix;
|
|
$suffix++;
|
|
}
|
|
|
|
return $slug;
|
|
}
|
|
} |