mirror of
https://github.com/ericasch6/scrum-poker-internal.git
synced 2026-10-13 15:52:09 +00:00
fix: refactored the codebase
This commit is contained in:
@@ -2,86 +2,44 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\RoomStateChange;
|
||||
use App\Events\RoomStateChanged;
|
||||
use App\Http\Requests\StoreRoomRequest;
|
||||
use App\Models\Deck;
|
||||
use App\Http\Resources\CurrentPlayerResource;
|
||||
use App\Http\Resources\DeckResource;
|
||||
use App\Http\Resources\RoomResource;
|
||||
use App\Models\Player;
|
||||
use App\Models\Room;
|
||||
use App\Services\Rooms\RoomCreationService;
|
||||
use App\Services\Rooms\RoomPageService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class RoomController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly RoomCreationService $roomCreation,
|
||||
private readonly RoomPageService $roomPage,
|
||||
) {}
|
||||
|
||||
public function create(Request $request): Response
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
$decks = Deck::query()
|
||||
->where(function ($query) use ($user) {
|
||||
$query->where('is_system', true);
|
||||
|
||||
if ($user !== null) {
|
||||
$query->orWhere('user_id', $user->id);
|
||||
}
|
||||
})
|
||||
->orderBy('name')
|
||||
->get([
|
||||
'id',
|
||||
'name',
|
||||
'cards',
|
||||
]);
|
||||
|
||||
return Inertia::render('rooms/Create', [
|
||||
'decks' => $decks,
|
||||
'defaultPlayerName' => $user?->name,
|
||||
'decks' => DeckResource::collection(
|
||||
$this->roomCreation->availableDecks($request->user()),
|
||||
)->resolve($request),
|
||||
'defaultPlayerName' => $request->user()?->name,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request, StoreRoomRequest $roomStoreRequest): RedirectResponse
|
||||
public function store(StoreRoomRequest $request): RedirectResponse
|
||||
{
|
||||
$validated = $roomStoreRequest->validated();
|
||||
$user = $request->user();
|
||||
|
||||
$deck = Deck::query()
|
||||
->whereKey($validated['deck_id'])
|
||||
->where(function ($query) use ($user) {
|
||||
$query->where('is_system', true);
|
||||
|
||||
if ($user !== null) {
|
||||
$query->orWhere('user_id', $user->id);
|
||||
}
|
||||
})
|
||||
->firstOrFail();
|
||||
|
||||
[$room, $player] = DB::transaction(function () use (
|
||||
/** @var array{deck_id: int, player_name: string, title?: string|null} $validated */
|
||||
$validated = $request->validated();
|
||||
[$room, $player] = $this->roomCreation->create(
|
||||
$validated,
|
||||
$deck,
|
||||
$user,
|
||||
) {
|
||||
$room = Room::create([
|
||||
'owner_id' => $user?->id,
|
||||
'title' => $validated['title'] ?? null,
|
||||
'last_activity_at' => now(),
|
||||
'deck_config' => [
|
||||
'id' => $deck->id,
|
||||
'name' => $deck->name,
|
||||
'cards' => $deck->cards,
|
||||
],
|
||||
]);
|
||||
|
||||
$player = $room->players()->create([
|
||||
'user_id' => $user?->id,
|
||||
'name' => $validated['player_name'],
|
||||
'is_moderator' => true,
|
||||
'last_seen_at' => now(),
|
||||
]);
|
||||
|
||||
return [$room, $player];
|
||||
});
|
||||
$request->user(),
|
||||
);
|
||||
|
||||
$request->session()->put(
|
||||
"room_players.{$room->id}",
|
||||
@@ -90,7 +48,7 @@ class RoomController extends Controller
|
||||
|
||||
$redirect = to_route('room.show', $room);
|
||||
|
||||
if ($user === null) {
|
||||
if ($request->user() === null) {
|
||||
$redirect->with('room_notice', [
|
||||
'guest_retention_days' => config('rooms.guest_retention_days'),
|
||||
'account_retention_days' => config('rooms.account_retention_days'),
|
||||
@@ -106,197 +64,15 @@ class RoomController extends Controller
|
||||
|
||||
abort_unless($currentPlayer instanceof Player, 403);
|
||||
|
||||
$room->load([
|
||||
'players' => fn ($query) => $query
|
||||
->select(['id', 'room_id', 'name', 'is_moderator', 'created_at'])
|
||||
->orderByDesc('is_moderator')
|
||||
->orderBy('created_at'),
|
||||
'players.votes' => fn ($query) => $query
|
||||
->where('room_id', $room->id)
|
||||
->where('round', $room->current_round),
|
||||
]);
|
||||
|
||||
$players = $room->players->map(function (Player $player) use ($room) {
|
||||
$vote = $player->votes->first();
|
||||
|
||||
return [
|
||||
'id' => $player->id,
|
||||
'name' => $player->name,
|
||||
'is_moderator' => $player->is_moderator,
|
||||
'has_voted' => $vote !== null,
|
||||
'vote' => $room->revealed_at !== null ? $vote?->value : null,
|
||||
];
|
||||
});
|
||||
|
||||
$currentVote = $room->players
|
||||
->firstWhere('id', $currentPlayer->id)
|
||||
?->votes
|
||||
->first();
|
||||
$statistics = $room->revealed_at !== null
|
||||
? $this->currentRoundStatistics($room)
|
||||
: null;
|
||||
|
||||
return Inertia::render('rooms/Show', [
|
||||
'room' => [
|
||||
'id' => $room->id,
|
||||
'title' => $room->title,
|
||||
'join_code' => $room->join_code,
|
||||
'current_round' => $room->current_round,
|
||||
'revealed' => $room->revealed_at !== null,
|
||||
'state_version' => $room->state_version,
|
||||
'statistics' => $statistics,
|
||||
'deck' => [
|
||||
'name' => $room->deck_config['name'],
|
||||
'cards' => $room->deck_config['cards'],
|
||||
],
|
||||
'players' => $players,
|
||||
],
|
||||
'currentPlayer' => [
|
||||
'id' => $currentPlayer->id,
|
||||
'name' => $currentPlayer->name,
|
||||
'is_moderator' => $currentPlayer->is_moderator,
|
||||
'current_vote' => $currentVote?->value,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function join(Request $request): Response
|
||||
{
|
||||
$joinCode = strtoupper(trim((string) $request->query('join_code')));
|
||||
$room = $joinCode !== ''
|
||||
? Room::query()->where('join_code', $joinCode)->first()
|
||||
: null;
|
||||
$existingPlayer = $room !== null && $request->user() !== null
|
||||
? $room->players()->where('user_id', $request->user()->id)->first()
|
||||
: null;
|
||||
|
||||
return Inertia::render('rooms/Join', [
|
||||
'join_code' => $joinCode,
|
||||
'defaultPlayerName' => $existingPlayer instanceof Player
|
||||
? $existingPlayer->name
|
||||
: $request->user()?->name,
|
||||
'closedRoom' => $room?->isClosed() ? [
|
||||
'title' => $room->title,
|
||||
'join_code' => $room->join_code,
|
||||
] : null,
|
||||
]);
|
||||
}
|
||||
|
||||
public function reopen(Room $room, Request $request): RedirectResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
abort_if($room->trashed(), 404);
|
||||
abort_unless($user !== null && $user->id === $room->owner_id, 403);
|
||||
|
||||
[$player, $stateVersion, $wasReopened] = DB::transaction(function () use ($room, $user): array {
|
||||
$lockedRoom = Room::query()->lockForUpdate()->findOrFail($room->id);
|
||||
|
||||
abort_unless($user->id === $lockedRoom->owner_id, 403);
|
||||
|
||||
$wasReopened = $lockedRoom->isClosed();
|
||||
|
||||
if ($lockedRoom->isClosed()) {
|
||||
$lockedRoom->update([
|
||||
'current_round' => $lockedRoom->current_round + 1,
|
||||
'revealed_at' => null,
|
||||
'closed_at' => null,
|
||||
'last_activity_at' => now(),
|
||||
'state_version' => $lockedRoom->state_version + 1,
|
||||
]);
|
||||
}
|
||||
|
||||
return [
|
||||
$lockedRoom->players()
|
||||
->where('user_id', $user->id)
|
||||
->firstOrFail(),
|
||||
$lockedRoom->state_version,
|
||||
$wasReopened,
|
||||
];
|
||||
});
|
||||
|
||||
$request->session()->put("room_players.{$room->id}", $player->id);
|
||||
|
||||
if ($wasReopened) {
|
||||
RoomStateChanged::dispatch(
|
||||
$room->id,
|
||||
$stateVersion,
|
||||
RoomStateChange::RoomReopened,
|
||||
);
|
||||
}
|
||||
|
||||
return to_route('room.show', $room);
|
||||
}
|
||||
|
||||
public function destroy(Room $room, Request $request): RedirectResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
abort_if($room->trashed(), 404);
|
||||
abort_unless($user !== null && $user->id === $room->owner_id, 403);
|
||||
|
||||
$stateVersion = DB::transaction(function () use ($room, $user): int {
|
||||
$lockedRoom = Room::withTrashed()
|
||||
->lockForUpdate()
|
||||
->findOrFail($room->id);
|
||||
|
||||
abort_if($lockedRoom->trashed(), 404);
|
||||
abort_unless($user->id === $lockedRoom->owner_id, 403);
|
||||
|
||||
$lockedRoom->update([
|
||||
'state_version' => $lockedRoom->state_version + 1,
|
||||
]);
|
||||
$lockedRoom->delete();
|
||||
|
||||
return $lockedRoom->state_version;
|
||||
});
|
||||
|
||||
$request->session()->forget("room_players.{$room->id}");
|
||||
|
||||
RoomStateChanged::dispatch(
|
||||
$room->id,
|
||||
$stateVersion,
|
||||
RoomStateChange::RoomDeleted,
|
||||
$pageData = $this->roomPage->prepare(
|
||||
$room,
|
||||
$currentPlayer,
|
||||
$request->user(),
|
||||
);
|
||||
|
||||
Inertia::flash('toast', [
|
||||
'type' => 'success',
|
||||
'message' => 'Der Raum wurde gelöscht.',
|
||||
return Inertia::render('rooms/Show', [
|
||||
'room' => (new RoomResource($pageData))->resolve($request),
|
||||
'currentPlayer' => (new CurrentPlayerResource($pageData))->resolve($request),
|
||||
]);
|
||||
|
||||
return to_route('dashboard');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{average: string|null, median: string|null}
|
||||
*/
|
||||
private function currentRoundStatistics(Room $room): array
|
||||
{
|
||||
$values = [];
|
||||
|
||||
foreach ($room->players as $player) {
|
||||
$vote = $player->votes->first();
|
||||
|
||||
if ($vote !== null && is_numeric($vote->value)) {
|
||||
$values[] = (float) $vote->value;
|
||||
}
|
||||
}
|
||||
|
||||
sort($values, SORT_NUMERIC);
|
||||
$count = count($values);
|
||||
|
||||
if ($count === 0) {
|
||||
return ['average' => null, 'median' => null];
|
||||
}
|
||||
|
||||
$middle = intdiv($count, 2);
|
||||
$median = $count % 2 === 1
|
||||
? $values[$middle]
|
||||
: ($values[$middle - 1] + $values[$middle]) / 2;
|
||||
|
||||
return [
|
||||
'average' => number_format(array_sum($values) / $count, 2, ',', ''),
|
||||
'median' => number_format($median, 2, ',', ''),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user