Symfony 2 Building on Alpha / Beta Technologies Daniel Knell
WHY?
PHP5.3
Closures $count = 0; $add = function($value) use ($count) { $count += $value; } $add(5); echo $count; // outputs 5
Namespaces $worker = new Artisan_SomeModule_Foo_Bar(); // vs use ArtisanSomeModuleFooBar; // ... $bar = new Bar();
Invoke class Slug { private $regexp; public function __construct($regexp) { $this->regexp = $regexp; } public function __invoke($value) { return strtolower(preg_replace($this->regexp, "-", $value)); } } $filter = new Slug("#^[^A-Z0-9]$#i"); echo $filter("Hello World"); // outputs hello-world
Late Static Binding abstract class Singleton { protected static $instance; protected function __construct() { } final public static function getInstance() { if (null === static::$instance) { static::$instance = new static(); } return static::$instance; } final private function __clone() { } }
Shiny
Best Frameworks Limited to 5.2
Existing 5.3 Frameworks?
Symfony 2
From Good Stock
BUT! No stable release
Crunch Time
WHAT?
Bundles
Reusability
symfony2bundles.org
Twig
Django Inspired
Simple <html> <body> {% if name is defined %} <p>Hello {{ name }}!</p> {% else %} <p>Hello World!</p> {% endif %} </html> </body>
Cool Stuff {% extends "layout.html" %} {% block content %} <ul id="navigation"> {% for item in navigation %} <li><a href="{{ item.href }}">{{ item.caption }}</a></li> {% endfor %} </ul> <h1>My Webpage</h1> {{ a_variable }} {% endblock %}
Twig namespace ArtisanFooBundleTwigExtension; class TextExtension extends Twig_Extension { public function getFilters() { return array( 'upper' => new Twig_Filter_Method($this, 'upper'), ); } public function upper($value) { return strtoupper($value); } public function getName() { return 'text'; } }
Doctrine2
Standard Classes class Widget { protected $id; protected $name; }
Give Me My Constructor Back class Widget { protected $id; protected $name; public function __construct($id, $name) { $this->id = $id; $this->name = $name; } }
YAML ArtisanWidgetBundleEntityWidget: type: entity table: widget id: id: type: integer fields: name: type: string length: 100
XML <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine- mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine- mapping http://doctrine-project.org/schemas/orm/doctrine- mapping.xsd"> <entity name="ArtisanWidgetBundleEntityWidget" table="product"> <id name="id" type="integer" column="id" /> <field name="name" column="name" type="string" length="100" /> </entity> </doctrine-mapping>
Annotations /** * @ORMEntity * @ORMTable(name="widget") */ class Widget { /** * @ORMId * @ORMColumn(type="integer") */ protected $id; /** * @ORMColumn(type="string", length=100) */ protected $name; }
Controllers namespace ArtisanHelloBundleController; use SymfonyComponentHttpFoundationResponse; class HelloController { public function indexAction($name) { return new Response('<html><body>Hello '.$name.'!</body></html>'); } }
Controllers namespace ArtisanHelloBundleController; use SymfonyBundleFrameworkBundleControllerController; class HelloController extends Controller { public function indexAction($name) { return $this->render(array(“name” => $name)); } }
Render Helper namespace ArtisanHelloBundleController; use SymfonyBundleFrameworkBundleControllerController; class HelloController extends Controller { public function indexAction($name) { return $this->render('HelloBundle:hello:index.html.twig', array( 'name' => $name )); } }
Framework Extras namespace ArtisanHelloBundleController; use SymfonyComponentHttpFoundationResponse; class HelloController { /** * @Route("/{name}") * @Template */ public function indexAction($name) { return array('name' => $name); } }
Template Annotation namespace ArtisanHelloBundleController; use SymfonyComponentHttpFoundationResponse; class HelloController { /** * @Route("/{name}") * @Template */ public function indexAction($name) { return array('name' => $name); } }
Console Interface
Console Extension namespace ArtisanHelloBundleCommand; class HelloCommand extends ContainerAwareCommand { protected function configure() { $this->addArgument('name', InputArgument::OPTIONAL, 'Who?') ->setName('demo:greet') ->setDescription('Greet someone'); } protected function execute(InputInterface $input, OutputInterface $output) { $name = $input->getArgument('name'); if (null === $name) { $name = 'World'; } $output->writeln('Hello ' . $text . '!'); } }
Validation
Validation namespace ArtisanWidgetBundleController; use SymfonyBundleFrameworkBundleControllerController; class WidgetController extends Controller { public function indexAction($name) { $widget = new Widget(); // do something with the widget... $errors = $this->get("validator")->validate($widget); return $this->render('HelloBundle:hello:index.html.twig', array( "errors" => $errors )); } }
YAML ArtisanWidgetBundleEntityWidget: properties: id: - Type: { type: integer } name: - NotBlank: ~ - Type: { type: string } - MaxLength: 100
XML <class name="ArtisanWidgetBundleEntityWidget"> <property name="id"> <constraint name="Type"> <option name="type">integer</option> </constraint> <constraint name="MinLength">3</constraint> </property> <property name="id"> <constraint name="NotBlank" /> <constraint name="Type"> <option name="type">string</option> </constraint> <constraint name="MinLength">100</constraint> </property> </class>
Annotations class Widget { /** * @AssertType(type="integer") */ protected $id; /** * @AssertNotBlank() * @AssertType(type="string") * @AssertMaxLength(100) */ protected $name; }
Forms
Forms public function newAction() { $widget = new Widget(); $form = $this->createFormBuilder($widget) ->add('name', 'text') ->getForm(); if ($request->getMethod() == 'POST') { $form->bindRequest($request); if ($form->isValid()) { // do something to save widget... return $this->redirect($this->generateUrl('widget_success')); } } return $this->render('WidgetBundle:widget:new.html.twig', array( "errors" => $errors )); }
Twig Intergration <form action="{{ path('widget_new') }}" method="post" {{ form_enctype(form) }}> {{ form_widget(form) }} <input type="submit" /> </form>
Security
Debugging
Web Profiler
Service Container parameters: widget_manager.class: ArtisanWidgetBundleWidgetWidgetManager services: widget_factory: # ... widget_manager: class: %widget_manager.class% arguments: [@widget_factory]
HOW?
Two Strategies
Snapshot
One Off
Update Often
Every Morning
Difficulty Increases Exponentially With Time
Wait
Downside
All Things In Time
Thats All Folks email: contact@danielknell.co.uk twitter: @danielknell website: http://danielknell.co.uk/ Any Questions?

Symfony2 Building on Alpha / Beta technology

  • 1.
    Symfony 2 Building onAlpha / Beta Technologies Daniel Knell
  • 2.
  • 3.
  • 4.
    Closures $count = 0; $add= function($value) use ($count) { $count += $value; } $add(5); echo $count; // outputs 5
  • 5.
    Namespaces $worker = newArtisan_SomeModule_Foo_Bar(); // vs use ArtisanSomeModuleFooBar; // ... $bar = new Bar();
  • 6.
    Invoke class Slug { private $regexp; public function __construct($regexp) { $this->regexp = $regexp; } public function __invoke($value) { return strtolower(preg_replace($this->regexp, "-", $value)); } } $filter = new Slug("#^[^A-Z0-9]$#i"); echo $filter("Hello World"); // outputs hello-world
  • 7.
    Late Static Binding abstract class Singleton { protected static $instance; protected function __construct() { } final public static function getInstance() { if (null === static::$instance) { static::$instance = new static(); } return static::$instance; } final private function __clone() { } }
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
    Simple <html> <body> {% if nameis defined %} <p>Hello {{ name }}!</p> {% else %} <p>Hello World!</p> {% endif %} </html> </body>
  • 22.
    Cool Stuff {% extends"layout.html" %} {% block content %} <ul id="navigation"> {% for item in navigation %} <li><a href="{{ item.href }}">{{ item.caption }}</a></li> {% endfor %} </ul> <h1>My Webpage</h1> {{ a_variable }} {% endblock %}
  • 23.
    Twig namespace ArtisanFooBundleTwigExtension; class TextExtensionextends Twig_Extension { public function getFilters() { return array( 'upper' => new Twig_Filter_Method($this, 'upper'), ); } public function upper($value) { return strtoupper($value); } public function getName() { return 'text'; } }
  • 24.
  • 25.
    Standard Classes class Widget { protected $id; protected $name; }
  • 26.
    Give Me My Constructor Back class Widget { protected $id; protected $name; public function __construct($id, $name) { $this->id = $id; $this->name = $name; } }
  • 27.
    YAML ArtisanWidgetBundleEntityWidget: type: entity table: widget id: id: type: integer fields: name: type: string length: 100
  • 28.
    XML <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine- mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine- mapping http://doctrine-project.org/schemas/orm/doctrine- mapping.xsd"> <entity name="ArtisanWidgetBundleEntityWidget" table="product"> <id name="id" type="integer" column="id" /> <field name="name" column="name" type="string" length="100" /> </entity> </doctrine-mapping>
  • 29.
    Annotations /** *@ORMEntity * @ORMTable(name="widget") */ class Widget { /** * @ORMId * @ORMColumn(type="integer") */ protected $id; /** * @ORMColumn(type="string", length=100) */ protected $name; }
  • 30.
    Controllers namespace ArtisanHelloBundleController; use SymfonyComponentHttpFoundationResponse; classHelloController { public function indexAction($name) { return new Response('<html><body>Hello '.$name.'!</body></html>'); } }
  • 31.
    Controllers namespace ArtisanHelloBundleController; use SymfonyBundleFrameworkBundleControllerController; classHelloController extends Controller { public function indexAction($name) { return $this->render(array(“name” => $name)); } }
  • 32.
    Render Helper namespace ArtisanHelloBundleController; useSymfonyBundleFrameworkBundleControllerController; class HelloController extends Controller { public function indexAction($name) { return $this->render('HelloBundle:hello:index.html.twig', array( 'name' => $name )); } }
  • 33.
    Framework Extras namespace ArtisanHelloBundleController; useSymfonyComponentHttpFoundationResponse; class HelloController { /** * @Route("/{name}") * @Template */ public function indexAction($name) { return array('name' => $name); } }
  • 34.
    Template Annotation namespace ArtisanHelloBundleController; use SymfonyComponentHttpFoundationResponse; class HelloController { /** * @Route("/{name}") * @Template */ public function indexAction($name) { return array('name' => $name); } }
  • 35.
  • 36.
    Console Extension namespace ArtisanHelloBundleCommand; classHelloCommand extends ContainerAwareCommand { protected function configure() { $this->addArgument('name', InputArgument::OPTIONAL, 'Who?') ->setName('demo:greet') ->setDescription('Greet someone'); } protected function execute(InputInterface $input, OutputInterface $output) { $name = $input->getArgument('name'); if (null === $name) { $name = 'World'; } $output->writeln('Hello ' . $text . '!'); } }
  • 37.
  • 38.
    Validation namespace ArtisanWidgetBundleController; use SymfonyBundleFrameworkBundleControllerController; class WidgetController extends Controller { public function indexAction($name) { $widget = new Widget(); // do something with the widget... $errors = $this->get("validator")->validate($widget); return $this->render('HelloBundle:hello:index.html.twig', array( "errors" => $errors )); } }
  • 39.
    YAML ArtisanWidgetBundleEntityWidget: properties: id: - Type: { type: integer } name: - NotBlank: ~ - Type: { type: string } - MaxLength: 100
  • 40.
    XML <class name="ArtisanWidgetBundleEntityWidget"> <property name="id"> <constraint name="Type"> <option name="type">integer</option> </constraint> <constraint name="MinLength">3</constraint> </property> <property name="id"> <constraint name="NotBlank" /> <constraint name="Type"> <option name="type">string</option> </constraint> <constraint name="MinLength">100</constraint> </property> </class>
  • 41.
    Annotations class Widget { /** * @AssertType(type="integer") */ protected $id; /** * @AssertNotBlank() * @AssertType(type="string") * @AssertMaxLength(100) */ protected $name; }
  • 42.
  • 43.
    Forms public function newAction() { $widget = new Widget(); $form = $this->createFormBuilder($widget) ->add('name', 'text') ->getForm(); if ($request->getMethod() == 'POST') { $form->bindRequest($request); if ($form->isValid()) { // do something to save widget... return $this->redirect($this->generateUrl('widget_success')); } } return $this->render('WidgetBundle:widget:new.html.twig', array( "errors" => $errors )); }
  • 44.
    Twig Intergration <form action="{{ path('widget_new') }}" method="post" {{ form_enctype(form) }}> {{ form_widget(form) }} <input type="submit" /> </form>
  • 45.
  • 46.
  • 47.
  • 48.
    Service Container parameters: widget_manager.class: ArtisanWidgetBundleWidgetWidgetManager services: widget_factory: # ... widget_manager: class: %widget_manager.class% arguments: [@widget_factory]
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
    Difficulty Increases Exponentially With Time
  • 56.
  • 57.
  • 58.
  • 59.
    Thats All Folks email:contact@danielknell.co.uk twitter: @danielknell website: http://danielknell.co.uk/ Any Questions?