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.

6a1345d8e6dd15b2c78eff0c331963b1?s=128

Denis Brumann

May 26, 2018
Tweet

Transcript

  1. 3.
  2. 6.

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

    $products = $this->getDoctrine()
 ->getRepository(Product::class)
 ->findAll(); return $this->render( 'catalog/show.html.twig', [ 'products' => $products, ] ); }
  3. 7.

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

    $products = $this->getDoctrine()
 ->getRepository(Product::class)
 ->findAll(); return $this->render( 'catalog/show.html.twig', [ 'products' => $products, ] ); }
  4. 8.

    /** * @Route("/products", name="products") * @Method("GET") */ public function listProducts()

    { return $this->getDoctrine()
 ->getRepository(Product::class)
 ->findAll(); }
  5. 9.

    /** * @Route("/products", name="products") * @Method("GET") */ public function listProducts()

    { return $this->getDoctrine()
 ->getRepository(Product::class)
 ->findAll(); } !❗
  6. 10.
  7. 11.
  8. 12.
  9. 17.

    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); } }
  10. 18.
  11. 19.

    /** * @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; }
  12. 20.
  13. 23.

    $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);
  14. 24.
  15. 25.
  16. 26.
  17. 27.

    /** * @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 ); }
  18. 30.

    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); }
  19. 31.

    public function supports(
 Request $request,
 ArgumentMetadata $argument
 ) { return

    ( strpos($request->getPathInfo(), '/api/') === 0 && $argument->getType() === Product:class && !empty($request->getContent()) ); }
  20. 32.

    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; }
  21. 33.
  22. 34.

    Request Response kernel.
 request kernel.
 response kernel.
 controller kernel.
 exception

    resolve controller call controller kernel.
 terminate Exception resolve arguments kernel.
 view SYMFONY KERNEL EVENTS
  23. 35.
  24. 36.

    SYMFONY 4 BENEFITS No changes to services.yaml were needed! Only

    1 added dependency (orm-pack) Small build artefact due to fewer dependencies
  25. 37.
  26. 39.
  27. 41.
  28. 42.

    <?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; // ... }
  29. 43.

    <?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; // ... }
  30. 44.
  31. 45.
  32. 46.

    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
  33. 47.
  34. 49.