Skip to content

Commit

Permalink
Practice 6: filters, sorts and pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoineGonzalez committed Apr 26, 2024
1 parent 4970d1b commit fb91fc2
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 34 deletions.
15 changes: 7 additions & 8 deletions src/Controller/API/Dinosaurs/Create.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

use App\Entity\Dinosaur;
use App\Entity\Species;
use Doctrine\Persistence\ManagerRegistry;
use App\Repository\DinosaurRepository;
use App\Repository\SpeciesRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
Expand Down Expand Up @@ -83,7 +84,8 @@ enum: ["Male", "Female"],
description: 'The species ID does not exists'
)]
public function __invoke(
ManagerRegistry $manager,
DinosaurRepository $dinosaurRepository,
SpeciesRepository $speciesDinosaur,
Request $request,
SerializerInterface $serializer,
JsonSchemaValidator $jsonSchemaValidator
Expand All @@ -100,9 +102,7 @@ public function __invoke(

$dinosaurData = json_decode($request->getContent(), true);

$species = $manager
->getRepository(Species::class)
->find($dinosaurData['speciesId']);
$species = $speciesDinosaur->find($dinosaurData['speciesId']);

if (!$species instanceof Species) {
return new JsonResponse([
Expand All @@ -122,9 +122,8 @@ public function __invoke(
$dinosaurData['eyesColor'],
);

$em = $manager->getManager();
$em->persist($dinosaur);
$em->flush();
$dinosaurRepository->persist($dinosaur);
$dinosaurRepository->flush();

$content = $serializer->serialize(
$dinosaur,
Expand Down
13 changes: 5 additions & 8 deletions src/Controller/API/Dinosaurs/Delete.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace App\Controller\API\Dinosaurs;

use App\Entity\Dinosaur;
use Doctrine\Persistence\ManagerRegistry;
use App\Repository\DinosaurRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
Expand All @@ -24,21 +24,18 @@ final class Delete extends AbstractController
response: Response::HTTP_NO_CONTENT,
description: 'Dinosaur successfully deleted'
)]
public function __invoke(ManagerRegistry $manager, string $id): Response
public function __invoke(DinosaurRepository $dinosaurRepository, string $id): Response
{
$dinosaur = $manager
->getRepository(Dinosaur::class)
->find($id);
$dinosaur = $dinosaurRepository->find($id);

if (!$dinosaur instanceof Dinosaur) {
return new JsonResponse([
'message' => sprintf('Dinosaur with id %s not found', $id)
], Response::HTTP_UNPROCESSABLE_ENTITY);
}

$em = $manager->getManager();
$em->remove($dinosaur);
$em->flush();
$dinosaurRepository->remove($dinosaur);
$dinosaurRepository->flush();

return new Response(status: Response::HTTP_NO_CONTENT);
}
Expand Down
68 changes: 64 additions & 4 deletions src/Controller/API/Dinosaurs/GetAll.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,59 @@
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\HttpFoundation\Request;
use Nelmio\ApiDocBundle\Annotation\Model;
use OpenApi\Attributes as OA;

final class GetAll extends AbstractController
{
#[Route('/api/dinosaurs', methods: 'GET')]
#[OA\Tag('dinosaur')]
#[OA\Parameter(
parameter: 'page',
name: 'page',
in: 'query',
schema: new OA\Schema(
type: 'integer',
default: 1,
minimum: 1
),
)]
#[OA\Parameter(
parameter: 'limit',
name: 'limit',
in: 'query',
schema: new OA\Schema(
type: 'integer',
default: 25,
minimum: 1
),
)]
#[OA\Parameter(
parameter: 'search',
name: 'search',
in: 'query',
schema: new OA\Schema(type: 'string'),
example: 'Dino'
)]
#[OA\Parameter(
parameter: 'filters',
name: 'filters',
in: 'query',
schema: new OA\Schema(type: 'object'),
style: 'deepObject',
explode: true,
example: '{"name": "rex", "gender": "male"}',
)]
#[OA\Parameter(
parameter: 'sorts',
name: 'sorts',
in: 'query',
schema: new OA\Schema(type: 'object'),
style: 'deepObject',
explode: true,
example: '{"age": "ASC"}'
)]
#[OA\Response(
response: Response::HTTP_OK,
description: 'List all the dinosaurs',
Expand All @@ -32,12 +78,26 @@ final class GetAll extends AbstractController
)
)]
public function __invoke(
ManagerRegistry $manager,
SerializerInterface $serializer
Request $request,
SerializerInterface $serializer,
ManagerRegistry $managerRegistry,
): Response {
$dinosaurs = $manager
$filters = $request->query->all('filters') ?? [];
$sorts = $request->query->all('sorts') ?? [];
$search = $request->query->get('search');
$page = $request->query->getInt('page', 1);
$limit = $request->query->getInt('limit', 5);

$dinosaurs = $managerRegistry
->getRepository(Dinosaur::class)
->findAll();
->search($search)
->filter($filters)
->search($search)
->sort($sorts)
->paginate(
$page,
$limit
);

$content = $serializer->serialize(
$dinosaurs,
Expand Down
7 changes: 3 additions & 4 deletions src/Controller/API/Dinosaurs/GetOne.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace App\Controller\API\Dinosaurs;

use App\Entity\Dinosaur;
use App\Repository\DinosaurRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
Expand All @@ -28,13 +29,11 @@ final class GetOne extends AbstractController
content: new Model(type: Dinosaur::class, groups: ['dinosaur'])
)]
public function __invoke(
ManagerRegistry $manager,
DinosaurRepository $dinosaurRepository,
SerializerInterface $serializer,
string $id
): Response {
$dinosaur = $manager
->getRepository(Dinosaur::class)
->find($id);
$dinosaur = $dinosaurRepository->find($id);

if (!$dinosaur instanceof Dinosaur) {
return new JsonResponse([
Expand Down
77 changes: 67 additions & 10 deletions src/Repository/DinosaurRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,82 @@

use App\Entity\Dinosaur;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;

class DinosaurRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
private const ALIAS = 'dinosaur';

private QueryBuilder $queryBuilder;
private ManagerRegistry $registry;

public function __construct(
ManagerRegistry $registry,
?QueryBuilder $queryBuilder
) {
parent::__construct($registry, Dinosaur::class);

$this->registry = $registry;
$this->queryBuilder = $queryBuilder ?: $this->createQueryBuilder(self::ALIAS);
}

public function search(?string $q): array

public function search(?string $name = null): self
{
if (null === $q) {
return $this->findAll();
$queryBuilder = $this->cloneQueryBuilder();

if ($name !== null) {
$queryBuilder
->andWhere(sprintf('%s.name LIKE :name', self::ALIAS))
->setParameter('name', '%' . $name . '%');
}

return $this->createQueryBuilder('d')
->where('d.name = :q')
->setParameter('q', $q)
->getQuery()
->getResult();
return $this->duplicate($queryBuilder);
}

public function sort(array $sorts): self
{
$queryBuilder = $this->cloneQueryBuilder();

foreach ($sorts as $field => $order) {
$queryBuilder->addOrderBy(sprintf('%s.%s', self::ALIAS, $field), $order);
}

return $this->duplicate($queryBuilder);
}

public function filter(array $filters): self
{
$queryBuilder = $this->cloneQueryBuilder();

foreach ($filters as $field => $value) {
$queryBuilder
->andWhere(sprintf('%s.%s = :%s', self::ALIAS, $field, $field))
->setParameter($field, $value);
}

return $this->duplicate($queryBuilder);
}

public function paginate(int $page, int $limit): array
{
$queryBuilder = $this->cloneQueryBuilder();

$queryBuilder
->setFirstResult(($page - 1) * $limit)
->setMaxResults($limit);

return $queryBuilder->getQuery()->getResult();
}

private function cloneQueryBuilder(): QueryBuilder
{
return clone $this->queryBuilder;
}

private function duplicate(QueryBuilder $queryBuilder): self
{
return new self($this->registry, $queryBuilder);
}
}

0 comments on commit fb91fc2

Please sign in to comment.