Your SlideShare is downloading. ×
Symfony2: estudo de caso IngressoPrático
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Symfony2: estudo de caso IngressoPrático

1,327
views

Published on

Referências usadas na apresentação disponíveis em: http://ifgy.co/phpconf2011_2

Referências usadas na apresentação disponíveis em: http://ifgy.co/phpconf2011_2

Published in: Technology

0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,327
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
0
Comments
0
Likes
4
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Symfony2: estudo de caso IngressoPrático
  • 2. “Symfony2 is a reusable set of standalone, decoupled and cohesive PHP components thatsolve common web development problems” Fabien Potencier, What is Symfony2? 2/73
  • 3. “Then, based on thesecomponents, Symfony2 is also a full-stack framework” Fabien Potencier, What is Symfony2? 3/73
  • 4. Eriksen CostaDesenvolvedor PHPContribuidor Symfony2Co-mantenedor DrüpenTrabalha na InfranologyBlog: ifgy.co/ecpE-mail: e@ifgy.co 4/73
  • 5.  infranology.com.br (ifgy.co) Power house web Drupal, Symfony, Solr e Hadoop 5/73
  • 6. Agenda IngressoPrático Por que Symfony2? O projeto Mapa de assentos Conclusões 6/73
  • 7. 7/73
  • 8. 8/73
  • 9. Por que Symfony2? Padrões Simplicidade Testabilidade Dependency Injection 9/73
  • 10. “Dependency Injection (DI) is a design pattern in object-oriented computer programming whosepurpose is to improve testability of, and simplify deployment of components in very large software systems” Wikipedia, Dependency Injection 10/73
  • 11. <?phpclass ListManager{ protected $db; public function __construct() { $this->db = new PDO(mysql:host=localhost;dbname=app, user, password); } public function delete($id) { return $this->db->query( DELETE FROM list WHERE id = . $this->db->quote($id, PDO::PARAM_INT) ); } // ...}$manager = new ListManager(); 11/73
  • 12. <?phpclass ListManager{ protected $db; public function __construct() { $this->db = new PDO(DB_PDO_DSN, DB_PDO_USER, DB_PDO_PASS); } // ...}define(DB_PDO_DSN, mysql:host=localhost;dbname=app);define(DB_PDO_USER, user);define(DB_PDO_PASS, password);$manager = new ListManager(); 12/73
  • 13. <?phpclass ListManager{ protected $db; public function __construct(array $db_params) { $this->db = new PDO( $db_params[dsn], $db_params[user], $db_params[pass] ); } // ...}$manager = new ListManager(array( dsn => mysql:host=localhost;dbname=app, user => user, pass => password,)); 13/73
  • 14. <?phpclass ListManager{ protected $db; public function __construct($db) { $this->db = $db; } // ...}$db = new PDO(mysql:host=localhost;dbname=app, user, password);$manager = new ListManager($db); 14/73
  • 15. “Put simply, a Service is any PHPobject that performs some sort of “global” task” Symfony Documentation, Service Container 15/73
  • 16. “A Service Container (orDependency Injection Container) is simply a PHP object that manages the instantiation of services” Symfony Documentation, Service Container 16/73
  • 17. # app/config/services.ymlservices: my_db: class: PDO arguments: [ mysql:host=localhost;dbname=app, user, password ] my_list_manager: class: FooBarBundleServiceListManager arguments: [ @my_db ] 17/73
  • 18. <?phpnamespace FooBarBundleController;use SymfonyBundleFrameworkBundleControllerController;class ListController extends Controller{ public function deleteAction($id) { $manager = $this->container->get(my_list_manager); $manager->delete($id); // ... }} 18/73
  • 19. 19/73
  • 20. Projeto Gestão Estimativa, equipe Bundles e bibliotecas usadas 20/73
  • 21. 21/73
  • 22. 22/73
  • 23. 23/73
  • 24. 24/73
  • 25. 25/73
  • 26. 300 horas/homem 2 desenvolvedores (part-time) 1 designer (terceirizado) 26/73
  • 27. 2 entregasAgenda do cliente: Pré-cadastro Vendas com cota Vendas sem cota 27/73
  • 28. Bundles, bibliotecas FOSUserBundle LiipFunctionalTestBundle KnpPaginatorBundle (+ ZF2 Paginator) KnpMenuBundle Doctrine ORM 2.1 Doctrine Fixtures Doctrine Migrations Gedmo Doctrine Extensions (Timestampable) 28/73
  • 29. 29/73
  • 30. Mapa de assentos Requisito do projeto Rápido Cores intuitivas 30/73
  • 31. 31/73
  • 32. Extensão Twig 2 funções: seatmap_render, seatmap_render_item 1 token parser: seatmap_theme 1 template base 1 template extra 32/73
  • 33. {# IngressoPratico/VenueBundle/Resources/SeatMap/base_layout.html.twig #}{% block seatmap %}<div class="seat-map"> {% for item in items %} {{ seatmap_render_item(item, variables) }} {% endfor %}</div>{% endblock seatmap %}{% block seatmap_seat %}{% spaceless %} {{ seatmap_render_seattype_css(item, image_dir) }} {% if item.status %} {% set status = ~ item.status %} {% endif %} {% set styles = seatmap_render_item_style(item, image_dir) %} <div class="seat {{ status }}"{% if styles %} {{ styles|raw }}{% endif %}> {{ seatmap_render_item(item.content) }} </div>{% endspaceless %}{% endblock seatmap_seat %}{# ... #} 33/73
  • 34. {# IngressoPratico/VenueBundle/Resources/SeatMap/base_layout.html.twig #}{% block seatmap %}<div class="seat-map"> {% for item in items %} {{ seatmap_render_item(item, variables) }} {% endfor %}</div>{% endblock seatmap %}{% block seatmap_seat %}{% spaceless %} {{ seatmap_render_seattype_css(item, image_dir) }} {% if item.status %} {% set status = ~ item.status %} {% endif %} {% set styles = seatmap_render_item_style(item, image_dir) %} <div class="seat {{ status }}"{% if styles %} {{ styles|raw }}{% endif %}> {{ seatmap_render_item(item.content) }} </div>{% endspaceless %}{% endblock seatmap_seat %}{# ... #} 34/73
  • 35. {# IngressoPratico/VenueBundle/Resources/SeatMap/base_layout.html.twig #}{% block seatmap %}<div class="seat-map"> {% for item in items %} {{ seatmap_render_item(item, variables) }} {% endfor %}</div>{% endblock seatmap %}{% block seatmap_seat %}{% spaceless %} {{ seatmap_render_seattype_css(item, image_dir) }} {% if item.status %} {% set status = ~ item.status %} {% endif %} {% set styles = seatmap_render_item_style(item, image_dir) %} <div class="seat {{ status }}"{% if styles %} {{ styles|raw }}{% endif %}> {{ seatmap_render_item(item.content) }} </div>{% endspaceless %}{% endblock seatmap_seat %}{# ... #} 35/73
  • 36. <?php// IngressoPratico/VenueBundle/Twig/SeatMapExtension.phpnamespace IngressoPraticoVenueBundleTwig;use IngressoPraticoVenueBundleEntitySeatMap;use IngressoPraticoVenueBundleEntityPlottable;class SeatMapExtension extends Twig_Extension{ public function __construct($templates) { $this->templates = is_string($templates) ? array($templates) : $templates; } public function getFunctions() { return array( seatmap_render => new Twig_Function_Method( $this, renderSeatMap, array(is_safe => array(html))), seatmap_render_item => new Twig_Function_Method( $this, renderSeatMapItem, array(is_safe => array(html))), ); } public function getTokenParsers() { return array(new SeatMapThemeTokenParser()); } // ...} 36/73
  • 37. <?php// IngressoPratico/VenueBundle/Twig/SeatMapExtension.phpnamespace IngressoPraticoVenueBundleTwig;use IngressoPraticoVenueBundleEntitySeatMap;use IngressoPraticoVenueBundleEntityPlottable;class SeatMapExtension extends Twig_Extension{ public function __construct($templates) { $this->templates = is_string($templates) ? array($templates) : $templates; } public function getFunctions() { return array( seatmap_render => new Twig_Function_Method( $this, renderSeatMap, array(is_safe => array(html))), seatmap_render_item => new Twig_Function_Method( $this, renderSeatMapItem, array(is_safe => array(html))), ); } public function getTokenParsers() { return array(new SeatMapThemeTokenParser()); } // ...} 37/73
  • 38. <?phpclass SeatMapExtension extends Twig_Extension{ public function renderSeatMap(SeatMap $seatMap, array $variables = array()) { return $this->render(seatmap, array( items => $seatMap->getItems(), variables => $variables, )); } public function renderSeatMapItem(Plottable $item, array $variables = array()) { $variables = array_merge(array(item => $item), $variables); return $this->render($item->getTypeName(), $variables); } private function render($section, array $variables) { if ($section != seatmap) { $section = seatmap_.$section; } $templates = $this->getTemplates(); if (isset($templates[$section])) { return $templates[$section]->renderBlock($section, $variables); } return null; }} 38/73
  • 39. <?phpclass SeatMapExtension extends Twig_Extension{ public function renderSeatMap(SeatMap $seatMap, array $variables = array()) { return $this->render(seatmap, array( items => $seatMap->getItems(), variables => $variables, )); } public function renderSeatMapItem(Plottable $item, array $variables = array()) { $variables = array_merge(array(item => $item), $variables); return $this->render($item->getTypeName(), $variables); } private function render($section, array $variables) { if ($section != seatmap) { $section = seatmap_.$section; } $templates = $this->getTemplates(); if (isset($templates[$section])) { return $templates[$section]->renderBlock($section, $variables); } return null; }} 39/73
  • 40. {% block content %} <ul> <li>{{ venue.name }}</li> <li>{{ venue.address }}</li> </ul> {% for seatMap in venue.seatMaps %} {% seatmap_render(venue.seatMap) %} {% endfor %}{% endblock %} 40/73
  • 41. # app/config/services.ymlservices: seatmap.twig.extension: class: IngressoPraticoVenueBundleTwigSeatMapExtension arguments: [ IngressoPraticoVenueBundle:SeatMap:base_layout.html.twig ] tags: - { name: twig.extension } 41/73
  • 42. # app/config/services.ymlservices: seatmap.twig.extension: class: IngressoPraticoVenueBundleTwigSeatMapExtension arguments: [ IngressoPraticoVenueBundle:SeatMap:base_layout.html.twig ] tags: - { name: twig.extension } 42/73
  • 43. {# IngressoPratico/VenueBundle/Resources/SeatMap/base_layout.html.twig #}{% block seatmap %}<div class="seat-map"> {% for item in items %} {{ seatmap_render_item(item, variables) }} {% endfor %}</div>{% endblock seatmap %}{% block seatmap_seat %}{% spaceless %} {{ seatmap_render_seattype_css(item, image_dir) }} {% if item.status %} {% set status = ~ item.status %} {% endif %} {% set styles = seatmap_render_item_style(item, image_dir) %} <div class="seat {{ status }}"{% if styles %} {{ styles|raw }}{% endif %}> {{ seatmap_render_item(item.content) }} </div>{% endspaceless %}{% endblock seatmap_seat %}{# ... #} 43/73
  • 44. 44/73
  • 45. 45/73
  • 46. 46/73
  • 47. 47/73
  • 48. 48/73
  • 49. 49/73
  • 50. 50/73
  • 51. <?php// IngressoPratico/EventBundle/Entity/EventManager.phpnamespace IngressoPraticoEventBundleEntity;use IngressoPraticoEventBundleViewEventView;class EventManager extends AbstractManager{ public function loadEventView($id) { $view = new EventView($this->findOneAndLoadVenueSeatMaps($id)); return $view ->setSeatsTickets($this->getEventTickets($id)) ->setTicketsStatus($this->getEventVenueTicketsStatuses($id)); } private function getEventVenueTicketsStatuses($id) { $sql = A long SQL clause.; $ticketsStatuses = $this->em ->getConnection() ->executeQuery($sql, array(eventId => $id)) ->fetchAll(); return $ticketsStatuses; }} 51/73
  • 52. <?php// IngressoPratico/EventBundle/Entity/EventManager.phpnamespace IngressoPraticoEventBundleEntity;use IngressoPraticoEventBundleViewEventView;class EventManager extends AbstractManager{ public function loadEventView($id) { $view = new EventView($this->findOneAndLoadVenueSeatMaps($id)); return $view ->setSeatsTickets($this->getEventTickets($id)) ->setTicketsStatus($this->getEventVenueTicketsStatuses($id)); } private function getEventVenueTicketsStatuses($id) { $sql = A long SQL clause.; $ticketsStatuses = $this->em ->getConnection() ->executeQuery($sql, array(eventId => $id)) ->fetchAll(); return $ticketsStatuses; }} 52/73
  • 53. 53/73
  • 54. <?php// IngressoPratico/EventBundle/Entity/EventManager.phpnamespace IngressoPraticoEventBundleEntity;use IngressoPraticoEventBundleViewEventView;class EventManager extends AbstractManager{ public function loadEventView($id) { $view = new EventView($this->findOneAndLoadVenueSeatMaps($id)); return $view ->setSeatsTickets($this->getEventTickets($id)) ->setTicketsStatus($this->getEventVenueTicketsStatuses($id)); } private function getEventVenueTicketsStatuses($id) { $sql = A long SQL clause.; $ticketsStatuses = $this->em ->getConnection() ->executeQuery($sql, array(eventId => $id)) ->fetchAll(); return $ticketsStatuses; }} 54/73
  • 55. 55/73
  • 56. <?php// IngressoPratico/EventBundle/Entity/EventManager.phpnamespace IngressoPraticoEventBundleEntity;use IngressoPraticoEventBundleViewEventView;class EventManager extends AbstractManager{ public function loadEventView($id) { $view = new EventView($this->findOneAndLoadVenueSeatMaps($id)); return $view ->setSeatsTickets($this->getEventTickets($id)) ->setTicketsStatus($this->getEventVenueTicketsStatuses($id)); } private function getEventVenueTicketsStatuses($id) { $sql = A long SQL clause.; $ticketsStatuses = $this->em ->getConnection() ->executeQuery($sql, array(eventId => $id)) ->fetchAll(); return $ticketsStatuses; }} 56/73
  • 57. 57/73
  • 58. <?php// IngressoPratico/EventBundle/View/EventView.phpnamespace IngressoPraticoEventBundleView;use IngressoPraticoEventBundleEntityEvent;class EventView{ protected $seatsTickets = array(); protected $ticketsStatus = array(); public function __construct(Event $event) { $this->event = $event; } public function setSeatsTickets(array $tickets) { foreach ($tickets as $ticket) { $this->seatsTickets[$ticket->getSeatId()] = $ticket; } } public function setTicketsStatus(array $ticketsStatus) { foreach ($ticketsStatus as $ticketStatus) { $this->ticketsStatus[$ticketStatus[catalog_items_id]] = array( status => $ticketStatus[status], origin => $ticketStatus[origin] ); } }} 58/73
  • 59. <?php// IngressoPratico/EventBundle/View/EventView.phpnamespace IngressoPraticoEventBundleView;use IngressoPraticoEventBundleEntityEvent;class EventView{ protected $seatsTickets = array(); protected $ticketsStatus = array(); public function __construct(Event $event) { $this->event = $event; } public function setSeatsTickets(array $tickets) { foreach ($tickets as $ticket) { $this->seatsTickets[$ticket->getSeatId()] = $ticket; } } public function setTicketsStatus(array $ticketsStatus) { foreach ($ticketsStatus as $ticketStatus) { $this->ticketsStatus[$ticketStatus[catalog_items_id]] = array( status => $ticketStatus[status], origin => $ticketStatus[origin] ); } }} 59/73
  • 60. 60/73
  • 61. 61/73
  • 62. <?phpnamespace IngressoPraticoOrderBundleModel;final class Statuses{ const CANCELLED = -1; const OPEN = 0; const PAID = 2;} 62/73
  • 63. <?php// IngressoPratico/OrderBundle/Util/OrderItemStatusConverter.phpnamespace IngressoPraticoOrderBundleUtil;use IngressoPraticoOrderBundleModelStatuses as OrderStatuses;use IngressoPraticoCommonBundleUtilStatusConverter;class OrderItemStatusConverter extends StatusConverter{ static public function getStatusString($status) { switch ($status): case OrderStatuses::CANCELLED: $status = available; break; case OrderStatuses::OPEN: $status = reserved; break; case OrderStatuses::PAID: $status = sold; break; default: $status = unavailable; break; endswitch; return $status; }} 63/73
  • 64. <?php// IngressoPratico/EventBundle/View/EventView.phpnamespace IngressoPraticoEventBundleView;use IngressoPraticoEventBundleEntityEvent;use IngressoPraticoEventBundleUtilStatusConverterFactory;class EventView{ // ... public function getTicketStatusForSeatId($seatId) { $ticketId = $this->getTicketIdForSeatId($seatId); if (isset($this->ticketsStatus[$ticketId])) { $status = $this->ticketsStatus[$ticketId][status]; $converter = $this->ticketsStatus[$ticketId][origin]; $converter = StatusConverterFactory::create($converter); return $converter->getStatusString($status); } return available; }} 64/73
  • 65. <?php// IngressoPratico/EventBundle/Controller/EventControllernamespace IngressoPraticoEventBundleController;use IngressoPraticoEventBundleEntityEventManager;use SymfonyBundleFrameworkBundleControllerController;class EventController extends Controller{ public function showAction($id) { $eventManager = $this->container->get(event.event.manager); $eventView = $eventManager->loadEventQuotaForSeatMapView($id, $user); if (!$eventView) { throw $this->createNotFoundException(); } return array(eventView => $eventView); }} 65/73
  • 66. {# IngressoPratico/EventBundle/Resources/Event/show.html.twig #}{% seatmap_theme IngressoPraticoEventBundle:Event:seatmap-blocks.html.twig %}{% for seatMap in eventView.eventVenueSeatMaps %}<div class="map map-{{ loop.index }}"> {{ seatmap_render(seatMap, {eventView: eventView}) }}</div>{% endfor %} 66/73
  • 67. {# IngressoPratico/EventBundle/Resources/Event/show.html.twig #}{% seatmap_theme IngressoPraticoEventBundle:Event:seatmap-blocks.html.twig %}{% for seatMap in eventView.eventVenueSeatMaps %}<div class="map map-{{ loop.index }}"> {{ seatmap_render(seatMap, {eventView: eventView}) }}</div>{% endfor %} 67/73
  • 68. {# IngressoPratico/EventBundle/Resources/Event/seatmap-blocks.html.twig #}{% block seatmap_seat %}{% spaceless %} {{ seatmap_render_seattype_css(item) }} {% set status = eventView.getTicketStatusForSeatId(item.id) %} {% set styles = seatmap_render_item_style(item, image_dir) %} <div class="seat {{ status }}"{% if styles %} {{ styles|raw }}{% endif %}> {{ ... }} </div>{% endspaceless %}{% endblock seatmap_seat %}{# ... #} 68/73
  • 69. 69/73
  • 70. <?phpnamespace IngressoPraticoVenueBundleTestsTwig;class SeatMapExtensionTest extends TestCase{ /** @test */ public function seatItemShouldMatchXpath() { $html = $this ->extension ->renderSeatMapItem($this->getSeatInstance()); $this->assertMatchesXpath($html,./style |./div[@class="seat available"] [ ./div[@class="content" and text() = "1"] ] ); }} 70/73
  • 71. Conclusões Testabilidade Código limpo Arquitetura Orientada a Serviços 71/73
  • 72. 380 horas/homem 427,5 hh projeto Maio → Setembro 18 de Julho 2011 (Symfony 2.0-BETA5) 21 implantações 72/73
  • 73. Obrigado! Eriksen Costa ifgy.co/ecp e@ifgy.co