Service approach for development REST API in Symfony2

40
Service approach for development Rest API in Symfony2 Aleksey Kryvtsov @ Web-developer at CPCS [email protected]

Transcript of Service approach for development REST API in Symfony2

Page 1: Service approach for development REST API in Symfony2

Service approach for

development Rest API in

Symfony2Aleksey Kryvtsov @ Web-developer at CPCS

[email protected]

Page 2: Service approach for development REST API in Symfony2

Development Rest API in Symfony22

Page 3: Service approach for development REST API in Symfony2

● Interface as contracts.● Thin Controller, Fat Service.● The Content Negotiation in the HTTP and REST.● Form as API interface.

Rules

3

Page 4: Service approach for development REST API in Symfony2

● FOSRestBundle● JMSSerializerBundle● NelmioApiDocBundle

List of bundles

// app/AppKernel.php

$bundles = [

//...

new FOS\RestBundle\FOSRestBundle(),new JMS\SerializerBundle\JMSSerializerBundle(),new Nelmio\ApiDocBundle\

NelmioApiDocBundle(),4

Page 5: Service approach for development REST API in Symfony2

/api/v1/{_lang}/pages/{id}.{_format}

/api/v1/en/pages/{id}.jsonWill return a json file

/api/v1/en/pages/{id}.xml Will return a xml file

/api/v1/en/pages/{id} and /api/v1/en/pages/{id}.html Will return the web page file

Format of routing

5

Page 6: Service approach for development REST API in Symfony2

# /app/config/routing.ymlacme_api_blog:

type: restprefix: /api/v1/{_lang}resource:

"@AcmeBlogBundle/Resources/config/routes.yml"

Adding the routes

6

Page 7: Service approach for development REST API in Symfony2

# /src/Acme/BlogBundle/Resources/config/routes.ymlacme_api_blog_page:

resource: "Acme\BlogBundle\Controller\PageController"

name_prefix: api_v1_ # naming collision

Create a route file into the bundle

# php app/console route:debug | grep api_v1_

7

Page 8: Service approach for development REST API in Symfony2

# /src/Acme/BlogBundle/Controller/PageController.phpnamespace Acme\BlogBundle\Controller;

use FOS\RestBundle\Controller\FOSRestController as Rest;

class PageController extends Rest {// another methods POST, PUT, DELETE and etc...

/* @annotations */

public function getPageAction ($id) {return $this->get('acme_blog.blog_post.handler')->get($id);

}}

Controller: PageController::getPageAction ()

8

Page 9: Service approach for development REST API in Symfony2

# /src/Acme/BlogBundle/Controller/PageController.phpuse FOS\RestBundle\Controller\Annotations as FOSRest;

/** * Get single Page * @FOSRest\Get("/pages/{id}", requirements={"id" = "\d+"}, name="get_page") * @FOSRest\View(serializerGroups={"page"}) * * @ApiDoc(/* documentation */) * * @param int $id The page id * * @return array * @throws NotFoundHttpException when page not exist or not found */public function getPageAction ($id) {}

Controller: PageController::getPageAction ()

9

Page 10: Service approach for development REST API in Symfony2

# /src/Acme/BlogBundle/Entity/Page.phpnamespace Acme\BlogBundle\Entity;

class Page implements PageInterface {// fields, getters and setters

}

Interface as contract# /src/Acme/BlogBundle/Model/PageInterface.phpnamespace Acme\BlogBundle\Model;

interface PageInterface {}

10

Page 11: Service approach for development REST API in Symfony2

Handler configuration. Part I# /src/Acme/BlogBundle/Resources/config/handlers.yml

services:1 acme_blog.page.entity:

class: Acme\BlogBundle\Entity\Page

2 acme_blog.blog_post.handler: class: Acme\BlogBundle\Handler\PageHandler arguments:3 - @doctrine.orm.entity_manager4 - @acme_blog.page.entity

11

Page 12: Service approach for development REST API in Symfony2

HandlerInterface: PageHandlerInterface::get()# /src/Acme/BlogBundle/Handler/PageHandlerInterface.phpnamespace Acme\BlogBundle\Handler; use Acme\BlogBundle\Model\PageInterface;

interface PageHandlerInterface {// another methods POST, PUT, DELETE and etc...

/** * Gets a page by id * * @api * @param integer $id * * @return PageInterface */public function get ($id);}

12

Page 13: Service approach for development REST API in Symfony2

Handler: PageHandler::__construct()# /src/Acme/BlogBundle/Handler/PageHandler.phpnamespace Acme\BlogBundle\Handler;

use Doctrine\ORM\EntityManager;use Acme\BlogBundle\Model\PageInterface;

class PageHandler implements PageHandlerInterface {

// another methods

/** * @return void */public function __construct(EntityManager $entityManager,PageInterface $pageEntity) // continue...

13

Page 14: Service approach for development REST API in Symfony2

Handler: PageHandler::__construct()# /src/Acme/BlogBundle/Handler/PageHandler.phpnamespace Acme\BlogBundle\Handler;

class PageHandler implements PageHandlerInterface {

// another methods

/* public function __construct (...) */ {1 $this->entityManager = $entityManager;2 $this->pageEntity = $pageEntity;

3 $this->repository = $entityManager->getRepository(get_class($pageEntity));}}

14

Page 15: Service approach for development REST API in Symfony2

Handler: PageHandler (UML)

15

Page 16: Service approach for development REST API in Symfony2

Handler: PageHandler::get()# /src/Acme/BlogBundle/Handler/PageHandler.phpnamespace Acme\BlogBundle\Handler;

class PageHandler implements PageHandlerInterface {

// another methods POST, PUT, DELETE and etc...

/** * {@inheritdoc} */public function get ($id) {

return $this->repository->find($id);}}

16

Page 17: Service approach for development REST API in Symfony2

Controller: PageController::getAllPagesAction()

17

URL: ~/pages

Page 18: Service approach for development REST API in Symfony2

18

Diagram: getAction, getAllAction

Page 19: Service approach for development REST API in Symfony2

# /src/Acme/BlogBundle/Controller/PageController.phpuse Symfony\Component\HttpFoundation\Request;

class PageController extends Rest {// another methods

/* @annotations -> URL: ~/pages */public function postPageAction(Request $request) {

return $this->get('acme_blog.blog_post.handler')->post($request->request->all());

}}

Controller: PageController::postPageAction ()

19

Page 20: Service approach for development REST API in Symfony2

HandlerInterface: PageHandlerInterface::post()# /src/Acme/BlogBundle/Handler/PageHandlerInterface.php use Acme\BlogBundle\Model\PageInterface;

interface PageHandlerInterface {

/** * Creates a new page * * @api

* @param array $parameters* @param array $options * * @return PageInterface */public function post(array $parameters = [], array $options = []);}

20

Page 21: Service approach for development REST API in Symfony2

Handler: PageHandler::post()# /src/Acme/BlogBundle/Handler/PageHandler.php

/* {@inheritdoc} */public function post ( array $parameters = [], array $options = []) {

return $this->processForm($this->pageClass, $parameters,$options,

“POST”);

}21

Page 22: Service approach for development REST API in Symfony2

Handler configuration. Part II# /src/Acme/BlogBundle/Resources/config/handlers.ymlservices:

// ...acme_blog.page.form_type:

class: Acme\BlogBundle\Form\Type\PageFormType

acme_blog.blog_post.handler: class: Acme\BlogBundle\Handler\PageHandler arguments:

- @doctrine.orm.entity_manager - @acme_blog.page.entity - @form.factory - @acme_blog.page.form_type

22

Page 23: Service approach for development REST API in Symfony2

Handler: PageHandler::__construct()# /src/Acme/BlogBundle/Handler/PageHandler.phpuse Symfony\Component\Form\FormFactoryInterface;use use Symfony\Component\Form\FormTypeInterface;

class PageHandler implements PageHandlerInterface {

/** * @return void */public function __construct (

//...FormFactoryInterface $formFactory, FormTypeInterface $formType

) {// ...$this->formFactory = $formFactory;$this->formType = $formType;

} 23

Page 24: Service approach for development REST API in Symfony2

HandlerInterface: PageHandlerInterface::processForm()# /src/Acme/BlogBundle/Handler/PageHandlerInterface.phpuse Acme\BlogBundle\Model\PageInterface;

interface PageHandlerInterface {/** * Processes the form * * @api

* @param PageInterface $page* @param array $parameters

* @param array $options* @param string $method * * @return PageInterface */private function processForm(PageInterface $page, array $parameters = [], array $options = [],

$method)} 24

Page 25: Service approach for development REST API in Symfony2

Handler: PageHandler::processForm()# /src/Acme/BlogBundle/Handler/PageHandler.php

protected function processForm (/*...*/) {1 $form = $this->formFactory->create($this->formType, $page, $options);

2 $form->submit($parameters, 'PATCH' !== $method);

3 if ($form->isValid()) {4 $this->entityManager->persist($page);5 $this->entityManager->flush($page);6 return $page;

}7 // throw new InvalidFormException(/*...*/)}

25

Page 26: Service approach for development REST API in Symfony2

Controller: PageController::[patch|put]PageAction()

26

URL: ~/pages/{id}

Page 27: Service approach for development REST API in Symfony2

Handler: PageHandler::patch()# /src/Acme/BlogBundle/Handler/PageHandler.php

// another methods

public function patch(array $parameters = [], array $options = []) {1 $page = $this->get($parameters[‘id’]);

2 return $this->processForm($page,$parameters,$options,“PATCH”

);}

27

Page 28: Service approach for development REST API in Symfony2

Handler: PageHandler::put()# /src/Acme/BlogBundle/Handler/PageHandler.php

// another methods

public function put(array $parameters = [], array $options = []) {1 $page = $this->get($parameters[‘id’]);

2 return $this->processForm($page,$parameters,$options,“PUT”

);}

28

Page 29: Service approach for development REST API in Symfony2

29

Diagram: postAction, putAction, patchAction

Page 30: Service approach for development REST API in Symfony2

# /src/Acme/BlogBundle/Controller/PageController.php

class PageController extends Rest {// another methods

/* @annotations -> URL: ~/pages/{id} */public function deletePageAction($id) {

return $this->get('acme_blog.blog_post.handler')->delete($id);

}}

Controller: PageController::deletePageAction()

30

Page 31: Service approach for development REST API in Symfony2

HandlerInterface: PageHandlerInterface::delete()# /src/Acme/BlogBundle/Handler/PageHandlerInterface.php use Acme\BlogBundle\Model\PageInterface;

interface PageHandlerInterface {

/** * Returns the result of removal * * @api

* @param integer $id * * @return string */public function delete($id);}

31

Page 32: Service approach for development REST API in Symfony2

Handler: PageHandler::delete()# /src/Acme/BlogBundle/Handler/PageHandler.phpnamespace Acme\BlogBundle\Handler;

class PageHandler implements PageHandlerInterface {/** * {@inheritdoc} */public function delete ($id) {

1 $page = $this->get($id);

2 $this->entityManager->remove($page);3 $this->entityManager->flush();

}}

32

Page 33: Service approach for development REST API in Symfony2

Diagram

33

Page 34: Service approach for development REST API in Symfony2

34

UML

Page 35: Service approach for development REST API in Symfony2

1. # app/config/routing.ymlNelmioApiDocBundle: resource: "@NelmioApiDocBundle/Resources/config/routing.yml" prefix: /api/doc

Documentation: ApiDoc

35

2. # /src/Acme/BlogBundle/Controller/PageController.php/** * @ApiDoc( * description = "Creates a new page from the submitted data.", * input = "Acme\BlogBundle\Form\PageType", * output = "Acme\BlogBundle\Entity\Page", * statusCodes = { * 200 = "Returned when successful", * 400 = "Returned when the form has errors" * } * ) */

Page 36: Service approach for development REST API in Symfony2

36

Page 37: Service approach for development REST API in Symfony2

JMSSerializer

37

# /src/Acme/BlogBundle/Entity/Page.phpuse Doctrine\ORM\Mapping as ORM;use JMS\Serializer\Annotation as JMS;/** * @ORM\Entity * @JMS\ExclusionPolicy("all") */class Page { /** * @JMS\Expose * @JMS\Type("AnotherClass") * @JMS\Groups({"page", "details"}) * @ORM\ManyToOne(targetEntity="AnotherClass") * @ORM\JoinColumn(name="column", referencedColumnName="id") */ protected $field;

{ "id": 1, "field": AnotherClass, "another": "anotherType"}

Page 38: Service approach for development REST API in Symfony2

JMSSerializer: Annotations

38

● @ExclusionPolicy

● @Exclude

● @Expose

● @SerializedName

● @Since

● @Until

● @Groups

● @MaxDepth

● @AccessType

● @Accessor

● @AccessorOrder

● @VirtualProperty

● @Inline

● @ReadOnly

● @PreSerialize

● @PostSerialize

● @PostDeserialize

● @HandlerCallback

● @Discriminator

● @Type

● @XmlRoot

● @XmlAttribute

● @XmlValue

● @XmlList

● @XmlMap

● @XmlKeyValuePairs

Page 39: Service approach for development REST API in Symfony2

Referenceshttp://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-apiBest Practices for Designing a Pragmatic RESTful APIhttp://williamdurand.fr/2012/08/02/rest-apis-with-symfony2-the-right-wayREST APIs with Symfony2: The Right Wayhttps://www.youtube.com/watch?v=Kkby5fG89K0Lukas Kahwe Smith (Symfony Camp)http://welcometothebundle.com/symfony2-rest-api-the-best-2013-waySymfony2 REST API: the best way

https://github.com/liuggio/symfony2-rest-api-the-best-2013-way

The github repository39

Page 40: Service approach for development REST API in Symfony2

Thank you

Questions ?Aleksey Kryvtsov [email protected]

40