Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
-
Upload
baldur-rensch -
Category
Technology
-
view
104 -
download
0
description
Transcript of Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned
Moving a ZF1 Application to SF2 -
Lessons learned
Baldur Rensch
Agenda
About me
What is Hautelook
HAL
Before
Switching to Symfony
Lessons Learned
About me
architecting at Hautelook
contributing on GitHub: @baldurrensch
opinionating on twitter: @brensch
What is ?Member only shopping site
Private, limited-time sale events
daily email invitation at 8am
Some stats
Alexa traffic rank for US: 847
More than 12 million members (on average 20k new members per day)
Up to 200 orders per minute
Massive traffic spikes (remember, 8am)
[1]
[1]
Some stats
Alexa traffic rank for US: 847
More than 12 million members (on average 20k new members per day)
Up to 200 orders per minute
Massive traffic spikes (remember, 8am) daily
Our Technologies
Our stack
API
Website Admin Mobile Clients (iOS / Android)
HAL+JSON HAL+JSON HAL+JSON
[13, 14]
HAL
[3]
Zend for thee!
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; }
View
Controller
Service
Model
View
Controller
Service
Model
Call the service
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; }
View
Controller
Service
Model
Prepare for 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; }
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);} View
Controller
Service
Model
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; }
View
Controller
Service
Model
Input Validation
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);}
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; }
View
Controller
Service
Model
Call 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; }
View
Controller
Service
Model
Prepare for 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);}
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);}
<?php
class V4_Model_CartItems{ public function itemsForMember($member_id) { $db = Zend_Registry::get('db');
$q = <<<EOT SELECT cart_id, cart_items.event_id, cart_items.sku, quantity, cart_item_status, expires_at, (...)EOT;
$result = $db->fetchAll($q, $member_id);
return $result; }}
View
Controller
Service
Model
Run some SQL
View
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);}
<?php
class V4_Model_CartItems{ public function itemsForMember($member_id) { $db = Zend_Registry::get('db');
$q = <<<EOT SELECT 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());}
Controller
Service
Model
View
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);}
<?php
class V4_Model_CartItems{ public function itemsForMember($member_id) { $db = Zend_Registry::get('db');
$q = <<<EOT SELECT 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());}
Controller
Service
Model
Convert array results to HAL+Json,
yuck!
Issues
This is fine when you have 5 end points and simple responses.
Lots of boiler plate code
Zend Framework 1 did not scale very well. We constantly had to overwrite parts of the framework.
Moving from Imperative to Declarative Programming
[4,5]
Imperative Declarative
“In computer science, imperative programming is a programming paradigm that
describes computation in terms of statements that change a program state.”
“In computer science, declarative programming is a programming paradigm that
expresses the logic of a computation without
describing its control flow.”
[4,5]
Imperative Declarative
“In computer science, imperative programming is a programming paradigm that
describes computation in terms of statements that change a program state.”
“In computer science, declarative programming is a programming paradigm that
expresses the logic of a computation without
describing its control flow.”
Moving from Imperative to Declarative Programming
Let’s use Symfony instead, shall we?
[2]
Advantages:
Symfony allows for way more declarative programming which allows us to write less code.
Allows us to extend way easier. And it’s actually fun.
Community is great.
Bundles we use
Friends of Symphony: RestBundle
Nelmio: ApiDocBundle, SolariumBundle
JMS: SerializerBundle
Football Social Club: HateoasBundle
Hautelook: GearmanBundle
/** * This function returns a member's cart * * @Route("/members/{memberId}/cart", requirements = { "memberId" = "\d+" } ) * @Method({"GET"}) * * @ApiDoc( * resource=true, * description="Retrieve a member's 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;}
Controller
Service
Model/View
Controller
Service
Model/View
Routing, Input Validation
/** * This function returns a member's cart * * @Route("/members/{memberId}/cart", requirements = { "memberId" = "\d+" } ) * @Method({"GET"}) * * @ApiDoc( * resource=true, * description="Retrieve a member's 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;}
Controller
Service
Model/View
Documentation
/** * This function returns a member's cart * * @Route("/members/{memberId}/cart", requirements = { "memberId" = "\d+" } ) * @Method({"GET"}) * * @ApiDoc( * resource=true, * description="Retrieve a member's 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;}
Controller
Service
Model/View
Call Service to get data
/** * This function returns a member's cart * * @Route("/members/{memberId}/cart", requirements = { "memberId" = "\d+" } ) * @Method({"GET"}) * * @ApiDoc( * resource=true, * description="Retrieve a member's 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;}
Controller
Service
Model/View
Createresponse
/** * This function returns a member's cart * * @Route("/members/{memberId}/cart", requirements = { "memberId" = "\d+" } ) * @Method({"GET"}) * * @ApiDoc( * resource=true, * description="Retrieve a member's 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;}
Controller
Service
Model/View
Controller
Service
Model/View
Get entities from database
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;}
Controller
Service
Model/ViewConvert to view
model
Get entities from database
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;}
use JMS\Serializer\Annotation as JMS;use FSC\HateoasBundle\Annotation as Rest;
/** * @author Baldur Rensch <[email protected]> * * @Rest\Relation("self", * href = @Rest\Route("hautelook_api_cart_getcart", * parameters = { "memberId": ".member.memberId" } * ) * ) * @Rest\Relation("http://hautelook.com/rels/cart/item", * href = @Rest\Route("hautelook_api_cart_getcart", * parameters = { "memberId": ".member.memberId" } * ), * embed = @Rest\Content( * property = ".cartItems" * ) * ) */class Cart
Controller
Service
Model/View
use JMS\Serializer\Annotation as JMS;use FSC\HateoasBundle\Annotation as Rest;
/** * @author Baldur Rensch <[email protected]> * * @Rest\Relation("self", * href = @Rest\Route("hautelook_api_cart_getcart", * parameters = { "memberId": ".member.memberId" } * ) * ) * @Rest\Relation("http://hautelook.com/rels/cart/item", * href = @Rest\Route("hautelook_api_cart_getcart", * parameters = { "memberId": ".member.memberId" } * ), * embed = @Rest\Content( * property = ".cartItems" * ) * ) */class Cart
Link
Controller
Service
Model/View
use JMS\Serializer\Annotation as JMS;use FSC\HateoasBundle\Annotation as Rest;
/** * @author Baldur Rensch <[email protected]> * * @Rest\Relation("self", * href = @Rest\Route("hautelook_api_cart_getcart", * parameters = { "memberId": ".member.memberId" } * ) * ) * @Rest\Relation("http://hautelook.com/rels/cart/item", * href = @Rest\Route("hautelook_api_cart_getcart", * parameters = { "memberId": ".member.memberId" } * ), * embed = @Rest\Content( * property = ".cartItems" * ) * ) */class Cart
Embedded
Controller
Service
Model/View
URI Templates are sexy
There 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 possible
1
URI Templates are sexy/demo?{&page}{&sort%5B%5D*}{&filter%5B%5D*}
There is a RFC for it: RFC-6570
There is a bundle for it™: TemplatedURIBundle
[6, 7, 12]1 And are the magic that make Hateoas possible
1
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-6570
There is a bundle for it™: TemplatedURIBundle
[6, 7, 12]1 And are the magic that make Hateoas possible
1
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-6570
There is a bundle for it™: TemplatedURIBundle
It even integrates with the HateoasBundle:
[6, 7, 12]1 And are the magic that make Hateoas possible
1
hautelook_style_image_resizable: pattern: /resizer/{width}x{height}/products/{styleNum}/{size}/{imageId}.jpg defaults: width: "{width}" height: "{height}"
URI Templates are sexy/demo?{&page}{&sort%5B%5D*}{&filter%5B%5D*}
/** * @Rest\Relation("http://hautelook.com/rels/image/resizable", * href = @Rest\Route("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-6570
There is a bundle for it™: TemplatedURIBundle
It even integrates with the HateoasBundle:
[6, 7, 12]1 And are the magic that make Hateoas possible
1
Documentation with NelmioAPIDocBundle
Yeay, RESTful :)
Documentation with NelmioAPIDocBundle
HTML form -makes testing
easy
Documentation with NelmioAPIDocBundle
Documentation with NelmioAPIDocBundle
Measuring performance with declarative programming
Why its difficult
[8, 9, 10]
Measuring performance with declarative programming
use JMS\Serializer\Annotation as JMS;use FSC\HateoasBundle\Annotation as Rest;
/** * @author Baldur Rensch <[email protected]> * * @Rest\Relation("self", * href = @Rest\Route("hautelook_api_cart_getcart", * parameters = { "memberId": ".member.memberId" } * ) * ) * @Rest\Relation("http://hautelook.com/rels/cart/item", * href = @Rest\Route("hautelook_api_cart_getcart", * parameters = { "memberId": ".member.memberId" } * ), * embed = @Rest\Content( * property = ".cartItems" * ) * ) */class Cart
Why its difficult
[8, 9, 10]
Measuring performance with declarative programming
Why its difficult
XHProf to the rescue
[8, 9, 10]
Measuring performance with declarative programming
Why its difficult
XHProf to the rescue
Hierarchical function-level profiler
[8, 9, 10]
Measuring performance with declarative programming
Why its difficult
XHProf to the rescue
Hierarchical function-level profiler
PHP Extension
[8, 9, 10]
Measuring performance with declarative programming
Why its difficult
XHProf to the rescue
Hierarchical function-level profiler
PHP Extension
Written by Facebook
[8, 9, 10]
Measuring performance with declarative programming
Why its difficult
XHProf to the rescue
Hierarchical function-level profiler
PHP Extension
Written by Facebook
XHGui on top of XHProf
[8, 9, 10]
Measuring performance with declarative programming
Why its difficult
XHProf to the rescue
Hierarchical function-level profiler
PHP Extension
Written by Facebook
XHGui on top of XHProf
Uses a shared database backend
[8, 9, 10]
Measuring performance with declarative programming
Why its difficult
XHProf to the rescue
Hierarchical function-level profiler
PHP Extension
Written by Facebook
XHGui on top of XHProf
Uses a shared database backend
There is a bundle for that™ as well![8, 9, 10]
Which functions are most
expensive?
How often was a function called, how long did it
take?
WTF?
Lessons learnedLarge scale Symfony deployments are not that common
Lessons learnedLarge scale Symfony deployments are not that common
Lessons learnedLarge scale Symfony deployments are not that common
A lot of modules that larger applications need don’t exist
Lessons learnedLarge scale Symfony deployments are not that common
A lot of modules that larger applications need don’t exist
Example: Session storage in multiple storage layers such as: Memcached and Database
Lessons learnedLarge scale Symfony deployments are not that common
A lot of modules that larger applications need don’t exist
Example: Session storage in multiple storage layers such as: Memcached and Database
There is a bundle for that™ now as well:
SessionStorageHandlerChainBundle
[11]
Lessons learnedLarge scale Symfony deployments are not that common
A lot of modules that larger applications need don’t exist
Need more documentation / community around enterprise level Symfony development
Lessons learnedLarge scale Symfony deployments are not that common
A lot of modules that larger applications need don’t exist
Need more documentation / community around enterprise level Symfony development
Our Developers love developing in Symfony
Questions?
Let’s get in touch for feedback, questions, discussion
Leave feedback at: https://joind.in/8676
Connect 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