Save workspace changes
This commit is contained in:
@@ -8,6 +8,7 @@ use App\Models\Artwork;
|
||||
use App\Models\Tag;
|
||||
use App\Services\EarlyGrowth\AdaptiveTimeWindow;
|
||||
use App\Services\Maturity\ArtworkMaturityService;
|
||||
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Pagination\LengthAwarePaginator as PaginationLengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -81,11 +82,28 @@ final class ArtworkSearchService
|
||||
$options['sort'] = $sort;
|
||||
}
|
||||
|
||||
$options = $this->viewerAwareOptions($options);
|
||||
|
||||
return Artwork::search($q ?: '')
|
||||
->options($options)
|
||||
$results = Artwork::search($q ?: '')
|
||||
->options($this->viewerAwareOptions($options))
|
||||
->paginate($perPage);
|
||||
|
||||
if (! $this->shouldFallbackToViewerVisibilityFiltering($results)) {
|
||||
return $results;
|
||||
}
|
||||
|
||||
$page = max(1, (int) request()->get('page', 1));
|
||||
$candidateCount = $this->determineSearchCandidatePoolSize($perPage, $page);
|
||||
$fallbackResults = Artwork::search($q ?: '')
|
||||
->options($options)
|
||||
->paginate($candidateCount, 'page', 1);
|
||||
|
||||
$visibleItems = $this->filterSearchCollectionByCatalogVisibility($fallbackResults->getCollection());
|
||||
$offset = max(0, ($page - 1) * $perPage);
|
||||
$slice = $visibleItems->slice($offset, $perPage)->values();
|
||||
$visibleTotal = (int) ($fallbackResults->total() <= $candidateCount
|
||||
? $visibleItems->count()
|
||||
: $fallbackResults->total());
|
||||
|
||||
return $this->makeModelPaginator($slice, $visibleTotal, $perPage, $page);
|
||||
}
|
||||
|
||||
public function searchWithThumbnailPreference(array $options, int $perPage, bool $excludeMissing = false, ?int $page = null): LengthAwarePaginator
|
||||
@@ -96,21 +114,18 @@ final class ArtworkSearchService
|
||||
->options($this->viewerAwareOptions($options))
|
||||
->paginate($candidateCount, 'page', 1);
|
||||
|
||||
$ordered = $this->rerankSearchCollectionByThumbnailHealth($results->getCollection(), $excludeMissing);
|
||||
if ($this->shouldFallbackToViewerVisibilityFiltering($results)) {
|
||||
$results = Artwork::search('')
|
||||
->options($options)
|
||||
->paginate($candidateCount, 'page', 1);
|
||||
}
|
||||
|
||||
$visibleItems = $this->filterSearchCollectionByCatalogVisibility($results->getCollection());
|
||||
$ordered = $this->rerankSearchCollectionByThumbnailHealth($visibleItems, $excludeMissing);
|
||||
$offset = max(0, ($page - 1) * $perPage);
|
||||
$slice = $ordered->slice($offset, $perPage)->values();
|
||||
|
||||
return new PaginationLengthAwarePaginator(
|
||||
$slice->all(),
|
||||
(int) $results->total(),
|
||||
$perPage,
|
||||
$page,
|
||||
[
|
||||
'path' => request()->url(),
|
||||
'query' => request()->query(),
|
||||
'pageName' => 'page',
|
||||
]
|
||||
);
|
||||
return $this->makeModelPaginator($slice, (int) $results->total(), $perPage, $page);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,15 +180,14 @@ final class ArtworkSearchService
|
||||
*/
|
||||
public function byCategory(string $cat, int $perPage = 24, array $filters = []): LengthAwarePaginator
|
||||
{
|
||||
$cacheKey = "search.cat.{$cat}.{$this->viewerCacheSegment()}.page." . request()->get('page', 1);
|
||||
$page = (int) request()->get('page', 1);
|
||||
$cacheKey = "search.cat.catalog-visible.v2.{$cat}.{$this->viewerCacheSegment()}.page." . $page;
|
||||
|
||||
return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($cat, $perPage) {
|
||||
return Artwork::search('')
|
||||
->options($this->viewerAwareOptions([
|
||||
'filter' => self::BASE_FILTER . ' AND category = "' . addslashes($cat) . '"',
|
||||
'sort' => ['created_at:desc'],
|
||||
]))
|
||||
->paginate($perPage);
|
||||
return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($cat, $perPage, $page) {
|
||||
return $this->searchWithThumbnailPreference([
|
||||
'filter' => self::BASE_FILTER . ' AND category = "' . addslashes($cat) . '"',
|
||||
'sort' => ['created_at:desc'],
|
||||
], $perPage, false, $page);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -214,15 +228,13 @@ final class ArtworkSearchService
|
||||
$sort = array_key_exists($sort, self::CATEGORY_SORT_FIELDS) ? $sort : 'trending';
|
||||
$page = (int) request()->get('page', 1);
|
||||
$ttl = self::CATEGORY_SORT_TTL[$sort] ?? self::CACHE_TTL;
|
||||
$cacheKey = "category.{$categorySlug}.{$sort}.{$this->viewerCacheSegment()}.{$page}";
|
||||
$cacheKey = "category.catalog-visible.v2.{$categorySlug}.{$sort}.{$this->viewerCacheSegment()}.{$page}";
|
||||
|
||||
return Cache::remember($cacheKey, $ttl, function () use ($categorySlug, $sort, $perPage) {
|
||||
return Artwork::search('')
|
||||
->options($this->viewerAwareOptions([
|
||||
'filter' => self::BASE_FILTER . ' AND category = "' . addslashes($categorySlug) . '"',
|
||||
'sort' => self::CATEGORY_SORT_FIELDS[$sort],
|
||||
]))
|
||||
->paginate($perPage);
|
||||
return $this->searchWithThumbnailPreference([
|
||||
'filter' => self::BASE_FILTER . ' AND category = "' . addslashes($categorySlug) . '"',
|
||||
'sort' => self::CATEGORY_SORT_FIELDS[$sort],
|
||||
], $perPage, false, (int) request()->get('page', 1));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -237,15 +249,13 @@ final class ArtworkSearchService
|
||||
$sort = array_key_exists($sort, self::CATEGORY_SORT_FIELDS) ? $sort : 'trending';
|
||||
$page = (int) request()->get('page', 1);
|
||||
$ttl = self::CATEGORY_SORT_TTL[$sort] ?? self::CACHE_TTL;
|
||||
$cacheKey = "content_type.{$contentTypeSlug}.{$sort}.{$this->viewerCacheSegment()}.{$page}";
|
||||
$cacheKey = "content_type.catalog-visible.v2.{$contentTypeSlug}.{$sort}.{$this->viewerCacheSegment()}.{$page}";
|
||||
|
||||
return Cache::remember($cacheKey, $ttl, function () use ($contentTypeSlug, $sort, $perPage) {
|
||||
return Artwork::search('')
|
||||
->options($this->viewerAwareOptions([
|
||||
'filter' => self::BASE_FILTER . ' AND content_type = "' . addslashes($contentTypeSlug) . '"',
|
||||
'sort' => self::CATEGORY_SORT_FIELDS[$sort],
|
||||
]))
|
||||
->paginate($perPage);
|
||||
return $this->searchWithThumbnailPreference([
|
||||
'filter' => self::BASE_FILTER . ' AND content_type = "' . addslashes($contentTypeSlug) . '"',
|
||||
'sort' => self::CATEGORY_SORT_FIELDS[$sort],
|
||||
], $perPage, false, (int) request()->get('page', 1));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -431,6 +441,15 @@ final class ArtworkSearchService
|
||||
return $options;
|
||||
}
|
||||
|
||||
private function shouldFallbackToViewerVisibilityFiltering(LengthAwarePaginator $results): bool
|
||||
{
|
||||
if ($results->total() > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->maturity->viewerPreferences(request()->user())['visibility'] === ArtworkMaturityService::VIEW_HIDE;
|
||||
}
|
||||
|
||||
private function viewerCacheSegment(): string
|
||||
{
|
||||
return 'visibility-' . $this->maturity->viewerPreferences(request()->user())['visibility'];
|
||||
@@ -513,6 +532,37 @@ final class ArtworkSearchService
|
||||
->values();
|
||||
}
|
||||
|
||||
private function filterSearchCollectionByCatalogVisibility(Collection $items): Collection
|
||||
{
|
||||
if ($items->isEmpty()) {
|
||||
return $items;
|
||||
}
|
||||
|
||||
$ids = $items
|
||||
->pluck('id')
|
||||
->filter(fn ($id) => is_numeric($id) && (int) $id > 0)
|
||||
->map(fn ($id) => (int) $id)
|
||||
->values();
|
||||
|
||||
if ($ids->isEmpty()) {
|
||||
return $items->values();
|
||||
}
|
||||
|
||||
$visibilityQuery = Artwork::query()
|
||||
->catalogVisible()
|
||||
->whereIn('id', $ids);
|
||||
|
||||
$visibleIds = $this->maturity
|
||||
->applyViewerFilter($visibilityQuery, request()->user())
|
||||
->pluck('id')
|
||||
->map(fn ($id) => (int) $id)
|
||||
->flip();
|
||||
|
||||
return $items
|
||||
->filter(fn ($item) => $visibleIds->has((int) ($item->id ?? 0)))
|
||||
->values();
|
||||
}
|
||||
|
||||
private function determineSearchCandidatePoolSize(int $perPage, int $page): int
|
||||
{
|
||||
return min(
|
||||
@@ -521,6 +571,23 @@ final class ArtworkSearchService
|
||||
);
|
||||
}
|
||||
|
||||
private function makeModelPaginator(Collection $items, int $total, int $perPage, int $page): LengthAwarePaginator
|
||||
{
|
||||
$paginator = new PaginationLengthAwarePaginator(
|
||||
[],
|
||||
$total,
|
||||
$perPage,
|
||||
$page,
|
||||
[
|
||||
'path' => request()->url(),
|
||||
'query' => request()->query(),
|
||||
'pageName' => 'page',
|
||||
]
|
||||
);
|
||||
|
||||
return $paginator->setCollection(new EloquentCollection($items->all()));
|
||||
}
|
||||
|
||||
private function emptyPaginator(int $perPage): LengthAwarePaginator
|
||||
{
|
||||
return new \Illuminate\Pagination\LengthAwarePaginator([], 0, $perPage);
|
||||
|
||||
Reference in New Issue
Block a user