Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

The Naked Bundle - Tryout

770 Aufrufe

Veröffentlicht am

Tryout of The Naked Bundle at e-Active in Zwolle (NL).

Veröffentlicht in: Technologie
  • Loggen Sie sich ein, um Kommentare anzuzeigen.

The Naked Bundle - Tryout

  1. 1. The Naked Bundle Matthias Noback @matthiasnoback
  2. 2. What is it, really?
  3. 3. An actual naked bundle
  4. 4. I could've called it BundleLitetm The No Code Bundle The Clean Bundle
  5. 5. But “naked” is catchy and controversial
  6. 6. The official view on bundles
  7. 7. First-class citizens Documentation » The Quick Tour » The Architecture
  8. 8. Importance Your code is more important than the framework, which is an implementation detail
  9. 9. Reuse
  10. 10. Nice!
  11. 11. All your code lives in a bundle Documentation » The Book » Creating Pages in Symfony2
  12. 12. Reuse “All your code in a bundle” contradicts the promise of reuse
  13. 13. Everything lives inside a bundle Documentation » Glossary
  14. 14. Not really true Many things live inside libraries (including the Symfony components)
  15. 15. Which is good!
  16. 16. But you probably know that already
  17. 17. “libraries first”
  18. 18. What about... ● Controllers ● Entities ● Templates ● ...
  19. 19. They just need to be in a bundle Or do they?
  20. 20. Don't get me wrong I love Symfony!
  21. 21. But a framework is just a framework ● Quickstarter for your projects ● Prevents and solves big security issues for you ● Has a community you can rely on
  22. 22. A framework is there for you
  23. 23. Your code doesn't need a framework
  24. 24. Noback's Principle Code shouldn't rely on something it doesn't truly need
  25. 25. Bundle conventions Things in a bundle often rely on conventions to work
  26. 26. Conventions aren't necessary at all
  27. 27. Naming conventions Controllers: ● *Controller classes ● *action methods Templates: ● in /Resources/views ● name: Controller/Action.html.twig
  28. 28. Structural conventions Controller: ● Extends framework Controller class ● Is ContainerAware
  29. 29. Configuration conventions Use lots of annotations! /** * @Route("/{id}") * @Method("GET") * @ParamConverter("post", class="SensioBlogBundle:Post") * @Template("SensioBlogBundle:Annot:show.html.twig") * @Cache(smaxage="15", lastmodified="post.getUpdatedAt()") * @Security("has_role('ROLE_ADMIN')") */ public function showAction(Post $post) { }
  30. 30. These conventions are what makes an application a Symfony2 application
  31. 31. A Year With Symfony
  32. 32. About bundles
  33. 33. A bundle exposes resources
  34. 34. Resources ● Service definitions ● Controllers ● Routes ● Templates ● Entities ● Form types ● Event listeners ● Translations ● ...
  35. 35. No need for them to be inside a bundle
  36. 36. When placed outside the bundle the resources could be reused separately
  37. 37. The bundle would be really small
  38. 38. And could just as well be a: Laravel or CodeIgniter package, Zend or Drupal module, CakePHP plugin, ...
  39. 39. So the challenge is to Make the bundle as clean as possible
  40. 40. Move the “misplaced” things to ● a library ● with dependencies ● but not symfony/framework­bundle ;)
  41. 41. Being realistic Practical reusability
  42. 42. Reuse within the Symfony family Think: Silex, Laravel, etc.
  43. 43. Allowed dependency HttpFoundation ● Request ● Response ● Exceptions ● etc.
  44. 44. What do we rely on HttpKernel namespace SymfonyComponentHttpKernel; use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpFoundationResponse; interface HttpKernelInterface { /** * Handles a Request to convert it to a Response. */ public function handle(Request $request, ...); }
  45. 45. Why? My secret missions ● Prevent the need for a complete rework ● “Let's rebuild the application, but this time we use Zend4 instead of Symfony2”
  46. 46. And of course Education
  47. 47. You need a strong coupling radar
  48. 48. Explicit dependencies ● Function calls ● Imported classes (“use”) ● ...
  49. 49. Implicit dependencies ● File locations ● File names ● Method names ● ...
  50. 50. There we go!
  51. 51. use SymfonyBundleFrameworkBundleControllerController; use SensioBundleFrameworkExtraBundleConfigurationRoute; use SensioBundleFrameworkExtraBundleConfigurationTemplate; /** * @Route(“/article”) */ class ArticleController extends Controller { /** * @Route(“/edit”) * @Template() */ function editAction(...) { ... } } Controller
  52. 52. TODO ✔ Don't rely on things that may not be there in another context: ✔ Parent Controller class ✔ Routing, template, annotations, etc.
  53. 53. class ArticleController { function editAction(...) { ... } } Nice and clean ;)
  54. 54. use SymfonyComponentHttpFoundationRequest; class ArticleController { public function editAction(Request $request) { $em = $this­> get('doctrine')­> getManager(); ... if (...) { throw $this­> createNotFoundException(); } ... return array( 'form' => $form­> createView() ); } } Zooming in a bit
  55. 55. TODO ✔ Inject dependencies ✔ Don't use helper methods ✔ Render the template manually ✔ Keep using Request (not really a TODO)
  56. 56. use DoctrineORMEntityManager; class ArticleController { function __construct( EntityManager $em, ) { $this­> em = $em; } ... } Inject dependencies
  57. 57. Inline helper methods use SymfonyComponentHttpKernelExceptionNotFoundHttpException class ArticleController { ... public function newAction(...) { ... throw new NotFoundHttpException(); ... } }
  58. 58. use SymfonyComponentHttpFoundationResponse; use SymfonyComponentTemplatingEngineInterface; class ArticleController { function __construct(..., EngineInterface $templating) { } public function newAction(...) { ... return new Response( $this­> templating­> render( '@MyBundle:Article:new.html.twig', array( 'form' => $form­> createView() ) ) ); } } Render the template manually
  59. 59. Dependencies are explicit now Also: no mention of a “bundle” anywhere! use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpFoundationResponse; use SymfonyComponentTemplatingEngineInterface; use DoctrineORMEntityManager;
  60. 60. TODO ✔ Set up routing ✔ Create a service and provide the right arguments
  61. 61. Bundle stuff: services.xml <!­­in MyBundle/Resources/config/services.xml → <?xml version="1.0" ?> <container> <services> <service id="article_controller" class="MyBundleControllerArticleController"> <argument type="service" id="doctrine.orm.default_entity_manager" /> <argument type="service" id="mailer" /> <argument type="service" id="templating" /> </service> </services> </container>
  62. 62. Bundle stuff: routing.xml <!­­in MyBundle/Resources/config/routing.xml → <?xml version="1.0" encoding="UTF­8" ?> <routes> <route id="new_article" path="/article/new"> <default key="_controller"> article_controller:newAction </default> </route> </routes>
  63. 63. Controller – Achievements ● Can be anywhere ● No need to follow naming conventions (“*Controller”, “*action”) ● Dependency injection, no service location ● Reusable in any application using HttpFoundation
  64. 64. Next up: Entities
  65. 65. namespace MyBundleEntity; use DoctrineORMMapping as ORM; class Article { ... /** * @ORMColumn(type=”string”) */ private $title; }
  66. 66. Convention ● They have to be in the /Entity directory ● You define mapping metadata using annotations
  67. 67. What's wrong with annotations? Annotations are classes which need to be there
  68. 68. namespace MyBundleEntity; use DoctrineORMMapping as ORM; class Article { ... /** * @ORMColumn(type=”string”) */ private $title; }
  69. 69. Well, uhm, yes, but...
  70. 70. Are you ever going to replace your persistence library?
  71. 71. There is no real alternative to Doctrine, right?
  72. 72. Well... Think of Doctrine MongoDB ODM, Doctrine CouchDB ODM, etc.
  73. 73. TODO ✔ Remove annotations ✔ Find another way to map the data
  74. 74. namespace MyBundleEntity; class Article { private $id; private $title; } Nice and clean A true POPO, the ideal of the data mapper pattern
  75. 75. Use XML for mapping metadata <doctrine­mapping> <entity name=”MyBundleEntityArticle”> <id name="id" type="integer" column="id"> <generator strategy="AUTO"/> </id> <field name=”title” type=”string”> </entity> </doctrine­mapping>
  76. 76. Conventions for XML metadata ● For MyBundleEntityArticle ● Put XML here: @MyBundle/Resources/config/doctrine/ Article.orm.xml
  77. 77. We don't want it in the bundle! There's a nice little trick
  78. 78. You need DoctrineBundle >=1.2 { "require": { ..., "doctrine/doctrine­bundle": "~1.2@dev" } }
  79. 79. use DoctrineBundleDoctrineBundleDependencyInjectionCompiler DoctrineOrmMappingsPass; class MyBundle { public function build(ContainerBuilder $container) { $container­> addCompilerPass( $this­> buildMappingCompilerPass() ); } private function buildMappingCompilerPass() { $xmlPath = '%kernel.root_dir%/../src/MyLibrary/Doctrine'; $namespacePrefix = 'MyLibraryModel'; return DoctrineOrmMappingsPass::createXmlMappingDriver( array($xmlPath => $namespacePrefix) ); } }
  80. 80. Now: ● For MyLibraryModelArticle ● Put XML here: src/MyLibrary/Mapping/Article.orm.xml
  81. 81. Entities - Achievements ● Entity classes can be anywhere ● Mapping metadata can be anywhere and in different formats ● Entities are true POPOs
  82. 82. Finally: Templates
  83. 83. Conventions ● In /Resources/views/[Controller] ● Filename: [Action].[format].[engine]
  84. 84. The difficulty with templates They can have all kinds of implicit dependencies: ● global variables, e.g. {{ app.request }} ● functions, e.g. {{ path(...) }} ● parent templates, e.g. {% extends “::base.html.twig” %} ● ...
  85. 85. Still, we want them out! And it's possible
  86. 86. Documentation » The Cookbook » Templating » How to use and Register namespaced Twig Paths # in config.yml twig: ... paths: Twig namespaces "%kernel.root_dir%/../src/MyLibrary/Views": MyLibrary // in the controller return $this­> templating­> render('@MyLibrary/Template.html.twig');
  87. 87. Get rid of absolute paths Using Puli, created by Bernhard Schüssek (Symfony Forms, Validation)
  88. 88. What Puli does Find the absolute paths of resources in a project
  89. 89. use WebmozartPuliRepositoryResourceRepository; $repo = new ResourceRepository(); $repo­> add('/my­library/ views', '/absolute/path/to/views/*'); /my-library/views /css/style.css /absolute/path/to/views /css/style.css echo $repo ­> get('/my­library/ views/index.html.twig') ­> getRealPath(); // => /absolute/path/to/views/index.html.twig
  90. 90. Register “prefixes” Manually, or using the Puli Composer plugin // in the composer.json file of a package or project { "extra": { "resources": { "/my­library/ views": "src/MyLibrary/Views" } } }
  91. 91. Puli – Twig extension // in composer.json { "extra": { "resources": { "/my­library/ views": "src/MyLibrary/Views" } } } // in the controller return $this­> templating ­> render('/my­library/ views/index.html.twig');
  92. 92. Many possibilities ● Templates ● Translation files ●Mapping metadata ● Service definitions ● ...
  93. 93. The future is bright ● Puli is not stable yet ● But I expect much from it:
  94. 94. Puli will be the ultimate tool
  95. 95. to create NAKED BUNDLES
  96. 96. and to enable reuse of many kinds of resources
  97. 97. cross package! cross application! cross framework!
  98. 98. But even without Puli There's a whole lot you can do to make your code not rely on the framework
  99. 99. Remember The framework is for you Your code doesn't need it
  100. 100. Questions?
  101. 101. Get a $7,50 discount: http://leanpub.com/a-year-with-symfony/c/SymfonyLiveLondon2014
  102. 102. Get a $10,00 introduction discount: http://leanpub.com/principles-of-php-package-design/c/SymfonyLive London2014
  103. 103. Thank you Feedback: joind.in/11553 Talk to me: @matthiasnoback
  104. 104. Images ● Sally MacKenzie: www.amazon.com

×