Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Enable highend SPAs using REST-APIs with Symfony 4

Enable highend SPAs using REST-APIs with Symfony 4

This talk describes how to prototype an API using Symfony 4's event cycle and introduces API Platform as a way for a production ready API built on top of the same concepts.

Denis Brumann

May 26, 2018
Tweet

More Decks by Denis Brumann

Other Decks in Programming

Transcript

  1. /** * @Route("/", name="catalog") */ public function showAction(): Response {

    $products = $this->getDoctrine()
 ->getRepository(Product::class)
 ->findAll(); return $this->render( 'catalog/show.html.twig', [ 'products' => $products, ] ); }
  2. /** * @Route("/", name="catalog") */ public function showAction(): Response {

    $products = $this->getDoctrine()
 ->getRepository(Product::class)
 ->findAll(); return $this->render( 'catalog/show.html.twig', [ 'products' => $products, ] ); }
  3. /** * @Route("/products", name="products") * @Method("GET") */ public function listProducts()

    { return $this->getDoctrine()
 ->getRepository(Product::class)
 ->findAll(); }
  4. /** * @Route("/products", name="products") * @Method("GET") */ public function listProducts()

    { return $this->getDoctrine()
 ->getRepository(Product::class)
 ->findAll(); } !❗
  5. class JsonResponseHandler implements EventSubscriberInterface { public static function getSubscribedEvents() {

    return [ KernelEvents::VIEW => 'onKernelView', ]; } public function onKernelView(
 GetResponseForControllerResultEvent $event
 ) { $data = $event->getControllerResult(); $response = new JsonResponse($data); if ($data === null) { $response->setStatusCode(
 JsonResponse::HTTP_NO_CONTENT
 ); } $event->setResponse($response); } }
  6. /** * @Route("/products/{id}", name="product_show") * @Method("GET") */ public function getProduct(int

    $id) { $product = $this->getDoctrine() ->getRepository(Product::class) ->find($id); if (!$product) { throw new NotFoundHttpException('...'); } return $product; }
  7. $request = $event->getRequest(); if (strpos($request->getPathInfo(), '/api/') !== 0) { return;

    } $exception = $event->getException(); $error = [ 'type' => $this->detectErrorType($exception), // Warning! Passing the message is insecure! 'message' => $exception->getMessage(), ]; $response = new JsonResponse(
 $error,
 $this->detectStatusCode($exception)
 ); $event->setResponse($response);
  8. /** * @Route("/products", name="products_create") * @Method("POST") */ public function createProduct(Product

    $product) { $manager = $this->getDoctrine()->getManager(); $manager->persist($product); $manager->flush(); return new JsonResponse( null, JsonResponse::HTTP_CREATED ); }
  9. interface ArgumentValueResolverInterface { /** * Whether this resolver can resolve

    the value for the given
 * ArgumentMetadata. * * @param Request $request * @param ArgumentMetadata $argument * * @return bool */ public function supports(Request $request, ArgumentMetadata $argument); /** * Returns the possible value(s). * * @param Request $request * @param ArgumentMetadata $argument * * @return \Generator */ public function resolve(Request $request, ArgumentMetadata $argument); }
  10. public function supports(
 Request $request,
 ArgumentMetadata $argument
 ) { return

    ( strpos($request->getPathInfo(), '/api/') === 0 && $argument->getType() === Product:class && !empty($request->getContent()) ); }
  11. public function resolve(Request $request, ArgumentMetadata $argument) { $data = $this->serializer->deserialize(

    $request->getContent(), $argument->getType(), 'json' ); $errors = $this->validator->validate($data, null, $groups); if (count($errors)) { throw new BadRequestHttpException((string) $errors); } yield $data; }
  12. Request Response kernel.
 request kernel.
 response kernel.
 controller kernel.
 exception

    resolve controller call controller kernel.
 terminate Exception resolve arguments kernel.
 view SYMFONY KERNEL EVENTS
  13. SYMFONY 4 BENEFITS No changes to services.yaml were needed! Only

    1 added dependency (orm-pack) Small build artefact due to fewer dependencies
  14. <?php namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; use Doctrine\ORM\Mapping as ORM; use

    Symfony\Component\Validator\Constraints as Assert; /** * @ApiResource * @ORM\Entity */ class Rollercoaster { /** * @ORM\Id() * @ORM\GeneratedValue * @ORM\Column(type="integer") */ public $id; // ... }
  15. <?php namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; use Doctrine\ORM\Mapping as ORM; use

    Symfony\Component\Validator\Constraints as Assert; /** * @ApiResource * @ORM\Entity */ class Rollercoaster { /** * @ORM\Id() * @ORM\GeneratedValue * @ORM\Column(type="integer") */ public $id; // ... }
  16. FEATURES Builds a fully featured API with JSON-LD, Hydra &

    GraphQL Auto-generated documentation with Swagger or Open API Authentication with JWT or OAuth HTTP Caching, CORS-support, schema.org-data model