1
0
Fork 0

Implement page for browsing leaderboards

This commit is contained in:
Krzysztof Sikorski 2022-04-25 23:54:56 +02:00
parent 9fc7564f28
commit f9d60a7d89
Signed by: krzysztof-sikorski
GPG Key ID: 4EB564BD08FE8476
6 changed files with 181 additions and 0 deletions

View File

@ -9,6 +9,7 @@ final class AppRoutes
{
public const HOME = 'app_home';
public const FAVICON_ICO = 'app_favicon_ico';
public const LEADERBOARDS = 'app_leaderboards';
public const LOGIN = 'app_login';
public const LOGOUT = 'app_logout';
public const SUBMIT_JSON = 'app_submit_json';

View File

@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Contract\Config\AppRoutes;
use App\Service\Repository\Nexus\GamePeriodRepository;
use App\Service\Repository\Nexus\LeaderboardRepository;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Twig\Environment;
use function array_key_exists;
use function intval;
use function sprintf;
final class LeaderboardController
{
public function __construct(
private Environment $twigEnvironment,
private GamePeriodRepository $gamePeriodRepository,
private LeaderboardRepository $leaderboardRepository,
) {
}
#[Route(path: '/leaderboards', name: AppRoutes::LEADERBOARDS, methods: [Request::METHOD_GET])]
public function index(
Request $request
): Response {
$gamePeriods = $this->gamePeriodRepository->findAll();
$defaultGamePeriodId = null;
$optionsGamePeriods = [];
foreach ($gamePeriods as $gamePeriod) {
$id = $gamePeriod->getId();
$optionsGamePeriods[$id] = $gamePeriod;
if ($gamePeriod->isCurrent()) {
$defaultGamePeriodId = $id;
}
}
$selectedGamePeriodStr = $request->get(key: 'gamePeriod');
if (null !== $selectedGamePeriodStr) {
$selectedGamePeriodId = intval(value: $selectedGamePeriodStr);
if (false === array_key_exists(key: $selectedGamePeriodId, array: $optionsGamePeriods)) {
throw new NotFoundHttpException(
message: sprintf('Invalid ID of game period: %d', $selectedGamePeriodId)
);
}
} else {
$selectedGamePeriodId = $defaultGamePeriodId;
}
$selectedGamePeriod = $optionsGamePeriods[$selectedGamePeriodId];
$leaderboards = $this->leaderboardRepository->findByGamePeriod(gamePeriod: $selectedGamePeriod);
$context = [
'optionsGamePeriods' => $optionsGamePeriods,
'selectedGamePeriodId' => $selectedGamePeriodId,
'leaderboards' => $leaderboards,
];
$responseBody = $this->twigEnvironment->render(name: 'leaderboards/index.html.twig', context: $context);
return new Response(content: $responseBody);
}
}

View File

@ -21,6 +21,11 @@ final class MainMenuService
'url' => $this->urlGenerator->generate(name: AppRoutes::HOME),
'external' => false,
],
[
'name' => 'Leaderboards',
'url' => $this->urlGenerator->generate(name: AppRoutes::LEADERBOARDS),
'external' => false,
],
[
'name' => 'Submit data',
'url' => $this->urlGenerator->generate(name: AppRoutes::SUBMIT_JSON),

View File

@ -26,4 +26,18 @@ final class GamePeriodRepository
return $query->getOneOrNullResult();
}
/**
* @return GamePeriod[]
*/
public function findAll(): array
{
$queryBuilder = $this->entityManager->createQueryBuilder()
->select(select: 'gp')
->from(from: GamePeriod::class, alias: 'gp')
->orderBy(sort: 'gp.id', order: 'ASC');
$query = $queryBuilder->getQuery();
return $query->getResult();
}
}

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace App\Service\Repository\Nexus;
use App\Doctrine\Entity\Nexus\GamePeriod;
use App\Doctrine\Entity\Nexus\Leaderboard;
use App\Doctrine\Entity\Nexus\LeaderboardCategory;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\Expr\Join;
final class LeaderboardRepository
{
public function __construct(
private EntityManagerInterface $entityManager,
) {
}
public function findByGamePeriod(GamePeriod $gamePeriod): array
{
$queryBuilder = $this->entityManager->createQueryBuilder()
->select(select: 'l')
->from(from: Leaderboard::class, alias: 'l')
->innerJoin(
join: LeaderboardCategory::class,
alias: 'cat',
conditionType: Join::WITH,
condition: 'l.category = cat',
)
->where(predicates: 'l.gamePeriod = :gamePeriod')
->orderBy(sort: 'cat.name', order: 'ASC')
->addOrderBy(sort: 'cat.type', order: 'ASC')
->setParameter(key: 'gamePeriod', value: $gamePeriod);
$query = $queryBuilder->getQuery();
return $query->getResult(hydrationMode: AbstractQuery::HYDRATE_OBJECT);
}
}

View File

@ -0,0 +1,50 @@
{% extends 'base.html.twig' %}
{% block title %}Leaderboards - Nexus Archive{% endblock %}
{% block body %}
<h1>Leaderboards</h1>
<section>
<form action="" method="get">
<label>
Game period:
<select name="gamePeriod">
{% for id, item in optionsGamePeriods %}
<option value="{{ id }}"
{% if id == selectedGamePeriodId %}selected{% endif %}>{{ item.name }}</option>
{% endfor %}
</select>
</label>
<button type="submit">Switch</button>
</form>
</section>
<section class="leaderboard-grid">
{% for leaderboard in leaderboards %}
<article>
<table>
<caption>{{ leaderboard.category.name }} ({{ leaderboard.category.type|capitalize }})</caption>
<thead>
<tr>
<th>Position</th>
<th>Character</th>
<th>{{ leaderboard.category.scoreLabel }}</th>
</tr>
</thead>
<tbody>
{% for entry in leaderboard.entries %}
<tr>
<td>{{ entry.position }}</td>
<td>{{ entry.characterName }}</td>
<td>{{ entry.score }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</article>
{% else %}
<article class="error">No leaderboards found for selected period.</article>
{% endfor %}
</section>
{% endblock %}