Slide 1

Slide 1 text

Symfony2 - From the Trenches by Lukas Kahwe Smith, Kris Wallsmith, Thibault Duplessis, Jeremy Mikola, Jordi Boggiano, Jonathan H. Wage, Bulat Shakirzyanov

Slide 2

Slide 2 text

Agenda Getting Setup Code Flow Dependency Injection Configuration Choices Controller Choices Application Choices Doctrine Caching Performance Tips Asset Management Testing Deployment Third Party Bundles Resources

Slide 3

Slide 3 text

Getting setup PHP 5.3+ with ext/intl (compat lib is in the works) Read check.php for details (dev/prod php.ini's from Liip) Using OSX? php53-intl from liangzhenjing or build-entropy-php from chregu Blog post on installing PHP 5.3 with intl from Justin Hileman Initial setup symfony-sandbox symfony-bootstrap Symfony2Project Read the Coding Style Guide (Code Sniffer Rules) Managing external dependencies Submodule: not everything is in git (svn, mercurial, etc.) Vendor install/update scripts: risk of getting out of sync MR (not cross platform)

Slide 4

Slide 4 text

Code Flow 1. Frontend Controller (web/app[_dev].php) Loads autoloader Creates/boots kernel Creates request (from globals) and passes to kernel 2. Kernel Loads app config (app/config/config_[prod|dev|test]) Resolves URL path to a controller (go to 3) Outputs response returned by the controller 3. Controller Loads model and view Potentially creates a sub-request (go to 2) Creates response and returns it

Slide 5

Slide 5 text

Execution Flow

Slide 6

Slide 6 text

Describe Your Services Bundle\Avalanche\SitemapBundle\Sitemap

Slide 7

Slide 7 text

Service Definitions Are Dumped to Raw PHP services['sitemap'] = new \Bundle\Avalanche\SitemapBundle\Sitemap(); } /** * Gets the default parameters. * * @return array An array of the default parameters */ protected function getDefaultParameters() { return array( 'sitemap.class' => 'Bundle\Avalanche\SitemapBundle\Sitemap' ); } }

Slide 8

Slide 8 text

Service Container (aka DIC) Benefits: No performance loss Lazy instantiation Readable service configurations Gotchas: Can become hard to work with if the DI extension tries to do too much Be aware of circular dependencies Might lead to code that cannot be used outside of DIC

Slide 9

Slide 9 text

Container Injection container = $container; } // or public function setContainer(ContainerInterface $container) { $this->container = $container; } public function getDocumentManager() { return $this->container->get('document_manager'); } }

Slide 10

Slide 10 text

Constructor Injection documentManager = $documentManager; } public function getDocumentManager() { return $this->documentManager; } }

Slide 11

Slide 11 text

Setter Injection documentManager = $documentManager; } public function getDocumentManager() { return $this->documentManager; } }

Slide 12

Slide 12 text

Interface Injection documentManager = $documentManager; } public function getDocumentManager() { return $this->documentManager; } }

Slide 13

Slide 13 text

Configuration Choices Symfony supports XML, YAML and PHP for configuration YAML and PHP uses underscore to separate words XML uses dashes to separate words XML attributes usually map to array values in YAML/PHP YAML merge key syntax to reuse pieces within a file XSD-aware editors provide auto-completion/validation XML is recommended for Bundle/DI configuration YAML is recommended for application configuration Bundle extensions can optionally utilize the Config component to normalize/parse configurations in any format See FrameworkExtension, SecurityExtension, TwigExtension

Slide 14

Slide 14 text

Controller Choices Defining Controllers as services is optional Non-service controllers must use container injection Create a Bundle extension to load Bundle services It's recommended to not extend from the base Controller The base controller is mainly a tool for beginners It provides convenience methods that invoke services, such as generateUrl(), redirect(), render()

Slide 15

Slide 15 text

Application Choices Security system makes it possible to have just one application for both frontend and admin backend Location of AppKernel is totally flexible, just update the frontend controllers accordingly Large projects should use multiple applications Better separation when multiple teams work Facilitate step-by-step updating and refactoring For example: main, mobile, API, admin

Slide 16

Slide 16 text

Doctrine Examples Retrieve references to entity/document without DB queries Using raw SQL queries with Doctrine2 ORM Simple search engine with Doctrine MongoDB ODM

Slide 17

Slide 17 text

Retrieving References w/o DB Queries $tags = array('baseball', 'basketball'); foreach ($tags as $tag) { $product->addTag($em->getReference('Tag', $tag)); }

Slide 18

Slide 18 text

Raw SQL Queries $rsm = new ResultSetMapping; $rsm->addEntityResult('User', 'u'); $rsm->addFieldResult('u', 'id', 'id'); $rsm->addFieldResult('u', 'name', 'name'); $query = $this->_em->createNativeQuery('SELECT id, name FROM users WHERE name = ?', $rsm); $query->setParameter(1, 'romanb'); $users = $query->getResult(); http://www.doctrine-project.org/docs/orm/2.0/en/reference/native-sql.html

Slide 19

Slide 19 text

Simple Search Engine interface HasKeywords { function setKeywords(); function getKeywords(); }

Slide 20

Slide 20 text

Simple Search Engine /** @mongodb:EmbeddedDocument */ class Keyword { // ... /** @mongodb:String @mongodb:Index */ private $keyword; /** @mongodb:Integer */ private $score; // ... }

Slide 21

Slide 21 text

Simple Search Engine /** @mongodb:Document */ class Product implements HasKeywords { /** @mongodb:Id */ private $id; /** @mongodb:String */ private $title; /** @mongodb:EmbedMany(targetDocument="Keyword") */ private $keywords = array(); // ... }

Slide 22

Slide 22 text

Simple Search Engine class KeywordListener { public function preUpdate(PreUpdateEventArgs $eventArgs) { $entity = $eventArgs->getEntity(); if ($entity instanceof HasKeywords) { $entity->setKeywords($this->buildKeywords($entity)); } } private function buildKeywords(HasKeywords $entity) { $keywords = array(); // build keywords from title, description, etc. return $keywords; } }

Slide 23

Slide 23 text

Simple Search Engine // find products by keyword $products = $dm->createQueryBuilder() ->field('keywords.keyword')->all($keywords) ->getQuery() ->execute();

Slide 24

Slide 24 text

Doctrine in the Real World Go see Jon Wage's talk at later today!

Slide 25

Slide 25 text

Database Migrations Deploy DB schema changes before the code Prevent DB schema BC breaks Use DoctrineMigrationBundle app/console doctrine:migrations:diff app/console doctrine:migrations:migrate Do not use entities in migration scripts ever! Write fixtures as migrations or make the fixtures able to update existing data gracefully app/console doctrine:data:load --fixtures=app/fixtures

Slide 26

Slide 26 text

Different Content Areas of a Page

Slide 27

Slide 27 text

Caching with Edge Side Includes Symfony2 provides support for Edge Side Includes (ESI) Proxy assembles page from snippets of HTML Snippets can have different cache rules Develop without ESI, test with Symfony2 internal ESI proxy, deploy using ultra-fast Varnish Proxy Break up page into different controller actions based on cache invalidation rules Do not worry about overhead from multiple render calls Never mix content that has different cache timeouts Consider caching user specific content in the client Varnish Reverse Proxy Super fast, PHP cannot match its performance Cache full pages for anonymous users Not just for HTML, also useful for JSON/XML API's

Slide 28

Slide 28 text

Page Rendered Behind a Reverse Proxy

Slide 29

Slide 29 text

Asset Management Go see Kris Wallsmith's talk next!

Slide 30

Slide 30 text

Performance Tips Dump routes to Apache rewrite rules app/console router:dump-apache Write custom cache warmers Do not explicitly inject optional services to controllers If your controller receives many services, which are optional or unused by some actions, that's probably a hint that you should break it up into multiple controllers Do minimal work in the controller, let templates pull additional data as needed Use a bytecode cache with MapFileClassLoader

Slide 31

Slide 31 text

Testing Symfony2 rocks for unit and functional testing Dependency Injection, core classes have interfaces (easy mocking) No base classes, no static dependencies, no ActiveRecord Client fakes "real" requests for functional testing (BrowserKit component) Functional Testing Pros: tests configuration, tests API not implementation Unit Testing Pros: pinpoints issues, very directed testing http://www.slideshare.net/avalanche123/clean-code-5609451 Recommendation: Functional testing is recommended for controller actions Symfony2 provides WebTestCase and BrowserKit Unit testing for complex algorithms, third party API's too hard to mock Use Liip\FunctionalTesting to load fixtures, validate HTML5

Slide 32

Slide 32 text

Deployment Debian style (aka Liip Debian Packager) Write a manifest in YAML Build Debian packages with MAKE Install with apt-get install Server specific settings are asked during install, change later with dpkg-reconfigure Maintain a global overview of all application dependencies in case of (security) updates Watch Lukas' unconference talk later today! Fabric (used at OpenSky) Repository managed with git-flow Clone tagged release branch, init submodules Fix permissions (e.g. cache, log), delete dev/test controllers Replace password/API-key placeholders with prod values in config files Upload in parallel to production nodes, swap "current" symlink

Slide 33

Slide 33 text

Third Party Bundles Here's a new year's resolution: to *always* work on an existing Symfony2 bundle and never recreate my own. #focus #teamwork @weaverryan Ryan Weaver 27 Dec http://twitter.com/weaverryan/status/19565706752299009

Slide 34

Slide 34 text

Third Party Bundles Many vendors have already published bundles: FriendsOfSymfony (http://github.com/friendsofsymfony) UserBundle (forked from knplabs' DoctrineUserBundle) FacebookBundle (forked from kriswallsmith) Liip (http://github.com/liip) FunctionalTestBundle ViewBundle OpenSky (http://github.com/opensky) LdapBundle Sonata (http://github.com/sonata-project) AdminBundle Additionally, a couple sites currently index community bundles: http://symfony2bundles.org/ http://symfohub.com/

Slide 35

Slide 35 text

Third Party Bundles Bundles should follow the best practices No version-tagging or official package manager (yet) Use bundles by adding git submodules to your project Maintain your own fork and "own" what you use Not all bundles are equally maintained Symfony2 API changes => broken bundles If you track symfony/symfony, learn to migrate bundles Avoid rewriting a bundle's services/parameters directly The bundle's DI extension should allow for such configuration; if not, submit a pull request If absolutely necessary, a CompilerPass is cleaner

Slide 36

Slide 36 text

Contributing to Third Party Bundles Similar to Symfony2's own patch guidlines Fork and add remote repository Merge regularly to keep up-to-date Avoid committing directly to your master Merges from upstream should be fast-forwards Once upstream changes are stable, bump your project's submodule pointer

Slide 37

Slide 37 text

Contributing to Third Party Bundles Create branches for patches and new features Can't wait to use this in your project? Temporarily change your project submodule to point to your branch until your pull request is accepted. Help ensure that your pull request merges cleanly Create feature branch based on upstream's master Rebase or merge upstream's master when finished

Slide 38

Slide 38 text

Contributing to Third Party Bundles Was your pull request accepted? Congratulations! Don't merge your feature branch into master! Doing so would cause your master to divert Merge upstream's master into your master Delete your feature branch Update your project's submodule to point to master

Slide 39

Slide 39 text

Resources If you want to jump in and contribute: http://docs.symfony-reloaded.org/master/contributing/community/other. html If you are still fuzzy on Dependency Injection: http://fabien.potencier.org/article/11/what-is-dependency-injection If you keep up with Symfony2 repository on github: http://docs.symfony-reloaded.org/master/

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

Application Using Dependency Injection

Slide 42

Slide 42 text

Circular Dependency Example

Slide 43

Slide 43 text

Dependency Injection All objects are instantiated in one of two ways: Using the "new" operator Using an object factory All objects get collaborators in one of two ways Passed to object constructor Set using a setter