4 - Extend SubjectPresenter with aggregate fields + add show endpoint

This commit is contained in:
myrmidex 2026-06-05 23:06:52 +00:00
parent dc6c4a082d
commit 17331526aa
4 changed files with 75 additions and 1 deletions

View file

@ -54,6 +54,22 @@ class SubjectController extends AbstractController
return $this->json(null, 201); return $this->json(null, 201);
} }
#[Route('/api/subjects/{id}', methods: ['GET'])]
public function show(
int $id,
SubjectRepository $repository,
SubjectPresenter $presenter,
OutputService $output,
): JsonResponse {
$subject = $repository->find($id);
if ($subject === null) {
return $this->json($output->error(['UNKNOWN_SUBJECT']), 404);
}
return $this->json($output->success(['subject' => $presenter->present($subject)]));
}
#[Route('/api/subjects/{id}', methods: ['PATCH'])] #[Route('/api/subjects/{id}', methods: ['PATCH'])]
public function update( public function update(
int $id, int $id,

View file

@ -32,6 +32,8 @@ final class RatingFactory extends PersistentObjectFactory
protected function defaults(): array|callable protected function defaults(): array|callable
{ {
return [ return [
'score' => self::faker()->numberBetween(1, 5),
'note' => self::faker()->optional()->sentence() ?? '',
'subject' => RestaurantFactory::new(), 'subject' => RestaurantFactory::new(),
'user' => UserFactory::new(), 'user' => UserFactory::new(),
]; ];

View file

@ -15,6 +15,10 @@ class SubjectPresenter
*/ */
public function present(Subject $subject): array public function present(Subject $subject): array
{ {
$ratings = $subject->getRatings()->toArray();
usort($ratings, fn($a, $b) => $b->getCreatedAt() <=> $a->getCreatedAt());
return [ return [
'id' => $subject->getId(), 'id' => $subject->getId(),
'name' => $subject->getName(), 'name' => $subject->getName(),
@ -24,13 +28,32 @@ class SubjectPresenter
'id' => $subject->getCreatedBy()->getId(), 'id' => $subject->getCreatedBy()->getId(),
'email' => $subject->getCreatedBy()->getEmail(), 'email' => $subject->getCreatedBy()->getEmail(),
], ],
'ratingCount' => count($ratings),
'averageScore' => $this->computeAverage($ratings),
'ratings' => array_map( 'ratings' => array_map(
fn($r) => $this->ratingPresenter->present($r), fn($r) => $this->ratingPresenter->present($r),
$subject->getRatings()->toArray() $ratings
), ),
]; ];
} }
/**
* @param array<\App\Entity\Rating> $ratings
*/
private function computeAverage(array $ratings): ?float
{
$scores = array_filter(
array_map(fn($r) => $r->getScore(), $ratings),
fn($s) => $s !== null
);
if (count($scores) === 0) {
return null;
}
return round(array_sum($scores) / count($scores), 1);
}
/** /**
* @param iterable<Subject> $subjects * @param iterable<Subject> $subjects
* @return array<int, array<string, mixed>> * @return array<int, array<string, mixed>>

View file

@ -4,6 +4,7 @@ namespace App\Tests\Application\Controller\Api;
use App\Entity\Restaurant; use App\Entity\Restaurant;
use App\Entity\Subject; use App\Entity\Subject;
use App\Factory\RatingFactory;
use App\Factory\RestaurantFactory; use App\Factory\RestaurantFactory;
use App\Factory\UserFactory; use App\Factory\UserFactory;
use App\Repository\SubjectRepository; use App\Repository\SubjectRepository;
@ -39,6 +40,38 @@ class SubjectControllerTest extends WebTestCase
$this->assertNotNull($subject['longitude']); $this->assertNotNull($subject['longitude']);
} }
public function testShow(): void
{
$client = static::createClient();
$user = UserFactory::createOne();
$client->loginUser($user);
$subject = RestaurantFactory::createOne(['createdBy' => $user]);
RatingFactory::createOne(['subject' => $subject, 'score' => 4, 'user' => $user]);
RatingFactory::createOne(['subject' => $subject, 'score' => 2, 'user' => UserFactory::createOne()]);
$client->request('GET', '/api/subjects/' . $subject->getId(), server: ['CONTENT_TYPE' => 'application/json']);
$data = json_decode($client->getResponse()->getContent(), true);
$this->assertResponseIsSuccessful();
$this->assertSame($subject->getId(), $data['payload']['subject']['id']);
$this->assertSame($subject->getName(), $data['payload']['subject']['name']);
$this->assertCount(2, $data['payload']['subject']['ratings']);
$this->assertSame(2, $data['payload']['subject']['ratingCount']);
$this->assertEquals(3.0, $data['payload']['subject']['averageScore']);
$rating = $data['payload']['subject']['ratings'][0];
$this->assertArrayHasKey('id', $rating);
$this->assertArrayHasKey('score', $rating);
$this->assertArrayHasKey('note', $rating);
$this->assertArrayHasKey('createdAt', $rating);
$this->assertArrayHasKey('user', $rating);
$this->assertArrayHasKey('id', $rating['user']);
}
public function testCreate(): void public function testCreate(): void
{ {
$client = static::createClient(); $client = static::createClient();