GG.deals clone: CodeIgniter 4 + MongoDB game price tracker
- Full gg.deals-style UI: deal feed with filters, game pages with price history charts, rankings, vouchers, news, prepaids, subscriptions, bundles, franchises, wishlist, API docs - Real data crawlers: * gg:crawl (Steam Store API + GOG Catalog + Epic free games) * gg:catalog (252K game slugs from gg.deals sitemaps) * gg:ggdeals (per-game prices via FlareSolverr session, JSON-LD parsing) * gg:seed / gg:crawl-covers (demo seed + Steam CDN cover art) - 24/7 self-updating scheduler (scripts/scheduler_loop.php, lockfile-protected) - Tailwind v4 dark/light theme, Alpine.js, Chart.js
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Models\GameModel;
|
||||
use App\Models\StoreModel;
|
||||
use App\Libraries\Mongo;
|
||||
|
||||
class Home extends BaseController
|
||||
{
|
||||
/**
|
||||
* Deal presets (mirrors gg.deals /deals/{preset}/ URLs).
|
||||
*/
|
||||
private const PRESETS = [
|
||||
'hot-new-deals' => ['title' => 'Hot New Deals', 'sort' => 'hot', 'preset' => 'hot_new'],
|
||||
'new-deals' => ['title' => 'New Deals', 'sort' => 'recent', 'preset' => 'new'],
|
||||
'historical-lows' => ['title' => 'Historical Lows', 'sort' => 'rating', 'preset' => 'hl'],
|
||||
'ending-soon' => ['title' => 'Ending Soon', 'sort' => 'ending', 'preset' => 'ending'],
|
||||
'at-least-75-off' => ['title' => 'At least 75% off', 'sort' => 'cut', 'preset' => 'cut75'],
|
||||
'at-least-85-off' => ['title' => 'At least 85% off', 'sort' => 'cut', 'preset' => 'cut85'],
|
||||
];
|
||||
|
||||
public function index(): string
|
||||
{
|
||||
$request = service('request');
|
||||
|
||||
// If any filter/search is active → regular deal feed. Otherwise → homepage dashboard.
|
||||
$hasFilters = $request->getGet('stores') !== null || $request->getGet('kind') !== null
|
||||
|| $request->getGet('min_price') !== null || $request->getGet('max_price') !== null
|
||||
|| $request->getGet('min_cut') !== null || $request->getGet('hl_only') !== null
|
||||
|| $request->getGet('sort') !== null || $request->getGet('price_tier') !== null
|
||||
|| $request->getGet('discount_tier') !== null || $request->getGet('deal_rating') !== null
|
||||
|| $request->getGet('release_range') !== null || $request->getGet('q') !== null;
|
||||
|
||||
if (! $hasFilters) {
|
||||
return $this->dashboard();
|
||||
}
|
||||
|
||||
return $this->renderFeed([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plain deal feed (used by /deals) regardless of filters.
|
||||
*/
|
||||
public function feedOnly(): string
|
||||
{
|
||||
return $this->renderFeed([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Homepage dashboard mirroring gg.deals: multiple preset sections.
|
||||
*/
|
||||
private function dashboard(): string
|
||||
{
|
||||
$model = new GameModel();
|
||||
|
||||
$data = [
|
||||
'pageTitle' => 'GG.deals — Best game deals, prices & discounts',
|
||||
'popular' => $model->dashboardDeals('best', 8),
|
||||
'newDeals' => $model->dashboardDeals('new', 8),
|
||||
'bestDeals' => $model->dashboardDeals('hot', 8),
|
||||
'histLows' => $model->dashboardDeals('hl', 8),
|
||||
'endingSoon' => $model->dashboardDeals('ending', 8),
|
||||
'mostWanted' => $model->dashboardGames('wanted', 8),
|
||||
'newReleases' => $model->dashboardGames('releases', 8),
|
||||
'upcoming' => $model->dashboardGames('upcoming', 8),
|
||||
'news' => $model->recentNews(6),
|
||||
'prepaids' => Mongo::collection('prepaids')->find([], ['sort' => ['name' => 1], 'limit' => 8])->toArray(),
|
||||
'stats' => $model->stats(),
|
||||
'stores' => (new StoreModel())->map(),
|
||||
'trending' => $model->trending(12),
|
||||
];
|
||||
|
||||
return view('homepage', $data);
|
||||
}
|
||||
|
||||
public function preset(string $name): string
|
||||
{
|
||||
if (! isset(self::PRESETS[$name])) {
|
||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
|
||||
return $this->renderFeed(self::PRESETS[$name]);
|
||||
}
|
||||
|
||||
private function renderFeed(array $preset): string
|
||||
{
|
||||
$request = service('request');
|
||||
|
||||
$filters = [
|
||||
'stores' => array_values(array_filter(array_map('strval', (array) $request->getGet('stores')))),
|
||||
'kind' => (string) ($request->getGet('kind') ?? ''),
|
||||
'min_price' => $request->getGet('min_price') !== null ? (string) $request->getGet('min_price') : '',
|
||||
'max_price' => $request->getGet('max_price') !== null ? (string) $request->getGet('max_price') : '',
|
||||
'min_cut' => (int) ($request->getGet('min_cut') ?? 0),
|
||||
'hl_only' => (bool) $request->getGet('hl_only'),
|
||||
'sort' => (string) ($request->getGet('sort') ?? ($preset['sort'] ?? 'trending')),
|
||||
'q' => trim((string) ($request->getGet('q') ?? '')),
|
||||
'price_tier'=> (string) ($request->getGet('price_tier') ?? ''),
|
||||
'discount_tier' => (string) ($request->getGet('discount_tier') ?? ''),
|
||||
'deal_rating' => (string) ($request->getGet('deal_rating') ?? ''),
|
||||
'release_range' => (string) ($request->getGet('release_range') ?? ''),
|
||||
'time_range' => (string) ($request->getGet('time_range') ?? ''),
|
||||
'view' => (string) ($request->getGet('view') ?? 'sidebar'),
|
||||
'wishlist' => array_values(array_filter(array_map('strval', (array) $request->getGet('wishlist')))),
|
||||
];
|
||||
|
||||
$filters['preset'] = $preset['preset'] ?? '';
|
||||
$filters['preset_name'] = $preset['title'] ?? '';
|
||||
|
||||
$page = max(1, (int) $request->getGet('page'));
|
||||
$perPage = 20;
|
||||
|
||||
$model = new GameModel();
|
||||
$feed = $model->feed($filters, $page, $perPage);
|
||||
|
||||
$data = [
|
||||
'pageTitle' => ($filters['preset_name'] !== '' ? $filters['preset_name'] . ' — ' : '') . 'Best PC game deals — GG.deals',
|
||||
'filters' => $filters,
|
||||
'feed' => $feed['rows'],
|
||||
'total' => $feed['total'],
|
||||
'pages' => $feed['pages'],
|
||||
'page' => $page,
|
||||
'perPage' => $perPage,
|
||||
'trending' => $model->trending(12),
|
||||
'stores' => (new StoreModel())->all(),
|
||||
'stats' => $model->stats(),
|
||||
'presets' => self::PRESETS,
|
||||
'activePreset' => $filters['preset_name'],
|
||||
];
|
||||
|
||||
return view('home', $data);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user