SlideShare una empresa de Scribd logo
1 de 68
Descargar para leer sin conexión
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

Más contenido relacionado

La actualidad más candente

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
 

La actualidad más candente (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
 

Similar a 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
 

Similar a 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
 

Último

AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAndrey Devyatkin
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...apidays
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Zilliz
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...apidays
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUKSpring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUKJago de Vreede
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native ApplicationsWSO2
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsNanddeep Nachan
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024The Digital Insurer
 
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
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWERMadyBayot
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Cyberprint. Dark Pink Apt Group [EN].pdf
Cyberprint. Dark Pink Apt Group [EN].pdfCyberprint. Dark Pink Apt Group [EN].pdf
Cyberprint. Dark Pink Apt Group [EN].pdfOverkill Security
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesrafiqahmad00786416
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDropbox
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusZilliz
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024The Digital Insurer
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodJuan lago vázquez
 

Último (20)

AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUKSpring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
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
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Cyberprint. Dark Pink Apt Group [EN].pdf
Cyberprint. Dark Pink Apt Group [EN].pdfCyberprint. Dark Pink Apt Group [EN].pdf
Cyberprint. Dark Pink Apt Group [EN].pdf
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 

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