categories->pluck('id')->filter()->values(); $tagIds = $artwork->tags->pluck('id')->filter()->values(); if ($categoryIds->isEmpty() || $this->matchingPivotRows($categoryIds) > self::SPLIT_PIVOT_ROW_THRESHOLD) { return $this->originalQuery($artwork, $viewer, $categoryIds, $tagIds, $limit)->get(); } $candidateRows = collect() ->merge($this->topAuthorCandidates($artwork, $viewer, $limit)) ->merge($this->topGroupCandidates($artwork, $viewer, $limit)) ->merge($this->topCategoryCandidates($artwork, $viewer, $categoryIds, $limit)) ->merge($this->topTagCandidates($artwork, $viewer, $tagIds, $limit)) ->reject(fn (object $row): bool => (int) $row->id === (int) $artwork->id) ->unique(fn (object $row): string => (string) $row->id) ->sortByDesc(fn (object $row): mixed => $row->published_at) ->take($limit) ->values(); $ids = $candidateRows->pluck('id')->map(static fn ($id): int => (int) $id)->all(); if ($ids === []) { return new EloquentCollection; } $hydrated = Artwork::query() ->with(self::RELATIONS) ->whereIn('id', $ids) ->tap(fn (Builder $builder) => $this->maturity->applyViewerFilter($builder, $viewer)) ->get() ->keyBy('id'); return new EloquentCollection(collect($ids) ->map(fn (int $id): ?Artwork => $hydrated->get($id)) ->filter() ->values() ->all()); } private function matchingPivotRows(Collection $categoryIds): int { return (int) DB::table('artwork_category') ->whereIn('category_id', $categoryIds->all()) ->count(); } private function candidateQuery(Artwork $artwork, ?User $viewer): Builder { return Artwork::query() ->select(['artworks.id', 'artworks.published_at']) ->whereKeyNot($artwork->id) ->public() ->published() ->tap(fn (Builder $builder) => $this->maturity->applyViewerFilter($builder, $viewer)); } private function topAuthorCandidates(Artwork $artwork, ?User $viewer, int $limit): Collection { return $this->candidateQuery($artwork, $viewer) ->where('user_id', $artwork->user_id) ->latest('published_at') ->limit($limit) ->get(); } private function topGroupCandidates(Artwork $artwork, ?User $viewer, int $limit): Collection { if (! $artwork->group_id) { return collect(); } return $this->candidateQuery($artwork, $viewer) ->where('group_id', $artwork->group_id) ->latest('published_at') ->limit($limit) ->get(); } private function topCategoryCandidates(Artwork $artwork, ?User $viewer, Collection $categoryIds, int $limit): Collection { return $this->candidateQuery($artwork, $viewer) ->join('artwork_category', 'artwork_category.artwork_id', '=', 'artworks.id') ->whereIn('artwork_category.category_id', $categoryIds->all()) ->select(['artworks.id', 'artworks.published_at']) ->distinct() ->orderByDesc('artworks.published_at') ->limit($limit) ->get(); } private function topTagCandidates(Artwork $artwork, ?User $viewer, Collection $tagIds, int $limit): Collection { if ($tagIds->isEmpty()) { return collect(); } return $this->candidateQuery($artwork, $viewer) ->whereHas('tags', fn ($query) => $query->whereIn('tags.id', $tagIds->all())) ->latest('published_at') ->limit($limit) ->get(); } private function originalQuery(Artwork $artwork, ?User $viewer, Collection $categoryIds, Collection $tagIds, int $limit): Builder { return Artwork::query() ->with(self::RELATIONS) ->whereKeyNot($artwork->id) ->public() ->published() ->tap(fn (Builder $builder) => $this->maturity->applyViewerFilter($builder, $viewer)) ->where(function ($query) use ($artwork, $categoryIds, $tagIds): void { $query->where('user_id', $artwork->user_id); if ($artwork->group_id) { $query->orWhere('group_id', $artwork->group_id); } if ($categoryIds->isNotEmpty()) { $query->orWhereHas('categories', function ($categoryQuery) use ($categoryIds): void { $categoryQuery->whereIn('categories.id', $categoryIds->all()); }); } if ($tagIds->isNotEmpty()) { $query->orWhereHas('tags', function ($tagQuery) use ($tagIds): void { $tagQuery->whereIn('tags.id', $tagIds->all()); }); } }) ->latest('published_at') ->limit($limit); } }