+ All Categories
Home > Technology > The Naked Bundle - Symfony Barcelona

The Naked Bundle - Symfony Barcelona

Date post: 02-Jul-2015
Category:
Upload: matthiasnoback
View: 1,511 times
Download: 4 times
Share this document with a friend
Description:
The Bundle system is one of the greatest and most powerful features of Symfony2. Bundles contain all the files related to a single feature of your application: controllers, entities, event listeners, form types, Twig templates, etc. But how much of that actually needs to be inside a bundle? In this talk we’ll take a bundle, containing all those different types of classes, configuration files and templates, and strip it down to the bare necessities. And I promise that after moving many files out of the bundle, everything still works. While looking for ways to move things out of the bundle, I will discuss some of the more advanced features of bundle design, like prepending configuration, compiler passes and Doctrine mapping drivers. We will end with a very lean bundle, surrounded by a few highly reusable, maximally decoupled libraries.
111
The Naked Bundle Matthias Noback @matthiasnoback Symfony Barcelona October 24 th , 2014
Transcript
Page 1: The Naked Bundle - Symfony Barcelona

The Naked Bundle

Matthias Noback@matthiasnoback

Symfony BarcelonaOctober 24th, 2014

Page 2: The Naked Bundle - Symfony Barcelona

What's it all about?

Page 3: The Naked Bundle - Symfony Barcelona

An actual naked bundle

Page 4: The Naked Bundle - Symfony Barcelona

I could've called it

BundleLitetm

The No Code Bundle

The Clean Bundle

Page 5: The Naked Bundle - Symfony Barcelona

But “naked” was catchy and controversial

Page 6: The Naked Bundle - Symfony Barcelona

Until Pierre came along

Page 7: The Naked Bundle - Symfony Barcelona

The official view on bundles

Page 8: The Naked Bundle - Symfony Barcelona

First-class citizensDocumentation » The Quick Tour » The Architecture

Page 9: The Naked Bundle - Symfony Barcelona

Importance

Your code is more important than the framework,

which is an implementation detail

Page 10: The Naked Bundle - Symfony Barcelona

Reuse

Page 11: The Naked Bundle - Symfony Barcelona

Nice!

Page 12: The Naked Bundle - Symfony Barcelona

All your code lives in a bundleDocumentation » The Book » Creating Pages in Symfony2

Page 13: The Naked Bundle - Symfony Barcelona

Reuse

“All your code in a bundle” contradicts the promise of reuse

Page 14: The Naked Bundle - Symfony Barcelona

Everything lives inside a bundleDocumentation » Glossary

Page 15: The Naked Bundle - Symfony Barcelona

Not really true

Many things live inside libraries

(the Symfony components are libraries too!)

Page 16: The Naked Bundle - Symfony Barcelona

Which is good!

Page 17: The Naked Bundle - Symfony Barcelona

But you probably know that already

Page 18: The Naked Bundle - Symfony Barcelona

“libraries first”

Page 19: The Naked Bundle - Symfony Barcelona

What about...● Controllers

● Entities

● Templates

● ...

Page 20: The Naked Bundle - Symfony Barcelona

They just need to be in a bundle

Or do they?

Page 21: The Naked Bundle - Symfony Barcelona

Don't get me wrong

I love Symfony!

Page 22: The Naked Bundle - Symfony Barcelona

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

Page 23: The Naked Bundle - Symfony Barcelona

A framework is there for you

Page 24: The Naked Bundle - Symfony Barcelona

Your code doesn't need a framework

Page 25: The Naked Bundle - Symfony Barcelona

Noback's Principle

Code shouldn't rely on something

it doesn't truly need

Page 26: The Naked Bundle - Symfony Barcelona

Bundle conventions

Things in a bundle often rely on conventions to work

Page 27: The Naked Bundle - Symfony Barcelona

Conventions aren't necessary at all

Page 28: The Naked Bundle - Symfony Barcelona

So according to Noback's Principle,code shouldn't rely on bundle conventions too

Page 29: The Naked Bundle - Symfony Barcelona

Naming conventionsControllers:

● *Controller classes

● *action methods

Templates:

● in /Resources/views

● name: Controller/Action.html.twig

Page 30: The Naked Bundle - Symfony Barcelona

Structural conventionsController:

● Extends framework Controller class

● Is ContainerAware

Page 31: The Naked Bundle - Symfony Barcelona

Behavioral conventionsController:

● Is allowed to return an array

● Actions can type-hint to objects which will be fetched based on route parameters (??)

Page 32: The Naked Bundle - Symfony Barcelona

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){}

Page 33: The Naked Bundle - Symfony Barcelona

These conventions are what makes an application a Symfony2 application

Page 34: The Naked Bundle - Symfony Barcelona
Page 35: The Naked Bundle - Symfony Barcelona

About bundles

Page 36: The Naked Bundle - Symfony Barcelona

A bundle exposes resources

Page 37: The Naked Bundle - Symfony Barcelona

Resources● Service definitions

● Controllers

● Routes

● Templates

● Entities

● Form types

● Event listeners

● Translations

● ...

Page 38: The Naked Bundle - Symfony Barcelona

No need for them to be inside a bundle

Page 39: The Naked Bundle - Symfony Barcelona

When placed outside the bundlethe resources could be reused separately

Page 40: The Naked Bundle - Symfony Barcelona

The bundle would be really small

Page 41: The Naked Bundle - Symfony Barcelona

And could just as well be a:Laravel package,

Zend or Drupal module,CakePHP plugin,

...

Page 42: The Naked Bundle - Symfony Barcelona

So the challenge is toMake the bundle as clean as possible

Page 43: The Naked Bundle - Symfony Barcelona

Move the “misplaced” things to● a library

● with dependencies

● but not symfony/framework-bundle ;)

Page 44: The Naked Bundle - Symfony Barcelona

Being realistic

Practical reusability

Page 45: The Naked Bundle - Symfony Barcelona

Reuse within the Symfony family

Think: Silex, Laravel, etc.

Page 46: The Naked Bundle - Symfony Barcelona

Allowed dependency

HttpFoundation● Request

● Response

● Exceptions

● etc.

Page 47: The Naked Bundle - Symfony Barcelona

What do we rely on

HttpKernel

namespace Symfony\Component\HttpKernel;

use Symfony\Component\HttpFoundation\Request;use Symfony\Component\HttpFoundation\Response;

interface HttpKernelInterface{ /** * Handles a Request to convert it to a Response. */ public function handle(Request $request, ...);}

Page 48: The Naked Bundle - Symfony Barcelona

Why? My secret missions

“Let's rebuild the application, but this time we use Zend4 instead of Symfony2”

Page 49: The Naked Bundle - Symfony Barcelona

And of course

Education

Page 50: The Naked Bundle - Symfony Barcelona

You need a strong coupling radar

Page 51: The Naked Bundle - Symfony Barcelona

Explicit dependencies● Function calls

● Imported classes (“use”)

● Included files

● ...

Page 52: The Naked Bundle - Symfony Barcelona

Implicit dependencies● File locations

● File, class, method names

● Structure of return values

● ...

Page 53: The Naked Bundle - Symfony Barcelona

There we go!

Page 54: The Naked Bundle - Symfony Barcelona

use Symfony\Bundle\FrameworkBundle\Controller\Controller;use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;

/** * @Route(“/article”) */class ArticleController extends Controller{

/** * @Route(“/edit”) * @Template() */function editAction(...){

...}

}

Controller

Page 55: The Naked Bundle - Symfony Barcelona

TODO✔ Don't rely on things that may not

be there in another context:✔ Parent Controller class

✔ Routing, template, annotations, etc.

Page 56: The Naked Bundle - Symfony Barcelona

class ArticleController{

function editAction(...){

...}

}

Nice and clean ;)

Page 57: The Naked Bundle - Symfony Barcelona

use Symfony\Component\HttpFoundation\Request;

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

Page 58: The Naked Bundle - Symfony Barcelona

TODO✔ Inject dependencies

✔ Don't use helper methods

✔ Render the template manually

✔ Keep using Request (not really a TODO)

Page 59: The Naked Bundle - Symfony Barcelona

use Doctrine\ORM\EntityManager;

class ArticleController{

function __construct(EntityManager $em,

) {$this->em = $em;

}

...}

Inject dependencies

Page 60: The Naked Bundle - Symfony Barcelona

Inline helper methodsuse Symfony\Component\HttpKernel\Exception\NotFoundHttpException

class ArticleController{ ...

public function editAction(...) { ... throw new NotFoundHttpException(); ... }}

Page 61: The Naked Bundle - Symfony Barcelona

use Symfony\Component\HttpFoundation\Response;use Symfony\Component\Templating\EngineInterface;

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

Page 62: The Naked Bundle - Symfony Barcelona

Dependencies are explicit now

use Symfony\Component\HttpFoundation\Request;use Symfony\Component\HttpFoundation\Response;use Symfony\Component\Templating\EngineInterface;use Doctrine\ORM\EntityManager;

Also: no mention of a “bundle” anywhere!

Page 63: The Naked Bundle - Symfony Barcelona

Dependency overflowuse Symfony\Component\HttpFoundation\Response;use Symfony\Component\Templating\EngineInterface;

class ArticleController{

function __construct(EntityManager $entityManager,EngineInterface $templating,TranslatorInterface $translator,ValidatorInterface $validator,Swift_Mailer $mailer,RouterInterface $router

) {...

}

...}

Page 64: The Naked Bundle - Symfony Barcelona

The cause?

Convention

Page 65: The Naked Bundle - Symfony Barcelona

One controller, many actions

one action!

Page 66: The Naked Bundle - Symfony Barcelona

__invoke()

namespace MyLibrary\Controller\Article;

class Edit{

function __construct(...) {

// only what's necessary for this “action”}

public function __invoke(...){

...}

}

Page 67: The Naked Bundle - Symfony Barcelona

Nice!

└─Controller └─Article ├─Create.php ├─Edit.php ├─Archive.php └─Delete.php

Page 68: The Naked Bundle - Symfony Barcelona

TODO✔ Set up routing

✔ Create a service and provide the right arguments

Page 69: The Naked Bundle - Symfony Barcelona

Bundle stuff: services.xml<!-- in MyBundle/Resources/config/services.xml →

<?xml version="1.0" ?><container><services>

<service id="edit_article_controller" class="MyBundle\Controller\Article\Edit"> <argument type="service" id="doctrine.orm.default_entity_manager" /> <argument type="service" id="templating" /></service>

</services></container>

Page 70: The Naked Bundle - Symfony Barcelona

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!

Page 71: The Naked Bundle - Symfony Barcelona

Controller – Achievements● Can be anywhere

● No need to follow naming conventions (“*Controller”, “*action”)

● Dependency injection, no service location

● Reusable in any application using HttpFoundation

Page 72: The Naked Bundle - Symfony Barcelona

Next up: Entities

Page 73: The Naked Bundle - Symfony Barcelona

namespace My\Bundle\Entity;

use Doctrine\ORM\Mapping as ORM;

class Article{ ...

/** * @ORM\Column(type=”string”) */ private $title;}

Entity conventions

Page 74: The Naked Bundle - Symfony Barcelona

What's wrong with annotations?

namespace My\Bundle\Entity;

use Doctrine\ORM\Mapping as ORM;

class Article{ ...

/** * @ORM\Column(type=”string”) */ private $title;}

Page 75: The Naked Bundle - Symfony Barcelona

Annotations are classes

Page 76: The Naked Bundle - Symfony Barcelona

use Doctrine\Common\Annotations\AnnotationReader;

$reader = new AnnotationReader();

$class = new \ReflectionClass('Article');

$reader->getClassAnnotations($class);

BANG

Class Doctrine\ORM\Mapping\Column

not found

Page 77: The Naked Bundle - Symfony Barcelona

“Well, uhm, yes, but...

Page 78: The Naked Bundle - Symfony Barcelona

… are you ever going to use anything else than Doctrine ORM?”

Page 79: The Naked Bundle - Symfony Barcelona

Well...Think about Doctrine MongoDB ODM, Doctrine CouchDB ODM, etc.namespace My\Bundle\Entity;

use Doctrine\ORM\Mapping as ORM;use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;use Doctrine\ODM\CouchDB\Mapping\Annotations as CoucheDB;

class Article{ /** * @ORM\Column * @MognoDB\Field * @CoucheDB\Field */ private $title;}

Page 80: The Naked Bundle - Symfony Barcelona

TODO✔ Remove annotations

✔ Find another way to map the data

Page 81: The Naked Bundle - Symfony Barcelona

namespace My\Bundle\Entity;

class Article{ private $id; private $title;}

Nice and clean

A true POPO, the ideal of the data mapper pattern

Page 82: The Naked Bundle - Symfony Barcelona

Use XML for mapping metadata<doctrine-mapping>

<entity name=”My\Bundle\Entity\Article”> <id name="id" type="integer" column="id"> <generator strategy="AUTO"/> </id> <field name=”title” type=”string”></entity> </doctrine-mapping>

Page 83: The Naked Bundle - Symfony Barcelona

Conventions for XML metadata● For MyBundle\Entity\Article

● Put XML here: @MyBundle/Resources/config/doctrine/ Article.orm.xml

Page 84: The Naked Bundle - Symfony Barcelona

We don't want it in the bundle!There's a nice little trick

Page 85: The Naked Bundle - Symfony Barcelona

You need DoctrineBundle >=1.3

{ "require": { ..., "doctrine/doctrine-bundle": "~1.3@dev" }}

Page 86: The Naked Bundle - Symfony Barcelona

use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ 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 = 'MyLibrary\Model';

return DoctrineOrmMappingsPass::createXmlMappingDriver( array($xmlPath => $namespacePrefix) ); }}

Page 87: The Naked Bundle - Symfony Barcelona

Now:● For MyLibrary\Model\Article

● Put XML here: src/MyLibrary/Doctrine/Article.orm.xml

Page 88: The Naked Bundle - Symfony Barcelona

Entities - Achievements● Entity classes can be anywhere● Mapping metadata can be

anywhere and in different formats● Entities are true POPOs

Page 89: The Naked Bundle - Symfony Barcelona

Finally: Templates

Page 90: The Naked Bundle - Symfony Barcelona

Conventions● In /Resources/views/[Controller]

● Filename: [Action].[format].[engine]

Page 91: The Naked Bundle - Symfony Barcelona

The difficulty with templatesThey 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” %}

Page 92: The Naked Bundle - Symfony Barcelona

Still, we want them out!

And it's possible

Page 93: The Naked Bundle - Symfony Barcelona

# in config.ymltwig: ... paths: "%kernel.root_dir%/../src/MyLibrary/Views": MyLibrary

Twig namespacesDocumentation » The Cookbook » Templating » How to use and Register namespaced Twig Paths

// in the controllerreturn $this->templating->render('@MyLibrary/Template.html.twig');

Page 94: The Naked Bundle - Symfony Barcelona

Get rid of absolute paths

Using Puli, created by Bernhard Schüssek (Symfony Forms, Validation)

Page 95: The Naked Bundle - Symfony Barcelona

What Puli does

Find the absolute paths of resources in a project

Page 96: The Naked Bundle - Symfony Barcelona

use Webmozart\Puli\Repository\ResourceRepository;

$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

Page 97: The Naked Bundle - Symfony Barcelona

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" } }}

Page 98: The Naked Bundle - Symfony Barcelona

Twig templates// in composer.json{ "extra": { "resources": { "/my-library/views": "src/MyLibrary/Views" } }}

// in the controllerreturn $this->templating ->render('/my-library/views/index.html.twig');

Puli Twig extension

Page 99: The Naked Bundle - Symfony Barcelona

Many possibilities● Templates

● Translation files

● Mapping metadata

● Service definitions

● And so on!

Page 100: The Naked Bundle - Symfony Barcelona

The future is bright● Puli is not stable yet

● But I expect much from it:

Page 101: The Naked Bundle - Symfony Barcelona

Puli will be the ultimate tool

Page 102: The Naked Bundle - Symfony Barcelona

to create NAKED BUNDLES

Page 103: The Naked Bundle - Symfony Barcelona

and to enable reuse of many kinds of resources

Page 104: The Naked Bundle - Symfony Barcelona

not limited byproject,

framework,even language

boundaries!

Page 105: The Naked Bundle - Symfony Barcelona

But even without Puli

There's a whole lot you can do to make your code not rely on the framework

Page 106: The Naked Bundle - Symfony Barcelona

Remember

The framework is for youYour code doesn't need it

Page 107: The Naked Bundle - Symfony Barcelona

Questions?

Page 108: The Naked Bundle - Symfony Barcelona

Buy it for $ 17,50http://leanpub.com/a-year-with-symfony/c/symfony-barcelona

Page 109: The Naked Bundle - Symfony Barcelona

Get a $10,00 introduction discount:http://leanpub.com/principles-of-php-package-design/c/symfony-barcelona

Page 110: The Naked Bundle - Symfony Barcelona

Thank you

Talk to me: @matthiasnoback


Recommended