+
Out of the box
“CRUD, data validation, pagination,
fi
ltering, sorting, json/hydra, GraphQL,
swagger, CORS, OWASP inside…”
Slide 7
Slide 7 text
+
Follow best practice because
you can’t do otherwise
Slide 8
Slide 8 text
+
Getting started
1. Official “Getting started” guide
2. SymfonyCast: RESTful APIs and
API Platform guides
3. StackOverflow
Slide 9
Slide 9 text
+
Installation
Dockerised distribution
(check symfony version)
or through symfony
Slide 10
Slide 10 text
+
Why?
Slide 11
Slide 11 text
+
Slide 12
Slide 12 text
+
Custom business logic for
any writing action — DataPersisters*
* use decorator pattern
Slide 13
Slide 13 text
+
final class UserQuizDataPersister implements ContextAwareDataPersisterInterface
{
private $decorated;
private $security;
…
public function persist($data, array $context = [])
{
if (is_null($data->getUser())) {
$user = $this->security->getUser();
$data->setUser($user);
}
$result = $this->decorated->persist($data, $context);
return $result;
}
public function remove($data, array $context = [])
{
return $this->decorated->remove($data, $context);
}
}
Data Persisters
Slide 14
Slide 14 text
+
Data Providers
Here should be an example
but I have not used providers…
Slide 15
Slide 15 text
+
Custom action for an action
of a resource — Action Controller
Slide 16
Slide 16 text
+
#[AsController]
class SkipUserQuestion extends AbstractController
{
public function __invoke(UserQuestion $data): UserQuestion
{
$data->setStatus(UserQuestion::STATUS_SKIPPED);
return $data;
}
}
Pseudo Controllers
Slide 17
Slide 17 text
+
Various input and output data
for the same model —
DataTransformer and DTO
Slide 18
Slide 18 text
+
public function transform($data, string $to, array $context = [])
{
$resetPasswordRequest = new ResetPasswordRequest();
$user = $this->userRepository->findOneByEmail($data->getEmail());
$resetPasswordRequest->setUser($user);
$now = new \DateTimeImmutable();
$expiredAt = new \DateTimeImmutable('+1 hour');
$resetPasswordRequest->setRequestedAt($now);
$resetPasswordRequest->setExpiresAt($expiredAt);
return $resetPasswordRequest;
}
Data Transformers
Slide 19
Slide 19 text
+
final class ResetPasswordRequestInput
{
#[Groups(['resetPasswordRequest:create', 'resetPasswordRequest:read'])]
#[Assert\NotBlank(groups: ['validation:create'])]
#[Assert\Email()]
private $email;
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
}
DTOs
Slide 20
Slide 20 text
+
… and much more:
EventListeners, Subscribers,
Filters, async …
Slide 21
Slide 21 text
+
Useful add ons
✅ JWT through LexikJWTAuthenticationBundle
✅ JWT refresh tokens GesdinetJWTRefreshTokenBundle
❌ Complete sign up / sign in
❌ Role based API versions
Slide 22
Slide 22 text
+
Disambiguous?
Slide 23
Slide 23 text
+
PATCH /entity/{id} or
PUT /entity/{id}/{custom-action}
Slide 24
Slide 24 text
+
GET /entity/{id}/?{subentity}=% or
GET /entity/{id}/{subentity}
Slide 25
Slide 25 text
+
Action-Controller or DataPersister
for custom writing logic?
Slide 26
Slide 26 text
+
4-5 extra classes (DTOs, Transformers, etc.) or
Custom controller outside of API Platform*
* and extra classes for OpenAPI docs…
Slide 27
Slide 27 text
+
Too many ways
how to perform a regular action
Slide 28
Slide 28 text
+
Good for RESTful APIs with regular customisations
Bad for custom APIs