SlideShare a Scribd company logo
The Naked Bundle 
Matthias Noback 
@matthiasnoback 
Symfony Barcelona 
October 24th, 2014
What's it all about?
An actual naked bundle
I could've called it 
BundleLitetm 
The No Code Bundle 
The Clean Bundle
But “naked” was catchy and controversial
Until Pierre came along
The official view on bundles
First-class citizens 
Documentation » The Quick Tour » The Architecture
Importance 
Your code is more important than 
the framework, 
which is an implementation detail
Reuse
Nice!
All your code lives in a bundle 
Documentation » The Book » Creating Pages in Symfony2
Reuse 
“All your code in a bundle” 
contradicts the promise of reuse
Everything lives inside a bundle 
Documentation » Glossary
Not really true 
Many things live inside libraries 
(the Symfony components are 
libraries too!)
Which is good!
But you probably know that already
“libraries first”
What about... 
● Controllers 
● Entities 
● Templates 
● ...
They just need to be in a bundle 
Or do they?
Don't get me wrong 
I love Symfony!
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
A framework is there for you
Your code doesn't need a framework
Noback's Principle 
Code shouldn't rely on something 
it doesn't truly need
Bundle conventions 
Things in a bundle often rely on 
conventions to work
Conventions aren't necessary at all
So according to Noback's Principle, 
code shouldn't rely on bundle conventions too
Naming conventions 
Controllers: 
● *Controller classes 
● *action methods 
Templates: 
● in /Resources/views 
● name: Controller/Action.html.twig
Structural conventions 
Controller: 
● Extends framework Controller class 
● Is ContainerAware
Behavioral conventions 
Controller: 
● Is allowed to return an array 
● Actions can type-hint to objects which will be 
fetched based on route parameters (??)
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) 
{}
These conventions are what makes an application 
a Symfony2 application
The Naked Bundle - Symfony Barcelona
About bundles
A bundle exposes resources
Resources 
● Service definitions 
● Controllers 
● Routes 
● Templates 
● Entities 
● Form types 
● Event listeners 
● Translations 
● ...
No need for them to be inside a bundle
When placed outside the bundle 
the resources could be reused separately
The bundle would be really small
And could just as well be a: 
Laravel package, 
Zend or Drupal module, 
CakePHP plugin, 
...
So the challenge is to 
Make the bundle as clean as possible
Move the “misplaced” things to 
● a library 
● with dependencies 
● but not symfony/framework-bundle ;)
Being realistic 
Practical reusability
Reuse within the Symfony family 
Think: Silex, Laravel, etc.
Allowed dependency 
HttpFoundation 
● Request 
● Response 
● Exceptions 
● etc.
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, ...); 
}
Why? My secret missions 
“Let's rebuild the application, but 
this time we use Zend4 instead of 
Symfony2”
And of course 
Education
You need a strong coupling radar
Explicit dependencies 
● Function calls 
● Imported classes (“use”) 
● Included files 
● ...
Implicit dependencies 
● File locations 
● File, class, method names 
● Structure of return values 
● ...
There we go!
use SymfonyBundleFrameworkBundleControllerController; 
use SensioBundleFrameworkExtraBundleConfigurationRoute; 
use SensioBundleFrameworkExtraBundleConfigurationTemplate; 
/** 
* @Route(“/article”) 
*/ 
class ArticleController extends Controller 
{ 
/** 
* @Route(“/edit”) 
* @Template() 
*/ 
function editAction(...) 
{ 
... 
} 
} 
Controller
TODO 
✔ Don't rely on things that may not 
be there in another context: 
✔ Parent Controller class 
✔ Routing, template, annotations, etc.
class ArticleController 
{ 
function editAction(...) 
{ 
... 
} 
} 
Nice and clean ;)
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
TODO 
✔ Inject dependencies 
✔ Don't use helper methods 
✔ Render the template manually 
✔ Keep using Request 
(not really a TODO)
use DoctrineORMEntityManager; 
class ArticleController 
{ 
function __construct( 
EntityManager $em, 
) { 
$this->em = $em; 
} 
... 
} 
Inject dependencies
Inline helper methods 
use SymfonyComponentHttpKernelExceptionNotFoundHttpException 
class ArticleController 
{ 
... 
public function editAction(...) 
{ 
... 
throw new NotFoundHttpException(); 
... 
} 
}
use SymfonyComponentHttpFoundationResponse; 
use SymfonyComponentTemplatingEngineInterface; 
class ArticleController 
{ 
function __construct(..., EngineInterface $templating) 
{ 
... 
} 
public function editAction(...) 
{ 
... 
return new Response( 
$this->templating->render( 
'@MyBundle:Article:edit.html.twig', 
array( 
'form' => $form->createView() 
) 
) 
); 
} 
} 
Render the template manually
Dependencies are explicit now 
Also: no mention of a “bundle” 
anywhere! 
use SymfonyComponentHttpFoundationRequest; 
use SymfonyComponentHttpFoundationResponse; 
use SymfonyComponentTemplatingEngineInterface; 
use DoctrineORMEntityManager;
Dependency overflow 
use SymfonyComponentHttpFoundationResponse; 
use SymfonyComponentTemplatingEngineInterface; 
class ArticleController 
{ 
function __construct( 
EntityManager $entityManager, 
EngineInterface $templating, 
TranslatorInterface $translator, 
ValidatorInterface $validator, 
Swift_Mailer $mailer, 
RouterInterface $router 
) { 
... 
} 
... 
}
The cause? 
Convention
One controller, many actions 
one action!
__invoke() 
namespace MyLibraryControllerArticle; 
class Edit 
{ 
function __construct(...) 
{ 
// only what's necessary for this “action” 
} 
public function __invoke(...) 
{ 
... 
} 
}
Nice! 
└─Controller 
└─Article 
├─Create.php 
├─Edit.php 
├─Archive.php 
└─Delete.php
TODO 
✔ Set up routing 
✔ Create a service and provide the 
right arguments
Bundle stuff: services.xml 
<!-- in MyBundle/Resources/config/services.xml → 
<?xml version="1.0" ?> 
<container> 
<services> 
<service id="edit_article_controller" 
class="MyBundleControllerArticleEdit"> 
<argument type="service" 
id="doctrine.orm.default_entity_manager" /> 
<argument type="service" id="templating" /> 
</service> 
</services> 
</container>
Bundle stuff: routing.xml 
<!-- in MyBundle/Resources/config/routing.xml → 
<?xml version="1.0" encoding="UTF-8" ?> 
<routes> 
<route id="edit_article" 
path="/article/edit/{id}"> 
<default key="_controller"> 
edit_article_controller:__invoke 
</default> 
</route> 
</routes> 
Pull request by Kevin Bond allows you to leave out the “:__invoke” part!
Controller – Achievements 
● Can be anywhere 
● No need to follow naming conventions 
(“*Controller”, “*action”) 
● Dependency injection, no service location 
● Reusable in any application using 
HttpFoundation
Next up: Entities
namespace MyBundleEntity; 
use DoctrineORMMapping as ORM; 
class Article 
{ 
... 
/** 
* @ORMColumn(type=”string”) 
*/ 
private $title; 
} 
Entity conventions
What's wrong with annotations? 
namespace MyBundleEntity; 
use DoctrineORMMapping as ORM; 
class Article 
{ 
... 
/** 
* @ORMColumn(type=”string”) 
*/ 
private $title; 
}
Annotations are classes
use DoctrineCommonAnnotationsAnnotationReader; 
$reader = new AnnotationReader(); 
$class = new ReflectionClass('Article'); 
$reader->getClassAnnotations($class); 
BANG 
Class 
DoctrineORMMappingColumn 
not found
“Well, uhm, yes, but...
… are you ever going to use 
anything else than Doctrine ORM?”
Well... 
Think about Doctrine MongoDB ODM, 
Doctrine CouchDB ODM, etc. 
namespace MyBundleEntity; 
use DoctrineORMMapping as ORM; 
use DoctrineODMMongoDBMappingAnnotations as MongoDB; 
use DoctrineODMCouchDBMappingAnnotations as CoucheDB; 
class Article 
{ 
/** 
* @ORMColumn 
* @MognoDBField 
* @CoucheDBField 
*/ 
private $title; 
}
TODO 
✔ Remove annotations 
✔ Find another way to map the data
namespace MyBundleEntity; 
class Article 
{ 
private $id; 
private $title; 
} 
Nice and clean 
A true POPO, the ideal of the data 
mapper pattern
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>
Conventions for XML metadata 
● For MyBundleEntityArticle 
● Put XML here: 
@MyBundle/Resources/config/doctrine/ 
Article.orm.xml
We don't want it in the bundle! 
There's a nice little trick
You need DoctrineBundle >=1.3 
{ 
"require": { 
..., 
"doctrine/doctrine-bundle": "~1.3@dev" 
} 
}
use DoctrineBundleDoctrineBundleDependencyInjectionCompiler 
DoctrineOrmMappingsPass; 
class MyBundle extends Bundle 
{ 
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) 
); 
} 
}
Now: 
● For MyLibraryModelArticle 
● Put XML here: 
src/MyLibrary/Doctrine/Article.orm.xml
Entities - Achievements 
● Entity classes can be anywhere 
●Mapping metadata can be 
anywhere and in different formats 
● Entities are true POPOs
Finally: Templates
Conventions 
● In /Resources/views/[Controller] 
● Filename: [Action].[format].[engine]
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” %}
Still, we want them out! 
And it's possible
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');
Get rid of absolute paths 
Using Puli, created by Bernhard 
Schüssek (Symfony Forms, 
Validation)
What Puli does 
Find the absolute paths of 
resources in a project
use WebmozartPuliRepositoryResourceRepository; 
$repo = new ResourceRepository(); 
$repo->add('/my-library/views', '/absolute/path/to/views/*'); 
/my-library/views /index.html.twig 
/absolute/path/to/views /index.html.twig 
echo $repo 
->get('/my-library/views/index.html.twig') 
->getRealPath(); 
// => /absolute/path/to/views/index.html.twig
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" 
} 
} 
}
Twig templates 
// in composer.json 
{ 
"extra": { 
"resources": { 
"/my-library/views": "src/MyLibrary/Views" 
} 
} 
} 
Puli Twig extension 
// in the controller 
return $this->templating 
->render('/my-library/views/index.html.twig');
Many possibilities 
● Templates 
● Translation files 
●Mapping metadata 
● Service definitions 
● And so on!
The future is bright 
● Puli is not stable yet 
● But I expect much from it:
Puli will be the ultimate tool
to create NAKED BUNDLES
and to enable reuse of 
many kinds of resources
not limited by 
project, 
framework, 
even language 
boundaries!
But even without Puli 
There's a whole lot you can do to make your code 
not rely on the framework
Remember 
The framework is for you 
Your code doesn't need it
Questions?
Buy it for $ 17,50 
http://leanpub.com/a-year-with-symfony/c/symfony-barcelona
Get a $10,00 introduction discount: 
http://leanpub.com/principles-of-php-package-design/c/symfony-barcelona
Thank you 
Talk to me: @matthiasnoback
Images 
Sally MacKenzie: 
www.amazon.com

More Related Content

PDF
The Naked Bundle - Symfony Usergroup Belgium
KEY
A tour on ruby and friends
PDF
JavaScript - From Birth To Closure
PPTX
5 Tips for Better JavaScript
PDF
Symfony 2
PDF
iPhone Seminar Part 2
PPT
Javascript and Jquery Best practices
PDF
The Naked Bundle - Symfony Usergroup Belgium
A tour on ruby and friends
JavaScript - From Birth To Closure
5 Tips for Better JavaScript
Symfony 2
iPhone Seminar Part 2
Javascript and Jquery Best practices

What's hot (20)

PDF
Fundamental JavaScript [UTC, March 2014]
KEY
Javascript tid-bits
PDF
Performance Optimization and JavaScript Best Practices
PDF
JavaScript Basics and Best Practices - CC FE & UX
KEY
Parte II Objective C
PPT
JavaScript - An Introduction
KEY
Runtime
PDF
Javascript Design Patterns
PDF
JS Level Up: Prototypes
PDF
What Makes Objective C Dynamic?
PPTX
Javascript Common Design Patterns
PPTX
Node.js for PHP developers
PPT
eXo SEA - JavaScript Introduction Training
PDF
Objective c runtime
PPTX
Developing components and extensions for ext js
PDF
Multithreading and Parallelism on iOS [MobOS 2013]
PPTX
Javascript basics for automation testing
PDF
JavaScript Programming
PDF
Iphone course 1
PDF
Dart Workshop
Fundamental JavaScript [UTC, March 2014]
Javascript tid-bits
Performance Optimization and JavaScript Best Practices
JavaScript Basics and Best Practices - CC FE & UX
Parte II Objective C
JavaScript - An Introduction
Runtime
Javascript Design Patterns
JS Level Up: Prototypes
What Makes Objective C Dynamic?
Javascript Common Design Patterns
Node.js for PHP developers
eXo SEA - JavaScript Introduction Training
Objective c runtime
Developing components and extensions for ext js
Multithreading and Parallelism on iOS [MobOS 2013]
Javascript basics for automation testing
JavaScript Programming
Iphone course 1
Dart Workshop
Ad

Similar to The Naked Bundle - Symfony Barcelona (20)

PDF
The Naked Bundle - Symfony Live London 2014
PDF
The Naked Bundle - Tryout
PDF
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
PDF
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
PDF
Symfony tips and tricks
PPT
Workshop: Symfony2 Intruduction: (Controller, Routing, Model)
PDF
Symfony finally swiped right on envvars
PDF
Dependency injection in Drupal 8
ODP
Bring the fun back to java
PPTX
Back-2-Basics: .NET Coding Standards For The Real World (2011)
PDF
Build powerfull and smart web applications with Symfony2
KEY
Reusable Ruby • Rt 9 Ruby Group • Jun 2012
PDF
Hands-on with the Symfony2 Framework
PDF
Symfony2 revealed
PDF
Living With Legacy Code
PDF
The Beauty And The Beast Php N W09
PPTX
Creating and destroying objects
PDF
Global objects in Node.pdf
PDF
cf.Objective() 2017 - Design patterns - Brad Wood
DOCX
C# Unit 2 notes
The Naked Bundle - Symfony Live London 2014
The Naked Bundle - Tryout
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
Symfony tips and tricks
Workshop: Symfony2 Intruduction: (Controller, Routing, Model)
Symfony finally swiped right on envvars
Dependency injection in Drupal 8
Bring the fun back to java
Back-2-Basics: .NET Coding Standards For The Real World (2011)
Build powerfull and smart web applications with Symfony2
Reusable Ruby • Rt 9 Ruby Group • Jun 2012
Hands-on with the Symfony2 Framework
Symfony2 revealed
Living With Legacy Code
The Beauty And The Beast Php N W09
Creating and destroying objects
Global objects in Node.pdf
cf.Objective() 2017 - Design patterns - Brad Wood
C# Unit 2 notes
Ad

More from Matthias Noback (20)

PDF
Rector fireside chat - PHPMiNDS meetup
PDF
Service abstractions - Part 1: Queries
PDF
Hexagonal Symfony - SymfonyCon Amsterdam 2019
PDF
Advanced web application architecture - PHP Barcelona
PDF
A testing strategy for hexagonal applications
PDF
Advanced web application architecture - Talk
PDF
DPC 2019, Amsterdam: Beyond design patterns and principles - writing good OO ...
PDF
Layers, ports and adapters
PDF
Beyond design principles and patterns (muCon 2019 edition)
PDF
Brutal refactoring, lying code, the Churn, and other emotional stories from L...
PDF
Advanced web application architecture Way2Web
PDF
Brutal refactoring, lying code, the Churn, and other emotional stories from L...
PDF
Beyond Design Principles and Patterns
PDF
Building Autonomous Services
PDF
Advanced Application Architecture Symfony Live Berlin 2018
PDF
Designing for Autonomy
PDF
Docker workshop
PDF
Docker swarm workshop
PDF
Docker compose workshop
PDF
Building autonomous services
Rector fireside chat - PHPMiNDS meetup
Service abstractions - Part 1: Queries
Hexagonal Symfony - SymfonyCon Amsterdam 2019
Advanced web application architecture - PHP Barcelona
A testing strategy for hexagonal applications
Advanced web application architecture - Talk
DPC 2019, Amsterdam: Beyond design patterns and principles - writing good OO ...
Layers, ports and adapters
Beyond design principles and patterns (muCon 2019 edition)
Brutal refactoring, lying code, the Churn, and other emotional stories from L...
Advanced web application architecture Way2Web
Brutal refactoring, lying code, the Churn, and other emotional stories from L...
Beyond Design Principles and Patterns
Building Autonomous Services
Advanced Application Architecture Symfony Live Berlin 2018
Designing for Autonomy
Docker workshop
Docker swarm workshop
Docker compose workshop
Building autonomous services

Recently uploaded (20)

PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Approach and Philosophy of On baking technology
PDF
Electronic commerce courselecture one. Pdf
PPT
Teaching material agriculture food technology
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Modernizing your data center with Dell and AMD
PDF
NewMind AI Monthly Chronicles - July 2025
PDF
Empathic Computing: Creating Shared Understanding
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PPTX
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Chapter 3 Spatial Domain Image Processing.pdf
Approach and Philosophy of On baking technology
Electronic commerce courselecture one. Pdf
Teaching material agriculture food technology
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Digital-Transformation-Roadmap-for-Companies.pptx
NewMind AI Weekly Chronicles - August'25 Week I
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Diabetes mellitus diagnosis method based random forest with bat algorithm
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
“AI and Expert System Decision Support & Business Intelligence Systems”
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
Mobile App Security Testing_ A Comprehensive Guide.pdf
Modernizing your data center with Dell and AMD
NewMind AI Monthly Chronicles - July 2025
Empathic Computing: Creating Shared Understanding
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf

The Naked Bundle - Symfony Barcelona

  • 1. The Naked Bundle Matthias Noback @matthiasnoback Symfony Barcelona October 24th, 2014
  • 2. What's it all about?
  • 4. I could've called it BundleLitetm The No Code Bundle The Clean Bundle
  • 5. But “naked” was catchy and controversial
  • 7. The official view on bundles
  • 8. First-class citizens Documentation » The Quick Tour » The Architecture
  • 9. Importance Your code is more important than the framework, which is an implementation detail
  • 10. Reuse
  • 11. Nice!
  • 12. All your code lives in a bundle Documentation » The Book » Creating Pages in Symfony2
  • 13. Reuse “All your code in a bundle” contradicts the promise of reuse
  • 14. Everything lives inside a bundle Documentation » Glossary
  • 15. Not really true Many things live inside libraries (the Symfony components are libraries too!)
  • 17. But you probably know that already
  • 19. What about... ● Controllers ● Entities ● Templates ● ...
  • 20. They just need to be in a bundle Or do they?
  • 21. Don't get me wrong I love Symfony!
  • 22. 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
  • 23. A framework is there for you
  • 24. Your code doesn't need a framework
  • 25. Noback's Principle Code shouldn't rely on something it doesn't truly need
  • 26. Bundle conventions Things in a bundle often rely on conventions to work
  • 28. So according to Noback's Principle, code shouldn't rely on bundle conventions too
  • 29. Naming conventions Controllers: ● *Controller classes ● *action methods Templates: ● in /Resources/views ● name: Controller/Action.html.twig
  • 30. Structural conventions Controller: ● Extends framework Controller class ● Is ContainerAware
  • 31. Behavioral conventions Controller: ● Is allowed to return an array ● Actions can type-hint to objects which will be fetched based on route parameters (??)
  • 32. 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) {}
  • 33. These conventions are what makes an application a Symfony2 application
  • 36. A bundle exposes resources
  • 37. Resources ● Service definitions ● Controllers ● Routes ● Templates ● Entities ● Form types ● Event listeners ● Translations ● ...
  • 38. No need for them to be inside a bundle
  • 39. When placed outside the bundle the resources could be reused separately
  • 40. The bundle would be really small
  • 41. And could just as well be a: Laravel package, Zend or Drupal module, CakePHP plugin, ...
  • 42. So the challenge is to Make the bundle as clean as possible
  • 43. Move the “misplaced” things to ● a library ● with dependencies ● but not symfony/framework-bundle ;)
  • 45. Reuse within the Symfony family Think: Silex, Laravel, etc.
  • 46. Allowed dependency HttpFoundation ● Request ● Response ● Exceptions ● etc.
  • 47. 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, ...); }
  • 48. Why? My secret missions “Let's rebuild the application, but this time we use Zend4 instead of Symfony2”
  • 49. And of course Education
  • 50. You need a strong coupling radar
  • 51. Explicit dependencies ● Function calls ● Imported classes (“use”) ● Included files ● ...
  • 52. Implicit dependencies ● File locations ● File, class, method names ● Structure of return values ● ...
  • 54. use SymfonyBundleFrameworkBundleControllerController; use SensioBundleFrameworkExtraBundleConfigurationRoute; use SensioBundleFrameworkExtraBundleConfigurationTemplate; /** * @Route(“/article”) */ class ArticleController extends Controller { /** * @Route(“/edit”) * @Template() */ function editAction(...) { ... } } Controller
  • 55. TODO ✔ Don't rely on things that may not be there in another context: ✔ Parent Controller class ✔ Routing, template, annotations, etc.
  • 56. class ArticleController { function editAction(...) { ... } } Nice and clean ;)
  • 57. 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
  • 58. TODO ✔ Inject dependencies ✔ Don't use helper methods ✔ Render the template manually ✔ Keep using Request (not really a TODO)
  • 59. use DoctrineORMEntityManager; class ArticleController { function __construct( EntityManager $em, ) { $this->em = $em; } ... } Inject dependencies
  • 60. Inline helper methods use SymfonyComponentHttpKernelExceptionNotFoundHttpException class ArticleController { ... public function editAction(...) { ... throw new NotFoundHttpException(); ... } }
  • 61. use SymfonyComponentHttpFoundationResponse; use SymfonyComponentTemplatingEngineInterface; class ArticleController { function __construct(..., EngineInterface $templating) { ... } public function editAction(...) { ... return new Response( $this->templating->render( '@MyBundle:Article:edit.html.twig', array( 'form' => $form->createView() ) ) ); } } Render the template manually
  • 62. Dependencies are explicit now Also: no mention of a “bundle” anywhere! use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpFoundationResponse; use SymfonyComponentTemplatingEngineInterface; use DoctrineORMEntityManager;
  • 63. Dependency overflow use SymfonyComponentHttpFoundationResponse; use SymfonyComponentTemplatingEngineInterface; class ArticleController { function __construct( EntityManager $entityManager, EngineInterface $templating, TranslatorInterface $translator, ValidatorInterface $validator, Swift_Mailer $mailer, RouterInterface $router ) { ... } ... }
  • 65. One controller, many actions one action!
  • 66. __invoke() namespace MyLibraryControllerArticle; class Edit { function __construct(...) { // only what's necessary for this “action” } public function __invoke(...) { ... } }
  • 67. Nice! └─Controller └─Article ├─Create.php ├─Edit.php ├─Archive.php └─Delete.php
  • 68. TODO ✔ Set up routing ✔ Create a service and provide the right arguments
  • 69. Bundle stuff: services.xml <!-- in MyBundle/Resources/config/services.xml → <?xml version="1.0" ?> <container> <services> <service id="edit_article_controller" class="MyBundleControllerArticleEdit"> <argument type="service" id="doctrine.orm.default_entity_manager" /> <argument type="service" id="templating" /> </service> </services> </container>
  • 70. Bundle stuff: routing.xml <!-- in MyBundle/Resources/config/routing.xml → <?xml version="1.0" encoding="UTF-8" ?> <routes> <route id="edit_article" path="/article/edit/{id}"> <default key="_controller"> edit_article_controller:__invoke </default> </route> </routes> Pull request by Kevin Bond allows you to leave out the “:__invoke” part!
  • 71. Controller – Achievements ● Can be anywhere ● No need to follow naming conventions (“*Controller”, “*action”) ● Dependency injection, no service location ● Reusable in any application using HttpFoundation
  • 73. namespace MyBundleEntity; use DoctrineORMMapping as ORM; class Article { ... /** * @ORMColumn(type=”string”) */ private $title; } Entity conventions
  • 74. What's wrong with annotations? namespace MyBundleEntity; use DoctrineORMMapping as ORM; class Article { ... /** * @ORMColumn(type=”string”) */ private $title; }
  • 76. use DoctrineCommonAnnotationsAnnotationReader; $reader = new AnnotationReader(); $class = new ReflectionClass('Article'); $reader->getClassAnnotations($class); BANG Class DoctrineORMMappingColumn not found
  • 78. … are you ever going to use anything else than Doctrine ORM?”
  • 79. Well... Think about Doctrine MongoDB ODM, Doctrine CouchDB ODM, etc. namespace MyBundleEntity; use DoctrineORMMapping as ORM; use DoctrineODMMongoDBMappingAnnotations as MongoDB; use DoctrineODMCouchDBMappingAnnotations as CoucheDB; class Article { /** * @ORMColumn * @MognoDBField * @CoucheDBField */ private $title; }
  • 80. TODO ✔ Remove annotations ✔ Find another way to map the data
  • 81. namespace MyBundleEntity; class Article { private $id; private $title; } Nice and clean A true POPO, the ideal of the data mapper pattern
  • 82. 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>
  • 83. Conventions for XML metadata ● For MyBundleEntityArticle ● Put XML here: @MyBundle/Resources/config/doctrine/ Article.orm.xml
  • 84. We don't want it in the bundle! There's a nice little trick
  • 85. You need DoctrineBundle >=1.3 { "require": { ..., "doctrine/doctrine-bundle": "~1.3@dev" } }
  • 86. use DoctrineBundleDoctrineBundleDependencyInjectionCompiler DoctrineOrmMappingsPass; class MyBundle extends Bundle { 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) ); } }
  • 87. Now: ● For MyLibraryModelArticle ● Put XML here: src/MyLibrary/Doctrine/Article.orm.xml
  • 88. Entities - Achievements ● Entity classes can be anywhere ●Mapping metadata can be anywhere and in different formats ● Entities are true POPOs
  • 90. Conventions ● In /Resources/views/[Controller] ● Filename: [Action].[format].[engine]
  • 91. 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” %}
  • 92. Still, we want them out! And it's possible
  • 93. 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');
  • 94. Get rid of absolute paths Using Puli, created by Bernhard Schüssek (Symfony Forms, Validation)
  • 95. What Puli does Find the absolute paths of resources in a project
  • 96. use WebmozartPuliRepositoryResourceRepository; $repo = new ResourceRepository(); $repo->add('/my-library/views', '/absolute/path/to/views/*'); /my-library/views /index.html.twig /absolute/path/to/views /index.html.twig echo $repo ->get('/my-library/views/index.html.twig') ->getRealPath(); // => /absolute/path/to/views/index.html.twig
  • 97. 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" } } }
  • 98. Twig templates // in composer.json { "extra": { "resources": { "/my-library/views": "src/MyLibrary/Views" } } } Puli Twig extension // in the controller return $this->templating ->render('/my-library/views/index.html.twig');
  • 99. Many possibilities ● Templates ● Translation files ●Mapping metadata ● Service definitions ● And so on!
  • 100. The future is bright ● Puli is not stable yet ● But I expect much from it:
  • 101. Puli will be the ultimate tool
  • 102. to create NAKED BUNDLES
  • 103. and to enable reuse of many kinds of resources
  • 104. not limited by project, framework, even language boundaries!
  • 105. But even without Puli There's a whole lot you can do to make your code not rely on the framework
  • 106. Remember The framework is for you Your code doesn't need it
  • 108. Buy it for $ 17,50 http://leanpub.com/a-year-with-symfony/c/symfony-barcelona
  • 109. Get a $10,00 introduction discount: http://leanpub.com/principles-of-php-package-design/c/symfony-barcelona
  • 110. Thank you Talk to me: @matthiasnoback
  • 111. Images Sally MacKenzie: www.amazon.com