Skip to main content
Evgeny Smirnov
and how to use (
fi
rst look)
+
Why?
+
+
+
What’s inside?
+
Out of the box
“CRUD, data validation, pagination,
fi
ltering, sorting, json/hydra, GraphQL,
swagger, CORS, OWASP inside…”
+
Follow best practice because
you can’t do otherwise
+
Getting started
1. Official “Getting started” guide


2. SymfonyCast: RESTful APIs and


API Platform guides


3. StackOverflow
+
Installation
Dockerised distribution


(check symfony version)


or through symfony
+
Why?
+
+
Custom business logic for


any writing action — DataPersisters*
* use decorator pattern
+
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
+
Data Providers
Here should be an example


but I have not used providers…
+
Custom action for an action


of a resource — Action Controller
+
#[AsController]


class SkipUserQuestion extends AbstractController


{


public function __invoke(UserQuestion $data): UserQuestion


{


$data->setStatus(UserQuestion::STATUS_SKIPPED);


return $data;


}


}
Pseudo Controllers
+
Various input and output data


for the same model —


DataTransformer and DTO
+
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
+
final class ResetPasswordRequestInput


{


#[Groups(['resetPasswordRequest:create', 'resetPasswordRequest:read'])]


#[AssertNotBlank(groups: ['validation:create'])]


#[AssertEmail()]


private $email;


public function getEmail(): ?string


{


return $this->email;


}


public function setEmail(string $email): self


{


$this->email = $email;


return $this;


}


}
DTOs
+
… and much more:


EventListeners, Subscribers,


Filters, async …
+
Useful add ons
✅ JWT through LexikJWTAuthenticationBundle


✅ JWT refresh tokens GesdinetJWTRefreshTokenBundle


❌ Complete sign up / sign in


❌ Role based API versions
+
Disambiguous?
+
PATCH /entity/{id} or


PUT /entity/{id}/{custom-action}
+
GET /entity/{id}/?{subentity}=% or


GET /entity/{id}/{subentity}
+
Action-Controller or DataPersister


for custom writing logic?
+
4-5 extra classes (DTOs, Transformers, etc.) or


Custom controller outside of API Platform*
* and extra classes for OpenAPI docs…
+
Too many ways


how to perform a regular action
+
Good for RESTful APIs with regular customisations


Bad for custom APIs
+
+
+