4 - Extend SubjectPresenter with aggregate fields + add show endpoint
This commit is contained in:
parent
dc6c4a082d
commit
17331526aa
4 changed files with 75 additions and 1 deletions
|
|
@ -54,6 +54,22 @@ class SubjectController extends AbstractController
|
|||
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'])]
|
||||
public function update(
|
||||
int $id,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ final class RatingFactory extends PersistentObjectFactory
|
|||
protected function defaults(): array|callable
|
||||
{
|
||||
return [
|
||||
'score' => self::faker()->numberBetween(1, 5),
|
||||
'note' => self::faker()->optional()->sentence() ?? '',
|
||||
'subject' => RestaurantFactory::new(),
|
||||
'user' => UserFactory::new(),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ class SubjectPresenter
|
|||
*/
|
||||
public function present(Subject $subject): array
|
||||
{
|
||||
$ratings = $subject->getRatings()->toArray();
|
||||
|
||||
usort($ratings, fn($a, $b) => $b->getCreatedAt() <=> $a->getCreatedAt());
|
||||
|
||||
return [
|
||||
'id' => $subject->getId(),
|
||||
'name' => $subject->getName(),
|
||||
|
|
@ -24,13 +28,32 @@ class SubjectPresenter
|
|||
'id' => $subject->getCreatedBy()->getId(),
|
||||
'email' => $subject->getCreatedBy()->getEmail(),
|
||||
],
|
||||
'ratingCount' => count($ratings),
|
||||
'averageScore' => $this->computeAverage($ratings),
|
||||
'ratings' => array_map(
|
||||
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
|
||||
* @return array<int, array<string, mixed>>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace App\Tests\Application\Controller\Api;
|
|||
|
||||
use App\Entity\Restaurant;
|
||||
use App\Entity\Subject;
|
||||
use App\Factory\RatingFactory;
|
||||
use App\Factory\RestaurantFactory;
|
||||
use App\Factory\UserFactory;
|
||||
use App\Repository\SubjectRepository;
|
||||
|
|
@ -39,6 +40,38 @@ class SubjectControllerTest extends WebTestCase
|
|||
$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
|
||||
{
|
||||
$client = static::createClient();
|
||||
|
|
|
|||
Loading…
Reference in a new issue