SlideShare ist ein Scribd-Unternehmen logo
1 von 68
Downloaden Sie, um offline zu lesen
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 model
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
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
Create
response
/**
* 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 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 Cart
Controller
Service
Model/
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 Cart
Link
Controller
Service
Model/
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 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*}
/**
* @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-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 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 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 learned
Large scale Symfony deployments are not that
common
Lessons learned
Large scale Symfony deployments are not that
common
Lessons learned
Large scale Symfony deployments are not that
common
A lot of modules that larger applications need don’t
exist
Lessons learned
Large 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 learned
Large 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 learned
Large 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 learned
Large 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

Weitere ähnliche Inhalte

Was ist angesagt?

Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web servicesMichelangelo van Dam
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteLeonardo Proietti
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersKacper Gunia
 
Error Reporting in ZF2: form messages, custom error pages, logging
Error Reporting in ZF2: form messages, custom error pages, loggingError Reporting in ZF2: form messages, custom error pages, logging
Error Reporting in ZF2: form messages, custom error pages, loggingSteve Maraspin
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Leonardo Proietti
 
November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2Kacper Gunia
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICKonstantin Kudryashov
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Kacper Gunia
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in actionJace Ju
 
Your code sucks, let's fix it - PHP Master Series 2012
Your code sucks, let's fix it - PHP Master Series 2012Your code sucks, let's fix it - PHP Master Series 2012
Your code sucks, let's fix it - PHP Master Series 2012Rafael Dohms
 
Your code sucks, let's fix it (CakeFest2012)
Your code sucks, let's fix it (CakeFest2012)Your code sucks, let's fix it (CakeFest2012)
Your code sucks, let's fix it (CakeFest2012)Rafael Dohms
 
Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From IusethisMarcus Ramberg
 
PhpSpec 2.0 ilustrated by examples
PhpSpec 2.0 ilustrated by examplesPhpSpec 2.0 ilustrated by examples
PhpSpec 2.0 ilustrated by examplesMarcello Duarte
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful softwareJorn Oomen
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonfRafael Dohms
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix itRafael Dohms
 

Was ist angesagt? (20)

Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web services
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il cliente
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4Developers
 
Error Reporting in ZF2: form messages, custom error pages, logging
Error Reporting in ZF2: form messages, custom error pages, loggingError Reporting in ZF2: form messages, custom error pages, logging
Error Reporting in ZF2: form messages, custom error pages, logging
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5
 
November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DIC
 
Wp query
Wp queryWp query
Wp query
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in action
 
Your code sucks, let's fix it - PHP Master Series 2012
Your code sucks, let's fix it - PHP Master Series 2012Your code sucks, let's fix it - PHP Master Series 2012
Your code sucks, let's fix it - PHP Master Series 2012
 
Crafting [Better] API Clients
Crafting [Better] API ClientsCrafting [Better] API Clients
Crafting [Better] API Clients
 
Your code sucks, let's fix it (CakeFest2012)
Your code sucks, let's fix it (CakeFest2012)Your code sucks, let's fix it (CakeFest2012)
Your code sucks, let's fix it (CakeFest2012)
 
Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From Iusethis
 
PhpSpec 2.0 ilustrated by examples
PhpSpec 2.0 ilustrated by examplesPhpSpec 2.0 ilustrated by examples
PhpSpec 2.0 ilustrated by examples
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
 
Unit testing zend framework apps
Unit testing zend framework appsUnit testing zend framework apps
Unit testing zend framework apps
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix it
 
Mocking Demystified
Mocking DemystifiedMocking Demystified
Mocking Demystified
 

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

Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needKacper Gunia
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your CodeAbbas Ali
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Shinya Ohyanagi
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & RESTHugo Hamon
 
You code sucks, let's fix it
You code sucks, let's fix itYou code sucks, let's fix it
You code sucks, let's fix itRafael Dohms
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConRafael Dohms
 
Top 5 Magento Secure Coding Best Practices
Top 5 Magento Secure Coding Best PracticesTop 5 Magento Secure Coding Best Practices
Top 5 Magento Secure Coding Best PracticesOleksandr Zarichnyi
 
WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015Fernando Daciuk
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hackingJeroen van Dijk
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutesBarang CK
 
Add edit delete in Codeigniter in PHP
Add edit delete in Codeigniter in PHPAdd edit delete in Codeigniter in PHP
Add edit delete in Codeigniter in PHPVineet Kumar Saini
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applicationselliando dias
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hackingJeroen van Dijk
 
WordPress as an application framework
WordPress as an application frameworkWordPress as an application framework
WordPress as an application frameworkDustin Filippini
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 

Ähnlich wie Moving a high traffic ZF1 Enterprise Application to SF2 - Lessons learned (20)

Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your Code
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
You code sucks, let's fix it
You code sucks, let's fix itYou code sucks, let's fix it
You code sucks, let's fix it
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnCon
 
Zero to SOLID
Zero to SOLIDZero to SOLID
Zero to SOLID
 
Top 5 Magento Secure Coding Best Practices
Top 5 Magento Secure Coding Best PracticesTop 5 Magento Secure Coding Best Practices
Top 5 Magento Secure Coding Best Practices
 
WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
 
Add edit delete in Codeigniter in PHP
Add edit delete in Codeigniter in PHPAdd edit delete in Codeigniter in PHP
Add edit delete in Codeigniter in PHP
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
 
WordPress as an application framework
WordPress as an application frameworkWordPress as an application framework
WordPress as an application framework
 
Bacbkone js
Bacbkone jsBacbkone js
Bacbkone js
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 

Kürzlich hochgeladen

Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024The Digital Insurer
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilV3cube
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...gurkirankumar98700
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 

Kürzlich hochgeladen (20)

Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 

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

  • 1. Moving a ZF1 Application to SF2 - Lessons learned Baldur Rensch
  • 2. Agenda About me What is Hautelook HAL Before Switching to Symfony Lessons Learned
  • 3. About me architecting at Hautelook contributing on GitHub: @baldurrensch opinionating on twitter: @brensch
  • 4. What is ? Member only shopping site Private, limited- time sale events daily email invitation at 8am
  • 5. 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]
  • 6. [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
  • 8. Our stack API Website Admin Mobile Clients (iOS / Android) HAL+JSON HAL+JSON HAL+JSON [13, 14]
  • 11. 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
  • 12. 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; }
  • 13. 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; }
  • 14. 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
  • 15. 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); }
  • 16. 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 model 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); }
  • 17. 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); }
  • 18. 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
  • 19. 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
  • 20. 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!
  • 21. 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.
  • 22. 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.”
  • 23. [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
  • 25. 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.
  • 26. Bundles we use Friends of Symphony: RestBundle Nelmio: ApiDocBundle, SolariumBundle JMS: SerializerBundle Football Social Club: HateoasBundle Hautelook: GearmanBundle
  • 27. /** * 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
  • 28. 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; }
  • 29. 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; }
  • 30. 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; }
  • 31. Controller Service Model/ View Create response /** * 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; }
  • 32. 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
  • 33. 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; }
  • 34. 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; }
  • 35. 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 Cart Controller Service Model/ View
  • 36. 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 Cart Link Controller Service Model/ View
  • 37. 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 Cart Embedded Controller Service Model/ View
  • 38. 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
  • 39. 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
  • 40. 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
  • 41. 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}"
  • 42. 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-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
  • 45. HTML form - makes testing easy Documentation with NelmioAPIDocBundle
  • 47. Measuring performance with declarative programming Why its difficult [8, 9, 10]
  • 48. Measuring performance with declarative programming 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 Cart Why its difficult [8, 9, 10]
  • 49. Measuring performance with declarative programming Why its difficult XHProf to the rescue [8, 9, 10]
  • 50. Measuring performance with declarative programming Why its difficult XHProf to the rescue Hierarchical function-level profiler [8, 9, 10]
  • 51. Measuring performance with declarative programming Why its difficult XHProf to the rescue Hierarchical function-level profiler PHP Extension [8, 9, 10]
  • 52. Measuring performance with declarative programming Why its difficult XHProf to the rescue Hierarchical function-level profiler PHP Extension Written by Facebook [8, 9, 10]
  • 53. 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]
  • 54. 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]
  • 55. 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]
  • 56.
  • 58. How often was a function called, how long did it take?
  • 59. WTF?
  • 60. Lessons learned Large scale Symfony deployments are not that common
  • 61. Lessons learned Large scale Symfony deployments are not that common
  • 62. Lessons learned Large scale Symfony deployments are not that common A lot of modules that larger applications need don’t exist
  • 63. Lessons learned Large 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
  • 64. Lessons learned Large 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]
  • 65. Lessons learned Large 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
  • 66. Lessons learned Large 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
  • 67. Questions? Let’s get in touch for feedback, questions, discussion Leave feedback at: https://joind.in/8676 Connect on Twitter or Github.
  • 68. 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