67 lines
2.2 KiB
PHP
67 lines
2.2 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Services;
|
||
|
|
|
||
|
|
use App\Actions\Prompts\ListPrompts;
|
||
|
|
use App\Models\Prompt;
|
||
|
|
use App\Models\Tag;
|
||
|
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||
|
|
use Illuminate\Database\Eloquent\Builder;
|
||
|
|
use Illuminate\Database\Eloquent\Collection;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Read side of the prompt domain (queries).
|
||
|
|
*/
|
||
|
|
class PromptCatalog
|
||
|
|
{
|
||
|
|
public const PER_PAGE = 10;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Paginated prompt list with search, tag/favorite filters, and
|
||
|
|
* whitelisted sorting. `last_used_at` keeps NULLs last.
|
||
|
|
*/
|
||
|
|
public function list(ListPrompts $query): LengthAwarePaginator
|
||
|
|
{
|
||
|
|
$sort = $query->normalizedSort();
|
||
|
|
$direction = $query->normalizedDirection();
|
||
|
|
$like = '%' . $query->search . '%';
|
||
|
|
|
||
|
|
return Prompt::query()
|
||
|
|
->with('tags')
|
||
|
|
->when(
|
||
|
|
$query->search !== null && $query->search !== '',
|
||
|
|
fn (Builder $q) => $q->where(
|
||
|
|
fn (Builder $w) => $w
|
||
|
|
->where('name', 'like', $like)
|
||
|
|
->orWhere('description', 'like', $like)
|
||
|
|
->orWhere('body', 'like', $like)
|
||
|
|
),
|
||
|
|
)
|
||
|
|
->when(
|
||
|
|
$query->tag !== null && $query->tag !== '',
|
||
|
|
fn (Builder $q) => $q->whereHas('tags', fn (Builder $t) => $t->where('name', $query->tag)),
|
||
|
|
)
|
||
|
|
->when($query->favoritesOnly, fn (Builder $q) => $q->where('is_favorite', true))
|
||
|
|
->when(
|
||
|
|
$sort === 'last_used_at',
|
||
|
|
// NULLs always sort last: `IS NULL` is 0/1, so a first
|
||
|
|
// ascending pass groups non-NULLs before NULLs in either
|
||
|
|
// direction, then the value itself orders within the group.
|
||
|
|
fn (Builder $q) => $q
|
||
|
|
->orderByRaw('last_used_at IS NULL')
|
||
|
|
->orderBy('last_used_at', $direction),
|
||
|
|
fn (Builder $q) => $q->orderBy($sort, $direction),
|
||
|
|
)
|
||
|
|
->orderBy('id', 'desc')
|
||
|
|
->paginate(self::PER_PAGE);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* All tags, alphabetically (for filter dropdowns).
|
||
|
|
*/
|
||
|
|
public function tags(): Collection
|
||
|
|
{
|
||
|
|
return Tag::query()->orderBy('name')->get();
|
||
|
|
}
|
||
|
|
}
|