game filter

This commit is contained in:
Lars Behrends
2025-11-10 03:11:23 +01:00
parent 2da58cfbd0
commit 0530f00cbf
3 changed files with 307 additions and 31 deletions

View File

@@ -189,6 +189,14 @@ class GameController extends Controller
}
$platforms = array_filter($platforms);
$features = $queryParams['features'] ?? [];
if (!is_array($features)) {
$features = [$features];
}
$features = array_filter($features);
$playtimeFilter = $queryParams['playtime'] ?? '';
// Get view mode
$viewMode = $queryParams['view'] ?? 'grid'; // grid, list, covers
@@ -196,14 +204,15 @@ class GameController extends Controller
$sort = $queryParams['sort'] ?? 'title_asc';
// Get games with pagination, filters, and sorting
$games = Game::getGroupedGamesWithPagination($this->pdo, $page, $perPage, $search, $genres, $platforms, $sort);
$games = Game::getGroupedGamesWithPagination($this->pdo, $page, $perPage, $search, $genres, $platforms, $features, $playtimeFilter, $sort);
// Get total count for pagination
$totalCount = Game::getTotalCount($this->pdo, $search, $genres, $platforms);
$totalCount = Game::getTotalCount($this->pdo, $search, $genres, $platforms, $features, $playtimeFilter);
// Get available filter options
$availableGenres = Game::getAvailableGenres($this->pdo);
$availablePlatforms = Game::getAvailablePlatforms($this->pdo);
$availableFeatures = Game::getAvailableFeatures($this->pdo);
// Calculate pagination info
$totalPages = ceil($totalCount / $perPage);
@@ -228,11 +237,14 @@ class GameController extends Controller
'view_modes' => ['grid', 'list', 'covers'],
'filters' => [
'genres' => $genres,
'platforms' => $platforms
'platforms' => $platforms,
'features' => $features,
'playtime' => $playtimeFilter
],
'available_filters' => [
'genres' => $availableGenres,
'platforms' => $availablePlatforms
'platforms' => $availablePlatforms,
'features' => $availableFeatures
],
'sort' => $sort,
'sort_options' => [

View File

@@ -264,7 +264,7 @@ class Game extends Model
/**
* Get total count of games for pagination
*/
public static function getTotalCount(\PDO $pdo, string $search = '', array $genres = [], array $platforms = []): int
public static function getTotalCount(\PDO $pdo, string $search = '', array $genres = [], array $platforms = [], array $features = [], string $playtimeFilter = ''): int
{
$sql = "SELECT COUNT(*) as count FROM games WHERE game_key IS NOT NULL";
$params = [];
@@ -275,12 +275,12 @@ class Game extends Model
}
if (!empty($genres)) {
$placeholders = [];
$genreConditions = [];
foreach ($genres as $index => $genre) {
$placeholders[] = ":genre_{$index}";
$genreConditions[] = "JSON_SEARCH(metadata, 'one', :genre_{$index}) IS NOT NULL";
$params["genre_{$index}"] = $genre;
}
$sql .= " AND genre IN (" . implode(',', $placeholders) . ")";
$sql .= " AND (" . implode(' OR ', $genreConditions) . ")";
}
if (!empty($platforms)) {
@@ -292,6 +292,38 @@ class Game extends Model
$sql .= " AND platform IN (" . implode(',', $placeholders) . ")";
}
if (!empty($features)) {
$featureConditions = [];
foreach ($features as $index => $feature) {
$featureConditions[] = "JSON_SEARCH(metadata, 'one', :feature_{$index}) IS NOT NULL";
$params["feature_{$index}"] = $feature;
}
$sql .= " AND (" . implode(' OR ', $featureConditions) . ")";
}
if (!empty($playtimeFilter)) {
switch ($playtimeFilter) {
case 'none':
$sql .= " AND (playtime_minutes IS NULL OR playtime_minutes = 0)";
break;
case 'under_1h':
$sql .= " AND playtime_minutes > 0 AND playtime_minutes < 60";
break;
case '1h_5h':
$sql .= " AND playtime_minutes >= 60 AND playtime_minutes < 300";
break;
case '5h_10h':
$sql .= " AND playtime_minutes >= 300 AND playtime_minutes < 600";
break;
case '10h_20h':
$sql .= " AND playtime_minutes >= 600 AND playtime_minutes < 1200";
break;
case 'over_20h':
$sql .= " AND playtime_minutes >= 1200";
break;
}
}
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
return (int) $stmt->fetch()['count'];
@@ -300,7 +332,7 @@ class Game extends Model
/**
* Get grouped games with pagination and search support
*/
public static function getGroupedGamesWithPagination(\PDO $pdo, int $page, int $perPage, string $search = '', array $genres = [], array $platforms = [], string $sort = 'title_asc'): array
public static function getGroupedGamesWithPagination(\PDO $pdo, int $page, int $perPage, string $search = '', array $genres = [], array $platforms = [], array $features = [], string $playtimeFilter = '', string $sort = 'title_asc'): array
{
$offset = ($page - 1) * $perPage;
@@ -330,12 +362,12 @@ class Game extends Model
}
if (!empty($genres)) {
$placeholders = [];
$genreConditions = [];
foreach ($genres as $index => $genre) {
$placeholders[] = ":genre_{$index}";
$genreConditions[] = "JSON_SEARCH(metadata, 'one', :genre_{$index}) IS NOT NULL";
$params["genre_{$index}"] = $genre;
}
$sql .= " AND genre IN (" . implode(',', $placeholders) . ")";
$sql .= " AND (" . implode(' OR ', $genreConditions) . ")";
}
if (!empty($platforms)) {
@@ -347,6 +379,38 @@ class Game extends Model
$sql .= " AND platform IN (" . implode(',', $placeholders) . ")";
}
if (!empty($features)) {
$featureConditions = [];
foreach ($features as $index => $feature) {
$featureConditions[] = "JSON_SEARCH(metadata, 'one', :feature_{$index}) IS NOT NULL";
$params["feature_{$index}"] = $feature;
}
$sql .= " AND (" . implode(' OR ', $featureConditions) . ")";
}
if (!empty($playtimeFilter)) {
switch ($playtimeFilter) {
case 'none':
$sql .= " AND (playtime_minutes IS NULL OR playtime_minutes = 0)";
break;
case 'under_1h':
$sql .= " AND playtime_minutes > 0 AND playtime_minutes < 60";
break;
case '1h_5h':
$sql .= " AND playtime_minutes >= 60 AND playtime_minutes < 300";
break;
case '5h_10h':
$sql .= " AND playtime_minutes >= 300 AND playtime_minutes < 600";
break;
case '10h_20h':
$sql .= " AND playtime_minutes >= 600 AND playtime_minutes < 1200";
break;
case 'over_20h':
$sql .= " AND playtime_minutes >= 1200";
break;
}
}
// Add sorting
$sortOptions = [
'title_asc' => 'title ASC',
@@ -485,12 +549,28 @@ class Game extends Model
public static function getAvailableGenres(\PDO $pdo): array
{
$stmt = $pdo->query("
SELECT DISTINCT genre
SELECT metadata
FROM games
WHERE genre IS NOT NULL AND genre != ''
ORDER BY genre
WHERE metadata IS NOT NULL AND metadata != '' AND metadata != '{}'
");
return $stmt->fetchAll(\PDO::FETCH_COLUMN);
$genres = [];
$results = $stmt->fetchAll(\PDO::FETCH_COLUMN);
foreach ($results as $json) {
$decoded = json_decode($json, true);
if (is_array($decoded) && isset($decoded['genres']) && is_array($decoded['genres'])) {
foreach ($decoded['genres'] as $genre) {
if (is_string($genre)) {
$genres[] = $genre;
}
}
}
}
$genres = array_unique($genres);
sort($genres);
return array_values(array_filter($genres));
}
/**
@@ -507,6 +587,36 @@ class Game extends Model
return $stmt->fetchAll(\PDO::FETCH_COLUMN);
}
/**
* Get available features for filtering
*/
public static function getAvailableFeatures(\PDO $pdo): array
{
$stmt = $pdo->query("
SELECT metadata
FROM games
WHERE metadata IS NOT NULL AND metadata != '' AND metadata != '{}'
");
$features = [];
$results = $stmt->fetchAll(\PDO::FETCH_COLUMN);
foreach ($results as $json) {
$decoded = json_decode($json, true);
if (is_array($decoded) && isset($decoded['features']) && is_array($decoded['features'])) {
foreach ($decoded['features'] as $feature) {
if (is_string($feature)) {
$features[] = $feature;
}
}
}
}
$features = array_unique($features);
sort($features);
return array_values(array_filter($features));
}
/**
* Check if game has rich Playnite data
*/