Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Upcoming SlideShare
Loading in...5
×
 

Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned

on

  • 2,396 views

Hautelook is a large ecommerce application that is currently running a Zend Framework 1 backend. The next iteration of its API (used by desktop, mobile, as well as iPhone and Android native ...

Hautelook is a large ecommerce application that is currently running a Zend Framework 1 backend. The next iteration of its API (used by desktop, mobile, as well as iPhone and Android native applications) is done with Symfony 2. This API is following the principles for hypermedia APIs. To that end, Hal+Json is the media-type we chose, and we implemented most of it using the FSC HateoasBundle. Another critical piece of Hal+Json APIs is documentation. To this end we have used NelmioApiDocBundle to automatically generate documentation for the API endpoints. The other critical piece of any application is performance for which we use XHProf with XHGui. In my talk I want to touch on all those aspects, show some of the lessons learned, how we solved some of the problems, and what is still unsolved.

Statistics

Views

Total Views
2,396
Views on SlideShare
2,134
Embed Views
262

Actions

Likes
7
Downloads
11
Comments
0

6 Embeds 262

https://twitter.com 117
http://lanyrd.com 102
http://librosweb.es 40
http://tweetedtimes.com 1
http://lw.192.168.2.13.xip.io 1
http://www.google.fr 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned Presentation Transcript

  • Moving a ZF1 Application to SF2-Lessons learnedBaldur Rensch
  • AgendaAbout meWhat is HautelookHALBeforeSwitching to SymfonyLessons Learned
  • About mearchitecting at Hautelookcontributing on GitHub: @baldurrenschopinionating on twitter: @brensch View slide
  • What is ?Member onlyshopping sitePrivate, limited-time sale eventsdaily emailinvitation at 8am View slide
  • Some statsAlexa traffic rank for US: 847More than 12 million members (on average 20k newmembers per day)Up to 200 orders per minuteMassive traffic spikes (remember, 8am)[1]
  • [1]Some statsAlexa traffic rank for US: 847More than 12 million members (on average 20k newmembers per day)Up to 200 orders per minuteMassive traffic spikes (remember, 8am) daily
  • Our Technologies
  • Our stackAPIWebsite AdminMobile Clients(iOS / Android)HAL+JSON HAL+JSON HAL+JSON[13, 14]
  • HAL[3]
  • Zend forthee!
  • class V4_Controller_Cart extends Halo_Rest_ViewController{public function get(){$service = new V4_Service_Cart;! !! ! $params = $this->getRequest()->getParams();$response = $service->resource($params);! !! ! if ($response->getSuccess()) {$this->getResponse()->setHttpResponseCode(200);} else {$this->getResponse()->setHttpResponseCode(400);}$this->view->member_id = (int) $params[member_id];$this->service_response = $response;}ViewControllerServiceModel
  • ViewControllerServiceModelCall the serviceclass V4_Controller_Cart extends Halo_Rest_ViewController{public function get(){$service = new V4_Service_Cart;! !! ! $params = $this->getRequest()->getParams();$response = $service->resource($params);! !! ! if ($response->getSuccess()) {$this->getResponse()->setHttpResponseCode(200);} else {$this->getResponse()->setHttpResponseCode(400);}$this->view->member_id = (int) $params[member_id];$this->service_response = $response;}
  • ViewControllerServiceModelPrepare for responseclass V4_Controller_Cart extends Halo_Rest_ViewController{public function get(){$service = new V4_Service_Cart;! !! ! $params = $this->getRequest()->getParams();$response = $service->resource($params);! !! ! if ($response->getSuccess()) {$this->getResponse()->setHttpResponseCode(200);} else {$this->getResponse()->setHttpResponseCode(400);}$this->view->member_id = (int) $params[member_id];$this->service_response = $response;}
  • class V4_Controller_Cart extends Halo_Rest_ViewController{public function get(){$service = new V4_Service_Cart;! !! ! $params = $this->getRequest()->getParams();$response = $service->resource($params);! !! ! if ($response->getSuccess()) {$this->getResponse()->setHttpResponseCode(200);} else {$this->getResponse()->setHttpResponseCode(400);}$this->view->member_id = (int) $params[member_id];$this->service_response = $response;}public function resource(array $data){$this->checkMemberId($data[member_id]);$input = new Zend_Filter_Input(array(* => StringTrim),array(member_id => array(Int,presence => required),),$data);if (!$input->isValid()) {return $this->response(false, $input);}$cart_items_model = $this->getComponent(V4_Model_CartItems);$items = $cart_items_model->itemsForMember($input->member_id);foreach ($items as $k => $item) {$items[$k][status] = $this->statusMap($item[cart_item_status]);$styles_service = $this->getComponent(V4_Service_Styles);$style = $styles_service->resource(array(event_id => $item[event_id],style_num => $item[style_num],));$items[$k][style] = $style->getData();}$result = array(gift_checkout => $gift_checkout,items => $items,);return $this->response(true, $result);}ViewControllerServiceModel
  • class V4_Controller_Cart extends Halo_Rest_ViewController{public function get(){$service = new V4_Service_Cart;! !! ! $params = $this->getRequest()->getParams();$response = $service->resource($params);! !! ! if ($response->getSuccess()) {$this->getResponse()->setHttpResponseCode(200);} else {$this->getResponse()->setHttpResponseCode(400);}$this->view->member_id = (int) $params[member_id];$this->service_response = $response;}ViewControllerServiceModelInput Validationpublic function resource(array $data){$this->checkMemberId($data[member_id]);$input = new Zend_Filter_Input(array(* => StringTrim),array(member_id => array(Int,presence => required),),$data);if (!$input->isValid()) {return $this->response(false, $input);}$cart_items_model = $this->getComponent(V4_Model_CartItems);$items = $cart_items_model->itemsForMember($input->member_id);foreach ($items as $k => $item) {$items[$k][status] = $this->statusMap($item[cart_item_status]);$styles_service = $this->getComponent(V4_Service_Styles);$style = $styles_service->resource(array(event_id => $item[event_id],style_num => $item[style_num],));$items[$k][style] = $style->getData();}$result = array(gift_checkout => $gift_checkout,items => $items,);return $this->response(true, $result);}
  • class V4_Controller_Cart extends Halo_Rest_ViewController{public function get(){$service = new V4_Service_Cart;! !! ! $params = $this->getRequest()->getParams();$response = $service->resource($params);! !! ! if ($response->getSuccess()) {$this->getResponse()->setHttpResponseCode(200);} else {$this->getResponse()->setHttpResponseCode(400);}$this->view->member_id = (int) $params[member_id];$this->service_response = $response;}ViewControllerServiceModelCall modelpublic function resource(array $data){$this->checkMemberId($data[member_id]);$input = new Zend_Filter_Input(array(* => StringTrim),array(member_id => array(Int,presence => required),),$data);if (!$input->isValid()) {return $this->response(false, $input);}$cart_items_model = $this->getComponent(V4_Model_CartItems);$items = $cart_items_model->itemsForMember($input->member_id);foreach ($items as $k => $item) {$items[$k][status] = $this->statusMap($item[cart_item_status]);$styles_service = $this->getComponent(V4_Service_Styles);$style = $styles_service->resource(array(event_id => $item[event_id],style_num => $item[style_num],));$items[$k][style] = $style->getData();}$result = array(gift_checkout => $gift_checkout,items => $items,);return $this->response(true, $result);}
  • class V4_Controller_Cart extends Halo_Rest_ViewController{public function get(){$service = new V4_Service_Cart;! !! ! $params = $this->getRequest()->getParams();$response = $service->resource($params);! !! ! if ($response->getSuccess()) {$this->getResponse()->setHttpResponseCode(200);} else {$this->getResponse()->setHttpResponseCode(400);}$this->view->member_id = (int) $params[member_id];$this->service_response = $response;}ViewControllerServiceModelPrepare for responsepublic function resource(array $data){$this->checkMemberId($data[member_id]);$input = new Zend_Filter_Input(array(* => StringTrim),array(member_id => array(Int,presence => required),),$data);if (!$input->isValid()) {return $this->response(false, $input);}$cart_items_model = $this->getComponent(V4_Model_CartItems);$items = $cart_items_model->itemsForMember($input->member_id);foreach ($items as $k => $item) {$items[$k][status] = $this->statusMap($item[cart_item_status]);$styles_service = $this->getComponent(V4_Service_Styles);$style = $styles_service->resource(array(event_id => $item[event_id],style_num => $item[style_num],));$items[$k][style] = $style->getData();}$result = array(gift_checkout => $gift_checkout,items => $items,);return $this->response(true, $result);}
  • class V4_Controller_Cart extends Halo_Rest_ViewController{public function get(){$service = new V4_Service_Cart;! !! ! $params = $this->getRequest()->getParams();$response = $service->resource($params);! !! ! if ($response->getSuccess()) {$this->getResponse()->setHttpResponseCode(200);} else {$this->getResponse()->setHttpResponseCode(400);}$this->view->member_id = (int) $params[member_id];$this->service_response = $response;}public function resource(array $data){$this->checkMemberId($data[member_id]);$input = new Zend_Filter_Input(array(* => StringTrim),array(member_id => array(Int,presence => required),),$data);if (!$input->isValid()) {return $this->response(false, $input);}$cart_items_model = $this->getComponent(V4_Model_CartItems);$items = $cart_items_model->itemsForMember($input->member_id);foreach ($items as $k => $item) {$items[$k][status] = $this->statusMap($item[cart_item_status]);$styles_service = $this->getComponent(V4_Service_Styles);$style = $styles_service->resource(array(event_id => $item[event_id],style_num => $item[style_num],));$items[$k][style] = $style->getData();}$result = array(gift_checkout => $gift_checkout,items => $items,);return $this->response(true, $result);}<?phpclass V4_Model_CartItems{public function itemsForMember($member_id){$db = Zend_Registry::get(db);$q = <<<EOTSELECT cart_id, cart_items.event_id, cart_items.sku, quantity,cart_item_status, expires_at, (...)EOT;$result = $db->fetchAll($q, $member_id);return $result;}}ViewControllerServiceModelRun some SQL
  • Viewclass V4_Controller_Cart extends Halo_Rest_ViewController{public function get(){$service = new V4_Service_Cart;! !! ! $params = $this->getRequest()->getParams();$response = $service->resource($params);! !! ! if ($response->getSuccess()) {$this->getResponse()->setHttpResponseCode(200);} else {$this->getResponse()->setHttpResponseCode(400);}$this->view->member_id = (int) $params[member_id];$this->service_response = $response;}public function resource(array $data){$this->checkMemberId($data[member_id]);$input = new Zend_Filter_Input(array(* => StringTrim),array(member_id => array(Int,presence => required),),$data);if (!$input->isValid()) {return $this->response(false, $input);}$cart_items_model = $this->getComponent(V4_Model_CartItems);$items = $cart_items_model->itemsForMember($input->member_id);foreach ($items as $k => $item) {$items[$k][status] = $this->statusMap($item[cart_item_status]);$styles_service = $this->getComponent(V4_Service_Styles);$style = $styles_service->resource(array(event_id => $item[event_id],style_num => $item[style_num],));$items[$k][style] = $style->getData();}$result = array(gift_checkout => $gift_checkout,items => $items,);return $this->response(true, $result);}<?phpclass V4_Model_CartItems{public function itemsForMember($member_id){$db = Zend_Registry::get(db);$q = <<<EOTSELECT cart_id, cart_items.event_id, cart_items.sku, quantity,cart_item_status, expires_at, (...)EOT;$result = $db->fetchAll($q, $member_id);return $result;}}protected function modifyData(Halo_Response $service_response){$member_id = $this->member_id;$data = $service_response->getData();$items = $data[items];unset($data[items]);$cart = new Hal_Resource("/v4/members/{$member_id}/cart", $data);$cart->setLink(new Hal_Link("/v4/members/{$member_id}/checkout",http://hautelook.com/rels/checkout));foreach ($items as $item) {$event_id = $item[sku][event_id];$color = $item[sku][color];$expires = new DateTime($item[expires_at]);$cart_data = array(quantity => (int) $item[quantity],(...));$sku_data = array(event_id => $event_id,(...));if (isset($item[style][images][strtolower($color)])) {$images = $item[style][images][strtolower($color)][angles];}$image_link = $images[0][zoom];$style = new Hal_Resource("/v4/events/{$event_id}/styles/{$item[style_num]}", $style_data);$r = new Hal_Resource("/v4/members/{$member_id}/cart/{$item[cart_id]}", $cart_data);$style->setEmbedded(http://hautelook.com/rels/sku, $sku);$style->setLink(new Hal_Link($image_link, http://hautelook.com/rels/images/style/zoom));$r->setEmbedded(http://hautelook.com/rels/styles, $style);$cart->setEmbedded(http://hautelook.com/rels/cart-items, $r);}$service_response->success($cart->toArray());}ControllerServiceModel
  • Viewclass V4_Controller_Cart extends Halo_Rest_ViewController{public function get(){$service = new V4_Service_Cart;! !! ! $params = $this->getRequest()->getParams();$response = $service->resource($params);! !! ! if ($response->getSuccess()) {$this->getResponse()->setHttpResponseCode(200);} else {$this->getResponse()->setHttpResponseCode(400);}$this->view->member_id = (int) $params[member_id];$this->service_response = $response;}public function resource(array $data){$this->checkMemberId($data[member_id]);$input = new Zend_Filter_Input(array(* => StringTrim),array(member_id => array(Int,presence => required),),$data);if (!$input->isValid()) {return $this->response(false, $input);}$cart_items_model = $this->getComponent(V4_Model_CartItems);$items = $cart_items_model->itemsForMember($input->member_id);foreach ($items as $k => $item) {$items[$k][status] = $this->statusMap($item[cart_item_status]);$styles_service = $this->getComponent(V4_Service_Styles);$style = $styles_service->resource(array(event_id => $item[event_id],style_num => $item[style_num],));$items[$k][style] = $style->getData();}$result = array(gift_checkout => $gift_checkout,items => $items,);return $this->response(true, $result);}<?phpclass V4_Model_CartItems{public function itemsForMember($member_id){$db = Zend_Registry::get(db);$q = <<<EOTSELECT cart_id, cart_items.event_id, cart_items.sku, quantity,cart_item_status, expires_at, (...)EOT;$result = $db->fetchAll($q, $member_id);return $result;}}protected function modifyData(Halo_Response $service_response){$member_id = $this->member_id;$data = $service_response->getData();$items = $data[items];unset($data[items]);$cart = new Hal_Resource("/v4/members/{$member_id}/cart", $data);$cart->setLink(new Hal_Link("/v4/members/{$member_id}/checkout",http://hautelook.com/rels/checkout));foreach ($items as $item) {$event_id = $item[sku][event_id];$color = $item[sku][color];$expires = new DateTime($item[expires_at]);$cart_data = array(quantity => (int) $item[quantity],(...));$sku_data = array(event_id => $event_id,(...));if (isset($item[style][images][strtolower($color)])) {$images = $item[style][images][strtolower($color)][angles];}$image_link = $images[0][zoom];$style = new Hal_Resource("/v4/events/{$event_id}/styles/{$item[style_num]}", $style_data);$r = new Hal_Resource("/v4/members/{$member_id}/cart/{$item[cart_id]}", $cart_data);$style->setEmbedded(http://hautelook.com/rels/sku, $sku);$style->setLink(new Hal_Link($image_link, http://hautelook.com/rels/images/style/zoom));$r->setEmbedded(http://hautelook.com/rels/styles, $style);$cart->setEmbedded(http://hautelook.com/rels/cart-items, $r);}$service_response->success($cart->toArray());}ControllerServiceModelConvert array results to HAL+Json,yuck!
  • IssuesThis is fine when you have 5 end points and simpleresponses.Lots of boiler plate codeZend Framework 1 did not scale very well. Weconstantly had to overwrite parts of the framework.
  • Moving from Imperative to DeclarativeProgramming[4,5]Imperative Declarative“In computer science,imperative programming is aprogramming paradigm thatdescribes computation interms of statements thatchange a program state.”“In computer science,declarative programming is aprogramming paradigm thatexpresses the logic of acomputation withoutdescribing its control flow.”
  • [4,5]Imperative Declarative“In computer science,imperative programming is aprogramming paradigm thatdescribes computation interms of statements thatchange a program state.”“In computer science,declarative programming is aprogramming paradigm thatexpresses the logic of acomputation withoutdescribing its control flow.”Moving from Imperative to DeclarativeProgramming
  • Let’s use Symfonyinstead, shall we?[2]
  • Advantages:Symfony allows for way more declarative programmingwhich allows us to write less code.Allows us to extend way easier. And it’s actually fun.Community is great.
  • Bundles we useFriends of Symphony: RestBundleNelmio: ApiDocBundle, SolariumBundleJMS: SerializerBundleFootball Social Club: HateoasBundleHautelook: GearmanBundle
  • /*** This function returns a members cart** @Route("/members/{memberId}/cart", requirements = { "memberId" = "d+" } )* @Method({"GET"})** @ApiDoc(* resource=true,* description="Retrieve a members cart",* statusCodes={* 200="Returned when successful",* 400="Returned when the request is not well-formed",* 403="Returned when the user is not authorized or not owner or have no admin access",* 404="Returned when the member is not found"* }* )** @param int $memberId** @return Response*/public function getCartAction($memberId){$member = $this->get(hautelook.model.member)->getMemberAndValidate($memberId);$cart = $this->get(hautelook.model.cart)->getCart($member);$response = $this->get(serializer)->serialize($cart, json);$response = new Response($response);$response->headers->set(Content-Type, application/json);$response->setETag(md5($response->getContent()));return $response;}ControllerServiceModel/View
  • ControllerServiceModel/ViewRouting, InputValidation/*** This function returns a members cart** @Route("/members/{memberId}/cart", requirements = { "memberId" = "d+" } )* @Method({"GET"})** @ApiDoc(* resource=true,* description="Retrieve a members cart",* statusCodes={* 200="Returned when successful",* 400="Returned when the request is not well-formed",* 403="Returned when the user is not authorized or not owner or have no admin access",* 404="Returned when the member is not found"* }* )** @param int $memberId** @return Response*/public function getCartAction($memberId){$member = $this->get(hautelook.model.member)->getMemberAndValidate($memberId);$cart = $this->get(hautelook.model.cart)->getCart($member);$response = $this->get(serializer)->serialize($cart, json);$response = new Response($response);$response->headers->set(Content-Type, application/hal+json);$response->setETag(md5($response->getContent()));return $response;}
  • ControllerServiceModel/ViewDocumentation/*** This function returns a members cart** @Route("/members/{memberId}/cart", requirements = { "memberId" = "d+" } )* @Method({"GET"})** @ApiDoc(* resource=true,* description="Retrieve a members cart",* statusCodes={* 200="Returned when successful",* 400="Returned when the request is not well-formed",* 403="Returned when the user is not authorized or not owner or have no admin access",* 404="Returned when the member is not found"* }* )** @param int $memberId** @return Response*/public function getCartAction($memberId){$member = $this->get(hautelook.model.member)->getMemberAndValidate($memberId);$cart = $this->get(hautelook.model.cart)->getCart($member);$response = $this->get(serializer)->serialize($cart, json);$response = new Response($response);$response->headers->set(Content-Type, application/hal+json);$response->setETag(md5($response->getContent()));return $response;}
  • ControllerServiceModel/ViewCall Service to get data/*** This function returns a members cart** @Route("/members/{memberId}/cart", requirements = { "memberId" = "d+" } )* @Method({"GET"})** @ApiDoc(* resource=true,* description="Retrieve a members cart",* statusCodes={* 200="Returned when successful",* 400="Returned when the request is not well-formed",* 403="Returned when the user is not authorized or not owner or have no admin access",* 404="Returned when the member is not found"* }* )** @param int $memberId** @return Response*/public function getCartAction($memberId){$member = $this->get(hautelook.model.member)->getMemberAndValidate($memberId);$cart = $this->get(hautelook.model.cart)->getCart($member);$response = $this->get(serializer)->serialize($cart, json);$response = new Response($response);$response->headers->set(Content-Type, application/hal+json);$response->setETag(md5($response->getContent()));return $response;}
  • ControllerServiceModel/ViewCreateresponse/*** This function returns a members cart** @Route("/members/{memberId}/cart", requirements = { "memberId" = "d+" } )* @Method({"GET"})** @ApiDoc(* resource=true,* description="Retrieve a members cart",* statusCodes={* 200="Returned when successful",* 400="Returned when the request is not well-formed",* 403="Returned when the user is not authorized or not owner or have no admin access",* 404="Returned when the member is not found"* }* )** @param int $memberId** @return Response*/public function getCartAction($memberId){$member = $this->get(hautelook.model.member)->getMemberAndValidate($memberId);$cart = $this->get(hautelook.model.cart)->getCart($member);$response = $this->get(serializer)->serialize($cart, json);$response = new Response($response);$response->headers->set(Content-Type, application/hal+json);$response->setETag(md5($response->getContent()));return $response;}
  • public function getCart(Members $member){$cartItems = $this->doctrine->getRepository("HautelookApiBundle:CartItems")->getCartItems($member->getMemberId());$cartItemArray = array();foreach ($cartItems as $cartItem) {$cartItemArray []= new CartItem($cartItem);}$cart = new CartModel($member, $cartItemArray);return $cart;}ControllerServiceModel/View
  • ControllerServiceModel/ViewGet entities from databasepublic function getCart(Members $member){$cartItems = $this->doctrine->getRepository("HautelookApiBundle:CartItems")->getCartItems($member->getMemberId());$cartItemArray = array();foreach ($cartItems as $cartItem) {$cartItemArray []= new CartItem($cartItem);}$cart = new CartModel($member, $cartItemArray);return $cart;}
  • ControllerServiceModel/ViewConvert to viewmodelGet entities from databasepublic function getCart(Members $member){$cartItems = $this->doctrine->getRepository("HautelookApiBundle:CartItems")->getCartItems($member->getMemberId());$cartItemArray = array();foreach ($cartItems as $cartItem) {$cartItemArray []= new CartItem($cartItem);}$cart = new CartModel($member, $cartItemArray);return $cart;}
  • use JMSSerializerAnnotation as JMS;use FSCHateoasBundleAnnotation as Rest;/*** @author Baldur Rensch <baldur.rensch@hautelook.com>** @RestRelation("self",* href = @RestRoute("hautelook_api_cart_getcart",* parameters = { "memberId": ".member.memberId" }* )* )* @RestRelation("http://hautelook.com/rels/cart/item",* href = @RestRoute("hautelook_api_cart_getcart",* parameters = { "memberId": ".member.memberId" }* ),* embed = @RestContent(* property = ".cartItems"* )* )*/class CartControllerServiceModel/View
  • use JMSSerializerAnnotation as JMS;use FSCHateoasBundleAnnotation as Rest;/*** @author Baldur Rensch <baldur.rensch@hautelook.com>** @RestRelation("self",* href = @RestRoute("hautelook_api_cart_getcart",* parameters = { "memberId": ".member.memberId" }* )* )* @RestRelation("http://hautelook.com/rels/cart/item",* href = @RestRoute("hautelook_api_cart_getcart",* parameters = { "memberId": ".member.memberId" }* ),* embed = @RestContent(* property = ".cartItems"* )* )*/class CartLinkControllerServiceModel/View
  • use JMSSerializerAnnotation as JMS;use FSCHateoasBundleAnnotation as Rest;/*** @author Baldur Rensch <baldur.rensch@hautelook.com>** @RestRelation("self",* href = @RestRoute("hautelook_api_cart_getcart",* parameters = { "memberId": ".member.memberId" }* )* )* @RestRelation("http://hautelook.com/rels/cart/item",* href = @RestRoute("hautelook_api_cart_getcart",* parameters = { "memberId": ".member.memberId" }* ),* embed = @RestContent(* property = ".cartItems"* )* )*/class CartEmbeddedControllerServiceModel/View
  • URI Templates are sexyThere is a RFC for it: RFC-6570[6, 7, 12]/demo?{&page}{&sort%5B%5D*}{&filter%5B%5D*}1 And are the magic that make Hateoas possible1
  • URI Templates are sexy/demo?{&page}{&sort%5B%5D*}{&filter%5B%5D*}There is a RFC for it: RFC-6570There is a bundle for it™: TemplatedURIBundle[6, 7, 12]1 And are the magic that make Hateoas possible1
  • URI Templates are sexy/demo?{&page}{&sort%5B%5D*}{&filter%5B%5D*}$templateLink = $this->get(hautelook.router.template)->generate(hautelook_demo_route,array(page => {page},sort => array({sort}),filter => array({filter}),));There is a RFC for it: RFC-6570There is a bundle for it™: TemplatedURIBundle[6, 7, 12]1 And are the magic that make Hateoas possible1
  • URI Templates are sexy/demo?{&page}{&sort%5B%5D*}{&filter%5B%5D*}$templateLink = $this->get(hautelook.router.template)->generate(hautelook_demo_route,array(page => {page},sort => array({sort}),filter => array({filter}),));There is a RFC for it: RFC-6570There is a bundle for it™: TemplatedURIBundleIt even integrates with the HateoasBundle:[6, 7, 12]1 And are the magic that make Hateoas possible1hautelook_style_image_resizable:pattern: /resizer/{width}x{height}/products/{styleNum}/{size}/{imageId}.jpgdefaults:width: "{width}"height: "{height}"
  • URI Templates are sexy/demo?{&page}{&sort%5B%5D*}{&filter%5B%5D*}/*** @RestRelation("http://hautelook.com/rels/image/resizable",* href = @RestRoute("hautelook_style_image_resizable",* parameters = { "styleNum": ".solrDocument.styleNum", "imageId": ".firstImageId" },* options = { "router": "templated" }* ),* excludeIf = { ".firstImageId": null },* attributes = { "templated": true }* )*/$templateLink = $this->get(hautelook.router.template)->generate(hautelook_demo_route,array(page => {page},sort => array({sort}),filter => array({filter}),));There is a RFC for it: RFC-6570There is a bundle for it™: TemplatedURIBundleIt even integrates with the HateoasBundle:[6, 7, 12]1 And are the magic that make Hateoas possible1
  • Documentation with NelmioAPIDocBundle
  • Yeay,RESTful :)Documentation with NelmioAPIDocBundle
  • HTML form -makes testingeasyDocumentation with NelmioAPIDocBundle
  • Documentation with NelmioAPIDocBundle
  • Measuring performance with declarativeprogrammingWhy its difficult[8, 9, 10]
  • Measuring performance with declarativeprogramminguse JMSSerializerAnnotation as JMS;use FSCHateoasBundleAnnotation as Rest;/*** @author Baldur Rensch <baldur.rensch@hautelook.com>** @RestRelation("self",* href = @RestRoute("hautelook_api_cart_getcart",* parameters = { "memberId": ".member.memberId" }* )* )* @RestRelation("http://hautelook.com/rels/cart/item",* href = @RestRoute("hautelook_api_cart_getcart",* parameters = { "memberId": ".member.memberId" }* ),* embed = @RestContent(* property = ".cartItems"* )* )*/class CartWhy its difficult[8, 9, 10]
  • Measuring performance with declarativeprogrammingWhy its difficultXHProf to the rescue[8, 9, 10]
  • Measuring performance with declarativeprogrammingWhy its difficultXHProf to the rescueHierarchical function-level profiler[8, 9, 10]
  • Measuring performance with declarativeprogrammingWhy its difficultXHProf to the rescueHierarchical function-level profilerPHP Extension[8, 9, 10]
  • Measuring performance with declarativeprogrammingWhy its difficultXHProf to the rescueHierarchical function-level profilerPHP ExtensionWritten by Facebook[8, 9, 10]
  • Measuring performance with declarativeprogrammingWhy its difficultXHProf to the rescueHierarchical function-level profilerPHP ExtensionWritten by FacebookXHGui on top of XHProf[8, 9, 10]
  • Measuring performance with declarativeprogrammingWhy its difficultXHProf to the rescueHierarchical function-level profilerPHP ExtensionWritten by FacebookXHGui on top of XHProfUses a shared database backend[8, 9, 10]
  • Measuring performance with declarativeprogrammingWhy its difficultXHProf to the rescueHierarchical function-level profilerPHP ExtensionWritten by FacebookXHGui on top of XHProfUses a shared database backendThere is a bundle for that™ as well![8, 9, 10]
  • Which functionsare mostexpensive?
  • How often was afunction called,how long did ittake?
  • WTF?
  • Lessons learnedLarge scale Symfony deployments are not thatcommon
  • Lessons learnedLarge scale Symfony deployments are not thatcommon
  • Lessons learnedLarge scale Symfony deployments are not thatcommonA lot of modules that larger applications need don’texist
  • Lessons learnedLarge scale Symfony deployments are not thatcommonA lot of modules that larger applications need don’texistExample: Session storage in multiple storage layerssuch as: Memcached and Database
  • Lessons learnedLarge scale Symfony deployments are not thatcommonA lot of modules that larger applications need don’texistExample: Session storage in multiple storage layerssuch as: Memcached and DatabaseThere is a bundle for that™ now as well:SessionStorageHandlerChainBundle[11]
  • Lessons learnedLarge scale Symfony deployments are not thatcommonA lot of modules that larger applications need don’texistNeed more documentation / community aroundenterprise level Symfony development
  • Lessons learnedLarge scale Symfony deployments are not thatcommonA lot of modules that larger applications need don’texistNeed more documentation / community aroundenterprise level Symfony developmentOur Developers love developing in Symfony
  • Questions?Let’s get in touch for feedback, questions, discussionLeave feedback at: https://joind.in/8676Connect on Twitter or Github.
  • Sources[1] http://www.alexa.com/siteinfo/hautelook.com[2] http://fc08.deviantart.net/fs50/f/2009/280/3/c/And_Then_There_Was_Light_by_GTwerks.jpg[3] http://stateless.co/hal_specification.html[4] https://en.wikipedia.org/wiki/Declarative_programming[5] https://en.wikipedia.org/wiki/Imperative_programming[6] https://tools.ietf.org/html/rfc6570[7] https://github.com/hautelook/TemplatedUriBundle[8] https://github.com/facebook/xhprof[9] https://github.com/preinheimer/xhprof[10] https://github.com/jonaswouters/XhprofBundle[11] https://github.com/hautelook/SessionStorageHandlerChainBundle[12] https://github.com/fxa/uritemplate-js[13] https://play.google.com/store/apps/details?id=com.hautelook.mcom&hl=en[14] https://itunes.apple.com/us/app/hautelook/id390783984?mt=8