Discover Pages
This folder documents how each public discovery surface is assembled today.
It is intentionally page-oriented rather than architecture-oriented.
For the broader discovery engine, signal collection, and personalization background, also see docs/discovery-personalization-engine.md.
Pages Covered
Trending→GET /discover/trendingRising→GET /discover/risingFresh→GET /discover/freshTop Rated→GET /discover/top-ratedMost Downloaded→GET /discover/most-downloadedToday Downloads→GET /downloads/todayOn This Day→GET /discover/on-this-dayFor You→GET /discover/for-you(auth only)
Shared Request Pipeline
Most Discover pages follow this pattern:
- Route enters
App\Http\Controllers\Web\DiscoverController. - The controller calls
App\Services\ArtworkSearchService. ArtworkSearchServicequeries theartworksMeilisearch index through Laravel Scout.- The search result usually contains only search/index fields.
DiscoverController::hydrateDiscoverSearchResults()then reloads fullArtworkrows from MySQL with relations (user,profile,categories) and converts them into the view model used by Blade.- Some pages can still pass through additional presentation layers such as
GridFiller, depending on the controller action.
Shared Visibility Rules
All search-backed pages use the same base visibility filter in ArtworkSearchService:
is_public = true AND is_approved = true
That means an artwork must be:
- public
- approved
- present in the Meilisearch index
If the database row is correct but search is stale, the page can still miss the artwork until indexing catches up.
Shared Cache Behavior
ArtworkSearchService uses application cache in front of Meilisearch.
- Default TTL: 300 seconds
Rising: 120 seconds- Category/content-type sort pages use per-sort TTLs, but those are outside this folder's scope
The page can therefore lag behind a real publish or stat change even when the underlying data is already correct.
Shared Supporting Jobs
These jobs are active in the current Laravel 11 runtime scheduler (routes/console.php):
skinbase:flush-redis-statsevery 5 minutesskinbase:recalculate-trending --period=24hevery 30 minutesskinbase:recalculate-trending --period=7d --skip-indexevery 30 minutesskinbase:reset-windowed-stats --period=24hdaily at 03:30skinbase:reset-windowed-stats --period=7dweekly (Monday) at 03:30nova:recalculate-rankings --sync-rank-scoresevery 30 minutesartworks:publish-scheduledevery minuteanalytics:aggregate-discovery-feedbackdaily at 03:25RecBuildItemPairsFromFavouritesJobevery 4 hoursRecComputeSimilarByTagsJobdaily at 02:00RecComputeSimilarByBehaviorJobdaily at 02:15RecComputeSimilarHybridJobdaily at 02:30
Important Scheduler Caveat
The codebase still contains some discovery-related schedules inside app/Console/Kernel.php, but the active Laravel 11 runtime schedule comes from routes/console.php.
The Rising pipeline depends on these active runtime jobs:
nova:metrics-snapshot-hourlyhourlynova:recalculate-heatevery 15 minutes
If Rising stops moving while Trending changes, check php artisan schedule:list first and confirm both jobs are still active.
File Map
trending.mdrising.mdfresh.mdtop-rated.mdmost-downloaded.mdtoday-downloads.mdon-this-day.mdfor-you.md