Datenbankstruktur für Spiele #16

Merged
flixcoo merged 22 commits from feature/13-datenbankstruktur-fuer-spiele into development 2025-11-15 15:56:40 +00:00
12 changed files with 1924 additions and 90 deletions
Showing only changes of commit a922f24150 - Show all commits

View File

@@ -2,6 +2,8 @@ import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/database.dart'; import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/db/tables/game_table.dart'; import 'package:game_tracker/data/db/tables/game_table.dart';
import 'package:game_tracker/data/dto/game.dart'; import 'package:game_tracker/data/dto/game.dart';
import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/data/dto/player.dart';
part 'game_dao.g.dart'; part 'game_dao.g.dart';
@@ -16,11 +18,27 @@ class GameDao extends DatabaseAccessor<AppDatabase> with _$GameDaoMixin {
return result.map((row) => Game(id: row.id, name: row.name)).toList(); return result.map((row) => Game(id: row.id, name: row.name)).toList();
} }
/// Retrieves a [Game] by its [id]. /// Retrieves a [Game] by its [gameId].
Future<Game> getGameById(String id) async { Future<Game> getGameById(String gameId) async {
final query = select(gameTable)..where((g) => g.id.equals(id)); final query = select(gameTable)..where((g) => g.id.equals(gameId));
final result = await query.getSingle(); final result = await query.getSingle();
return Game(id: result.id, name: result.name);
List<Player>? players;
sneeex marked this conversation as resolved
Review

Wieso gehst du überall davon aus, dass players null sein kann? Man kann doch kein Game ohne Player erstellen oder in welchem case ist das der Fall?

Wieso gehst du überall davon aus, dass players null sein kann? Man kann doch kein Game ohne Player erstellen oder in welchem case ist das der Fall?
Review

Weil ein Game ja auch nur eine Group bekommern kann

Weil ein `Game` ja auch nur eine `Group` bekommern kann
if (await db.playerGameDao.hasGamePlayers(gameId)) {
players = await db.playerGameDao.getPlayersByGameId(gameId);
}
Group? group;
if (await db.groupGameDao.hasGameGroup(gameId)) {
group = await db.groupGameDao.getGroupByGameId(gameId);
}
return Game(
id: result.id,
name: result.name,
players: players,
group: group,
winner: result.winnerId,
);
} }
/// Retrieves the number of games in the database. /// Retrieves the number of games in the database.

View File

@@ -19,14 +19,14 @@ class GroupDao extends DatabaseAccessor<AppDatabase> with _$GroupDaoMixin {
.toList(); .toList();
} }
/// Retrieves a [Group] by its [id], including its members. /// Retrieves a [Group] by its [groupId], including its members.
Future<Group> getGroupById(String id) async { Future<Group> getGroupById(String groupId) async {
final query = select(groupTable)..where((g) => g.id.equals(id)); final query = select(groupTable)..where((g) => g.id.equals(groupId));
final result = await query.getSingle(); final result = await query.getSingle();
List<Player> members = []; List<Player> members = await db.playerGroupDao.getPlayersOfGroupById(
groupId,
members = await db.playerGroupDao.getPlayersOfGroupById(id); );
return Group(id: result.id, name: result.name, members: members); return Group(id: result.id, name: result.name, members: members);
} }

View File

@@ -0,0 +1,33 @@
import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/db/tables/group_game_table.dart';
import 'package:game_tracker/data/dto/group.dart';
part 'group_game_dao.g.dart';
@DriftAccessor(tables: [GroupGameTable])
class GroupGameDao extends DatabaseAccessor<AppDatabase>
with _$GroupGameDaoMixin {
GroupGameDao(super.db);
/// Checks if there is a group associated with the given [gameId].
/// Returns `true` if there is a group, otherwise `false`.
Future<bool> hasGameGroup(String gameId) async {
final count =
await (selectOnly(groupGameTable)
..where(groupGameTable.gameId.equals(gameId))
..addColumns([groupGameTable.groupId.count()]))
.map((row) => row.read(groupGameTable.groupId.count()))
.getSingle();
return (count ?? 0) > 0;
}
flixcoo marked this conversation as resolved
Review

doku vergessen

doku vergessen
Future<Group> getGroupByGameId(String gameId) async {
final result = await (select(
groupGameTable,
)..where((g) => g.gameId.equals(gameId))).getSingle();
final group = await db.groupDao.getGroupById(result.groupId);
return group;
}
}

View File

@@ -0,0 +1,10 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'group_game_dao.dart';
// ignore_for_file: type=lint
mixin _$GroupGameDaoMixin on DatabaseAccessor<AppDatabase> {
$PlayerTableTable get playerTable => attachedDatabase.playerTable;
$GroupTableTable get groupTable => attachedDatabase.groupTable;
$GroupGameTableTable get groupGameTable => attachedDatabase.groupGameTable;
}

View File

@@ -0,0 +1,40 @@
import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/db/tables/player_game_table.dart';
import 'package:game_tracker/data/dto/player.dart';
part 'player_game_dao.g.dart';
@DriftAccessor(tables: [PlayerGameTable])
class PlayerGameDao extends DatabaseAccessor<AppDatabase>
with _$PlayerGameDaoMixin {
PlayerGameDao(super.db);
/// Checks if there are any players associated with the given [gameId].
/// Returns `true` if there are players, otherwise `false`.
Future<bool> hasGamePlayers(String gameId) async {
final count =
flixcoo marked this conversation as resolved
Review

Finde den namen hasGamePlayers schlecht, besser gameHasPlayers

Finde den namen hasGamePlayers schlecht, besser `gameHasPlayers`
await (selectOnly(playerGameTable)
..where(playerGameTable.gameId.equals(gameId))
..addColumns([playerGameTable.playerId.count()]))
.map((row) => row.read(playerGameTable.playerId.count()))
.getSingle();
return (count ?? 0) > 0;
}
/// Retrieves a list of [Player]s associated with the given [gameId].
/// Returns an empty list if no players are found.
Future<List<Player>> getPlayersByGameId(String gameId) async {
final result = await (select(
playerGameTable,
)..where((p) => p.gameId.equals(gameId))).get();
if (result.isEmpty) return <Player>[];
final futures = result.map(
(row) => db.playerDao.getPlayerById(row.playerId),
);
final players = await Future.wait(futures);
return players.whereType<Player>().toList();
}
}

View File

@@ -0,0 +1,10 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'player_game_dao.dart';
// ignore_for_file: type=lint
mixin _$PlayerGameDaoMixin on DatabaseAccessor<AppDatabase> {
$PlayerTableTable get playerTable => attachedDatabase.playerTable;
$GroupTableTable get groupTable => attachedDatabase.groupTable;
$PlayerGameTableTable get playerGameTable => attachedDatabase.playerGameTable;
}

View File

@@ -2,10 +2,14 @@ import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart'; import 'package:drift_flutter/drift_flutter.dart';
import 'package:game_tracker/data/dao/game_dao.dart'; import 'package:game_tracker/data/dao/game_dao.dart';
import 'package:game_tracker/data/dao/group_dao.dart'; import 'package:game_tracker/data/dao/group_dao.dart';
import 'package:game_tracker/data/dao/group_game_dao.dart';
import 'package:game_tracker/data/dao/player_dao.dart'; import 'package:game_tracker/data/dao/player_dao.dart';
import 'package:game_tracker/data/dao/player_game_dao.dart';
import 'package:game_tracker/data/dao/player_group_dao.dart'; import 'package:game_tracker/data/dao/player_group_dao.dart';
import 'package:game_tracker/data/db/tables/game_table.dart'; import 'package:game_tracker/data/db/tables/game_table.dart';
import 'package:game_tracker/data/db/tables/group_game_table.dart';
import 'package:game_tracker/data/db/tables/group_table.dart'; import 'package:game_tracker/data/db/tables/group_table.dart';
import 'package:game_tracker/data/db/tables/player_game_table.dart';
import 'package:game_tracker/data/db/tables/player_group_table.dart'; import 'package:game_tracker/data/db/tables/player_group_table.dart';
import 'package:game_tracker/data/db/tables/player_table.dart'; import 'package:game_tracker/data/db/tables/player_table.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
@@ -13,8 +17,22 @@ import 'package:path_provider/path_provider.dart';
part 'database.g.dart'; part 'database.g.dart';
@DriftDatabase( @DriftDatabase(
tables: [PlayerTable, GroupTable, PlayerGroupTable, GameTable], tables: [
daos: [GroupDao, PlayerDao, PlayerGroupDao, GameDao], PlayerTable,
GroupTable,
PlayerGroupTable,
PlayerGameTable,
GroupGameTable,
GameTable,
],
daos: [
GroupDao,
PlayerDao,
GameDao,
PlayerGroupDao,
PlayerGameDao,
GroupGameDao,
],
) )
class AppDatabase extends _$AppDatabase { class AppDatabase extends _$AppDatabase {
AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection()); AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@ import 'package:drift/drift.dart';
class GameTable extends Table { class GameTable extends Table {
TextColumn get id => text()(); TextColumn get id => text()();
TextColumn get name => text()(); TextColumn get name => text()();
TextColumn get winnerId => text().nullable()();
@override @override
Set<Column<Object>> get primaryKey => {id}; Set<Column<Object>> get primaryKey => {id};

View File

@@ -0,0 +1,11 @@
import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/tables/group_table.dart';
import 'package:game_tracker/data/db/tables/player_table.dart';
class GroupGameTable extends Table {
TextColumn get groupId => text().references(PlayerTable, #id)();
TextColumn get gameId => text().references(GroupTable, #id)();
flixcoo marked this conversation as resolved
Review

Müsste PlayerTable nicht GameTable sein? ist ja die Verbindungstabelle von group und game

Müsste PlayerTable nicht GameTable sein? ist ja die Verbindungstabelle von group und game
@override
Set<Column<Object>> get primaryKey => {groupId, gameId};
}

View File

@@ -0,0 +1,11 @@
import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/tables/group_table.dart';
import 'package:game_tracker/data/db/tables/player_table.dart';
class PlayerGameTable extends Table {
TextColumn get playerId => text().references(PlayerTable, #id)();
TextColumn get gameId => text().references(GroupTable, #id)();
@override
Set<Column<Object>> get primaryKey => {playerId, gameId};
}

View File

@@ -1,6 +1,18 @@
import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/data/dto/player.dart';
class Game { class Game {
final String id; final String id;
final String name; final String name;
final List<Player>? players;
final Group? group;
final String? winner;
Game({required this.id, required this.name}); Game({
this.players,
this.group,
this.winner,
required this.id,
required this.name,
});
} }