Lines 100.00% 23 / 23
Functions and Methods 100.00% 3 / 3
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
Read 100.00% 23 / 23 100.00% 3 / 3 13 100.00% 1 / 1
 __invoke 100.00% 17 / 17 100.00% 1 / 1 8
 agent 100.00% 4 / 4 100.00% 1 / 1 3
 timelineCounts 100.00% 2 / 2 100.00% 1 / 1 2
1<?php
2
3declare(strict_types=1);
4
5namespace App\Controller\Agent;
6
7use App\ApiResource\Bannergress;
8use App\Controller\BaseController;
9use App\Entity\AgentStat;
10use App\Entity\User;
11use App\Enum\ProfilePart;
12use App\Enum\UserRole;
13use App\Medal\MedalChecker;
14use App\Repository\AgentStatRepository;
15use App\Repository\DoneBannerRepository;
16use App\Repository\TimelineEventRepository;
17use App\Repository\UserRepository;
18use App\Timeline\TimelineBuilder;
19use App\Timeline\TimelineEntry;
20use Symfony\Component\HttpFoundation\Response;
21use Symfony\Component\Routing\Attribute\Route;
22use Symfony\Component\Security\Http\Attribute\IsGranted;
23
24/**
25 * An agent's page with the profile parts the agent shows; 404 when the agent part is private.
26 * The stats history is a summary of the timeline, which is a page of its own (Agent\Timeline),
27 * as on the own stats page. The agent sees it as the others do.
28 */
29#[Route('/agents/{agentName}', name: 'app_agent_show', requirements: ['agentName' => '[A-Za-z0-9_]{1,50}'], methods: ['GET'])]
30#[IsGranted(UserRole::USER->value)]
31final class Read extends BaseController
32{
33    public function __invoke(
34        string $agentName,
35        UserRepository $userRepository,
36        AgentStatRepository $agentStatRepository,
37        TimelineEventRepository $timelineEventRepository,
38        MedalChecker $medalChecker,
39        TimelineBuilder $timelineBuilder,
40        DoneBannerRepository $doneBannerRepository,
41    ): Response {
42        $agent = $this->agent($agentName, $userRepository);
43        $stats = $agentStatRepository->findByOwner($agent);
44        $latest = $stats[0] ?? null;
45        $showLatestStats = $latest instanceof AgentStat && $agent->isVisible(ProfilePart::LATEST_STATS);
46        $showHistory = $agent->isVisible(ProfilePart::STATS_HISTORY);
47
48        return $this->render('agent/show.html.twig', [
49            'agent' => $agent,
50            'latest' => $latest,
51            // The first import tells about the history: only with it
52            'first' => $showHistory ? \array_last($stats) : null,
53            'timelineCounts' => $showHistory ? $this->timelineCounts($agent, $stats, $timelineEventRepository, $doneBannerRepository, $timelineBuilder) : null,
54            'showLatestStats' => $showLatestStats,
55            'medals' => $showLatestStats ? $medalChecker->medalLevels($latest) : [],
56            'coreBadges' => $showLatestStats ? $medalChecker->coreBadges($latest->getStats()['months_subscribed'] ?? 0) : [],
57            'customMedals' => $agent->isVisible(ProfilePart::CUSTOM_MEDALS) ? $medalChecker->customMedalTiers($agent->getCustomMedals()) : null,
58            'bannergress' => $agent->isVisible(ProfilePart::BANNERGRESS) ? Bannergress::fromStored($agent->getBannergress()) : null,
59            'isOwn' => $agent->id === $this->getUser()->id,
60        ]);
61    }
62
63    /** The agent, 404 when there is none with the name or the agent part is private. */
64    private function agent(string $agentName, UserRepository $userRepository): User
65    {
66        $agent = $userRepository->findOneByAgentName($agentName);
67        if (!$agent instanceof User || !$agent->isVisible(ProfilePart::AGENT)) {
68            throw $this->createNotFoundException('No agent '.$agentName.' to show.');
69        }
70
71        return $agent;
72    }
73
74    /**
75     * The number of timeline entries of each kind, for the summary.
76     *
77     * @param list<AgentStat> $stats newest first
78     *
79     * @return array<string, int> by kind
80     */
81    private function timelineCounts(
82        User $agent,
83        array $stats,
84        TimelineEventRepository $timelineEventRepository,
85        DoneBannerRepository $doneBannerRepository,
86        TimelineBuilder $timelineBuilder,
87    ): array {
88        // Banners are on the timeline only with the Bannergress part
89        $banners = $agent->isVisible(ProfilePart::BANNERGRESS) ? $doneBannerRepository->findDatedByOwner($agent) : [];
90
91        return TimelineEntry::counts($timelineBuilder->build($stats, $timelineEventRepository->findByOwner($agent), $banners));
92    }
93}