the naked bundle - tryout

104
The Naked Bundle Matthias Noback @matthiasnoback

Upload: matthiasnoback

Post on 02-Jul-2015

203 views

Category:

Technology


0 download

DESCRIPTION

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

TRANSCRIPT

Page 1: The Naked Bundle - Tryout

The Naked BundleMatthias Noback@matthiasnoback

Page 2: The Naked Bundle - Tryout

What is it, really?

Page 3: The Naked Bundle - Tryout

An actual naked bundle

Page 4: The Naked Bundle - Tryout

I could've called it

BundleLitetm

The No Code Bundle

The Clean Bundle

Page 5: The Naked Bundle - Tryout

But “naked” is catchy and controversial

Page 6: The Naked Bundle - Tryout

The official view on bundles

Page 7: The Naked Bundle - Tryout

First-class citizensDocumentation » The Quick Tour » The Architecture

Page 8: The Naked Bundle - Tryout

Importance

Your code is more important than the framework,

which is an implementation detail

Page 9: The Naked Bundle - Tryout

Reuse

Page 10: The Naked Bundle - Tryout

Nice!

Page 11: The Naked Bundle - Tryout

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

Page 12: The Naked Bundle - Tryout

Reuse

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

Page 13: The Naked Bundle - Tryout

Everything lives inside a bundleDocumentation » Glossary

Page 14: The Naked Bundle - Tryout

Not really true

Many things live inside libraries (including the Symfony components)

Page 15: The Naked Bundle - Tryout

Which is good!

Page 16: The Naked Bundle - Tryout

But you probably know that already

Page 17: The Naked Bundle - Tryout

“libraries first”

Page 18: The Naked Bundle - Tryout

What about...● Controllers

● Entities

● Templates

● ...

Page 19: The Naked Bundle - Tryout

They just need to be in a bundle

Or do they?

Page 20: The Naked Bundle - Tryout

Don't get me wrong

I love Symfony!

Page 21: The Naked Bundle - Tryout

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 22: The Naked Bundle - Tryout

A framework is there for you

Page 23: The Naked Bundle - Tryout

Your code doesn't need a framework

Page 24: The Naked Bundle - Tryout

Noback's Principle

Code shouldn't rely on something

it doesn't truly need

Page 25: The Naked Bundle - Tryout

Bundle conventions

Things in a bundle often rely on conventions to work

Page 26: The Naked Bundle - Tryout

Conventions aren't necessary at all

Page 27: The Naked Bundle - Tryout

Naming conventionsControllers:

● *Controller classes

● *action methods

Templates:

● in /Resources/views

● name: Controller/Action.html.twig

Page 28: The Naked Bundle - Tryout

Structural conventionsController:

● Extends framework Controller class

● Is ContainerAware

Page 29: The Naked Bundle - Tryout

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 30: The Naked Bundle - Tryout

These conventions are what makes an application a Symfony2 application

Page 31: The Naked Bundle - Tryout

A Year With Symfony

Page 32: The Naked Bundle - Tryout

About bundles

Page 33: The Naked Bundle - Tryout

A bundle exposes resources

Page 34: The Naked Bundle - Tryout

Resources● Service definitions

● Controllers

● Routes

● Templates

● Entities

● Form types

● Event listeners

● Translations

● ...

Page 35: The Naked Bundle - Tryout

No need for them to be inside a bundle

Page 36: The Naked Bundle - Tryout

When placed outside the bundlethe resources could be reused separately

Page 37: The Naked Bundle - Tryout

The bundle would be really small

Page 38: The Naked Bundle - Tryout

And could just as well be a:Laravel or CodeIgniter package,

Zend or Drupal module,CakePHP plugin,

...

Page 39: The Naked Bundle - Tryout

So the challenge is to

Make the bundle as clean as possible

Page 40: The Naked Bundle - Tryout

Move the “misplaced” things to● a library

● with dependencies

● but not symfony/framework­bundle ;)

Page 41: The Naked Bundle - Tryout

Being realistic

Practical reusability

Page 42: The Naked Bundle - Tryout

Reuse within the Symfony family

Think: Silex, Laravel, etc.

Page 43: The Naked Bundle - Tryout

Allowed dependency

HttpFoundation● Request● Response

● Exceptions● etc.

Page 44: The Naked Bundle - Tryout

What do we rely on

HttpKernelnamespace 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 45: The Naked Bundle - Tryout

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”

Page 46: The Naked Bundle - Tryout

And of course

Education

Page 47: The Naked Bundle - Tryout

You need a strong coupling radar

Page 48: The Naked Bundle - Tryout

Explicit dependencies● Function calls

● Imported classes (“use”)

● ...

Page 49: The Naked Bundle - Tryout

Implicit dependencies● File locations

● File names

● Method names

● ...

Page 50: The Naked Bundle - Tryout

There we go!

Page 51: The Naked Bundle - Tryout

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 52: The Naked Bundle - Tryout

TODO✔ Don't rely on things that may not

be there in another context:✔ Parent Controller class

✔ Routing, template, annotations, etc.

Page 53: The Naked Bundle - Tryout

class ArticleController{

function editAction(...){

...}

}

Nice and clean ;)

Page 54: The Naked Bundle - Tryout

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 55: The Naked Bundle - Tryout

TODO✔ Inject dependencies

✔ Don't use helper methods

✔ Render the template manually

✔ Keep using Request (not really a TODO)

Page 56: The Naked Bundle - Tryout

use Doctrine\ORM\EntityManager;

class ArticleController{

function __construct(EntityManager $em, 

) {$this­>em = $em;

}

...}

Inject dependencies

Page 57: The Naked Bundle - Tryout

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

class ArticleController{    ...

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

Page 58: The Naked Bundle - Tryout

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

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

Page 59: The Naked Bundle - Tryout

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 60: The Naked Bundle - Tryout

TODO✔ Set up routing

✔ Create a service and provide the right arguments

Page 61: The Naked Bundle - Tryout

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

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

<service id="article_controller"           class="MyBundle\Controller\ArticleController">    <argument type="service"              id="doctrine.orm.default_entity_manager" />    <argument type="service" id="mailer" />    <argument type="service" id="templating" /></service>

</services></container>

Page 62: The Naked Bundle - Tryout

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>

Page 63: The Naked Bundle - Tryout

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 64: The Naked Bundle - Tryout

Next up: Entities

Page 65: The Naked Bundle - Tryout

namespace My\Bundle\Entity;

use Doctrine\ORM\Mapping as ORM;

class Article{    ...

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

Page 66: The Naked Bundle - Tryout

Convention● They have to be in the /Entity

directory

● You define mapping metadata using annotations

Page 67: The Naked Bundle - Tryout

What's wrong with annotations?

Annotations are classes which need to be there

Page 68: The Naked Bundle - Tryout

namespace My\Bundle\Entity;

use Doctrine\ORM\Mapping as ORM;

class Article{    ...

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

Page 69: The Naked Bundle - Tryout

Well, uhm, yes, but...

Page 70: The Naked Bundle - Tryout

Are you ever going to replace your persistence library?

Page 71: The Naked Bundle - Tryout

There is no real alternative to Doctrine, right?

Page 72: The Naked Bundle - Tryout

Well...

Think of Doctrine MongoDB ODM, Doctrine CouchDB ODM, etc.

Page 73: The Naked Bundle - Tryout

TODO✔ Remove annotations

✔ Find another way to map the data

Page 74: The Naked Bundle - Tryout

namespace My\Bundle\Entity;

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

Nice and clean

A true POPO, the ideal of the data mapper pattern

Page 75: The Naked Bundle - Tryout

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 76: The Naked Bundle - Tryout

Conventions for XML metadata● For MyBundle\Entity\Article

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

Page 77: The Naked Bundle - Tryout

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

Page 78: The Naked Bundle - Tryout

You need DoctrineBundle >=1.2

{    "require": {        ...,        "doctrine/doctrine­bundle": "~1.2@dev"    }}

Page 79: The Naked Bundle - Tryout

use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\        DoctrineOrmMappingsPass;

class MyBundle{    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 80: The Naked Bundle - Tryout

Now:● For MyLibrary\Model\Article

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

Page 81: The Naked Bundle - Tryout

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

anywhere and in different formats● Entities are true POPOs

Page 82: The Naked Bundle - Tryout

Finally: Templates

Page 83: The Naked Bundle - Tryout

Conventions● In /Resources/views/[Controller]

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

Page 84: The Naked Bundle - Tryout

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 85: The Naked Bundle - Tryout

Still, we want them out!

And it's possible

Page 86: The Naked Bundle - Tryout

# 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 87: The Naked Bundle - Tryout

Get rid of absolute paths

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

Page 88: The Naked Bundle - Tryout

What Puli does

Find the absolute paths of resources in a project

Page 89: The Naked Bundle - Tryout

use Webmozart\Puli\Repository\ResourceRepository;

$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

Page 90: The Naked Bundle - Tryout

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 91: The Naked Bundle - Tryout

Puli – Twig extension// in composer.json{    "extra": {        "resources": {            "/my­library/views": "src/MyLibrary/Views"        }    }}

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

Page 92: The Naked Bundle - Tryout

Many possibilities● Templates

● Translation files

● Mapping metadata

● Service definitions

● ...

Page 93: The Naked Bundle - Tryout

The future is bright● Puli is not stable yet

● But I expect much from it:

Page 94: The Naked Bundle - Tryout

Puli will be the ultimate tool

Page 95: The Naked Bundle - Tryout

to create NAKED BUNDLES

Page 96: The Naked Bundle - Tryout

and to enable reuse of many kinds of resources

Page 97: The Naked Bundle - Tryout

cross package!cross application!cross framework!

Page 98: The Naked Bundle - Tryout

But even without Puli

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

Page 99: The Naked Bundle - Tryout

Remember

The framework is for youYour code doesn't need it

Page 100: The Naked Bundle - Tryout

Questions?

Page 101: The Naked Bundle - Tryout

Get a $7,50 discount:http://leanpub.com/a-year-with-symfony/c/SymfonyLiveLondon2014

Page 102: The Naked Bundle - Tryout

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

Page 103: The Naked Bundle - Tryout

Thank you

Feedback: joind.in/11553

Talk to me: @matthiasnoback

Page 104: The Naked Bundle - Tryout

Images● Sally MacKenzie:

www.amazon.com