Jednoduché API ve frameworku Slim 4 – č. 7 Validace dat
- Jednoduché API ve frameworku Slim 4 – č. 1 Instalace
- Jednoduché API ve frameworku Slim 4 – č. 2 Základní CRUD
- Jednoduché API ve frameworku Slim 4 – č. 3 Struktura API a připojení k databázi
- Jednoduché API ve frameworku Slim 4 – č. 4 Testování funkcionality našeho malého API
- Jednoduché API ve frameworku Slim 4 – č. 5 Vylepšení UserControlleru
- Jednoduché API ve frameworku Slim 4 – č. 6 Přidání Model a Repositories
- Jednoduché API ve frameworku Slim 4 – č. 7 Validace dat
- Jednoduché API ve frameworku Slim 4 – č. 8 Přidáme si do datbáze produkty
Naše API již se má čile k světu, co se mi ale dál neíbí a zasloužilo by upravit je validace vstupních dat v controlleru. Bylo by lepší a přehlednější ji vyčlenit do samostané třídy.
Takže si vytvoříme další adresář Validators a v něm novou třídu pro kontrolu vstupních dat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<?php namespace App\Validators; use Slim\Exception\HttpBadRequestException; class UserDataValidator { /** * @throws HttpBadRequestException */ public function validate(array $data): void { $this->validateRequired($data); $this->validateEmail($data['email']); } /** * @throws HttpBadRequestException */ private function validateRequired(array $data): void { if (empty($data['name']) || empty($data['email'])) { throw new HttpBadRequestException(null, 'Name and email are required'); } } /** * @throws HttpBadRequestException */ private function validateEmail(string $email): void { if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { throw new HttpBadRequestException(null, 'Invalid email format'); } } } |
a upravíme adekvátně náš controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
<?php namespace App\Controllers; use App\Repositories\UserRepository; use App\Validators\UserDataValidator; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Slim\Exception\HttpNotFoundException; use Slim\Exception\HttpBadRequestException; class UserController { private UserRepository $userRepository; private UserDataValidator $validator; public function __construct( UserRepository $userRepository, UserDataValidator $validator ) { $this->userRepository = $userRepository; $this->validator = $validator; } private function jsonResponse(Response $response, mixed $data, int $status = 200): Response { $response->getBody()->write(json_encode($data, JSON_THROW_ON_ERROR)); return $response ->withHeader('Content-Type', 'application/json') ->withStatus($status); } public function getAll(Request $request, Response $response): Response { $users = $this->userRepository->findAll(); return $this->jsonResponse($response, $users); } public function getOne(Request $request, Response $response, array $args): Response { $user = $this->userRepository->findById((int)$args['id']); if (!$user) { throw new HttpNotFoundException($request, 'User not found'); } return $this->jsonResponse($response, $user); } public function create(Request $request, Response $response): Response { $data = $request->getParsedBody(); $this->validator->validate($data); try { $user = $this->userRepository->create($data); return $this->jsonResponse($response, $user, 201); } catch (\RuntimeException $e) { if ($e->getCode() === 23000) { throw new HttpBadRequestException($request, 'Email already exists'); } throw $e; } } public function update(Request $request, Response $response, array $args): Response { $data = $request->getParsedBody(); $this->validator->validate($data); try { $user = $this->userRepository->update((int)$args['id'], $data); if (!$user) { throw new HttpNotFoundException($request, 'User not found'); } return $this->jsonResponse($response, $user); } catch (\RuntimeException $e) { if ($e->getCode() === 23000) { throw new HttpBadRequestException($request, 'Email already exists'); } throw $e; } } public function delete(Request $request, Response $response, array $args): Response { $deleted = $this->userRepository->delete((int)$args['id']); if (!$deleted) { throw new HttpNotFoundException($request, 'User not found'); } return $response->withStatus(204); } } |
Krása střídá nádheru. Otestuje všechny požadavky na naše API zda vše funguje jak má. Jenom mám takový problém, že když si testuji PUT požadavek (update nějakého záznamu v databázi) validator vyžaduje všechny povinné položky tedy jak jméno tak email uživatele. Mělo by ale přeci stačit zadat pouze položku, kterou chci updatovat. Takže si upravíme validator následovně
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
<?php namespace App\Validators; use Slim\Exception\HttpBadRequestException; class UserDataValidator { /** * Validates data for user creation * @throws HttpBadRequestException */ public function validateForCreate(array $data): void { if (empty($data['name']) || empty($data['email'])) { throw new HttpBadRequestException(null, 'Name and email are required'); } $this->validateEmail($data['email']); } /** * Validates data for user update * At least one field should be present * @throws HttpBadRequestException */ public function validateForUpdate(array $data): void { if (empty($data)) { throw new HttpBadRequestException(null, 'No data provided for update'); } // Validate email only if it's being updated if (isset($data['email'])) { if (empty($data['email'])) { throw new HttpBadRequestException(null, 'Email cannot be empty'); } $this->validateEmail($data['email']); } // Validate name only if it's being updated if (isset($data['name']) && empty($data['name'])) { throw new HttpBadRequestException(null, 'Name cannot be empty'); } } /** * @throws HttpBadRequestException */ private function validateEmail(string $email): void { if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { throw new HttpBadRequestException(null, 'Invalid email format'); } } } |
Máme tedy dvě samostané metod jednu pro vytvoření a druhou pro update uživatele. Upravíme si ještě metody v controlleru
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
public function create(Request $request, Response $response): Response { $data = $request->getParsedBody(); $this->validator->validateForCreate($data); try { $user = $this->userRepository->create($data); return $this->jsonResponse($response, $user, 201); } catch (\RuntimeException $e) { if ($e->getCode() === 23000) { throw new HttpBadRequestException($request, 'Email already exists'); } throw $e; } } public function update(Request $request, Response $response, array $args): Response { $data = $request->getParsedBody(); $this->validator->validateForUpdate($data); try { $user = $this->userRepository->update((int)$args['id'], $data); if (!$user) { throw new HttpNotFoundException($request, 'User not found'); } return $this->jsonResponse($response, $user); } catch (\RuntimeException $e) { if ($e->getCode() === 23000) { throw new HttpBadRequestException($request, 'Email already exists'); } throw $e; } } |
Opět pořádně otestujeme všechny endpointy našeho API, zda vše funguje jak má. Funguje a API získalo na přehlednosti.
Podobným způsobem, bychom si mohli přidat další endpointy pro další tabulky v naší aplikaci.