Complex Sites with Silex 
Chris Tankersley 
ZendCon 2014
2 
Who Am I? 
● A PHP Developer for 10 Years 
● Lots of projects no one uses, 
and a few some do 
● https://github.com/dragonmantank
3 
What is Silex?
4 
Microframework 
● Built off of the Symfony2 
Components 
● Micro Service Container 
● Provides Routing 
● Provides Extensions
5 
Why to Not Use Silex 
● You have to piece things 
together 
● Not great for large projects
6 
Why use Silex? 
● You can use the libraries you 
want 
● Great for small/medium 
projects 
● Prototypers best friend
7 
Sites Change over time
8 
Silex Allows Us To Adapt
9 
Basic Silex App
10 
Set up Silex 
composer require “silex/silex”:”~1.2”
11 
Sample Silex App 
<?php 
require_once __DIR__ . '/../vendor/autoload.php'; 
$app = new SilexApplication(); 
$app->get('/', function() use ($app) { 
return 'Hello World'; 
}); 
$app->run();
12 
Congrats!
13 
And you add more pages 
<?php 
require_once __DIR__ . '/../vendor/autoload.php'; 
$app = new SilexApplication(); 
$app->get('/', function() use ($app) {/*...*/}); 
$app->get('/about, function() use ($app) {/*...*/}); 
$app->get('/contact', function() use ($app) {/*...*/}); 
$app->get('/services', function() use ($app) {/*...*/}); 
$app->run();
14 
Service Providers
15 
What do they do? 
● Allows code reuse 
● For both services and 
controllers
16 
Service Providers 
● Twig 
● URL Generator 
● Session 
● Validator 
● Form 
● HTTP Cache 
● HTTP Fragments 
● Security 
● Remember Me 
● Switftmailer 
● Monolog 
● Translation 
● Serializer 
● Doctrine 
● Controllers as 
Services
17 
And you add templating 
<?php 
require_once __DIR__ . '/../vendor/autoload.php'; 
$app = new SilexApplication(); 
$app->register(new SilexProviderTwigServiceProvider(), array( 
'twig.path' => __DIR__.'/../views', 
)); 
$app->get('/', function() use ($app) { 
return $app['twig']->render('homepage.html.twig'); 
}); 
$app->get('/about, function() use ($app) {/*...*/}); 
$app->get('/contact', function() use ($app) {/*...*/}); 
$app->get('/services', function() use ($app) {/*...*/}); 
$app->run();
18 
Adding a Form 
●We need to generate the form 
●We need to accept the form 
●We need to e-mail the form
19 
Now we add a form 
<?php 
require_once __DIR__ . '/../vendor/autoload.php'; 
use SymfonyComponentHttpFoundationRequest; 
$app = new SilexApplication(); 
$app->register(new SilexProviderTwigServiceProvider(), array( 
'twig.path' => __DIR__.'/views', 
)); 
$app->register(new SilexProviderFormServiceProvider()); 
$app->register(new SilexProviderTranslationServiceProvider()); 
$app->get('/', function() use ($app) {/*...*/}); 
$app->get('/about, function() use ($app) {/*...*/}); 
$app->get('/contact', function(Request $request) use ($app) { 
$form = $app['form.factory']->createBuilder('form') 
->add('name') 
->add('email') 
->getForm() 
; 
$form->handleRequest($request); 
if($form->isValid()) { 
$data = $form->getData(); 
// Mail it 
} 
return $app['twig']->render('contact.html.twig', array('form' => $form->createView())); 
}); 
$app->get('/services', function() use ($app) {/*..}); 
$app->run();
20 
HTTP Method Routing
21 
Whoops
22 
Allow Both Methods 
<?php 
require_once __DIR__ . '/../vendor/autoload.php'; 
use SymfonyComponentHttpFoundationRequest; 
$app = new SilexApplication(); 
$app->register(new SilexProviderTwigServiceProvider(), array( 
'twig.path' => __DIR__.'/views', 
)); 
$app->register(new SilexProviderFormServiceProvider()); 
$app->register(new SilexProviderTranslationServiceProvider()); 
$app->get('/', function() use ($app) {/*...*/}); 
$app->get('/about, function() use ($app) {/*...*/}); 
$app->match('/contact', function(Request $request) use ($app) { 
$form = $app['form.factory']->createBuilder('form') 
->add('name') 
->add('email') 
->getForm() 
; 
$form->handleRequest($request); 
if($form->isValid()) { 
$data = $form->getData(); 
// Mail it 
} 
return $app['twig']->render('contact.html.twig', array('form' => $form->createView())); 
})->method('GET|POST'); 
$app->get('/services', function() use ($app) {/*..}); 
$app->run();
23 
We've silently introduced a problem into our 
code
24 
Code Quickly Turns to 
Spaghetti 
http://commons.wikimedia.org/wiki/File:Spaghetti-prepared.jpg
25 
Pretend to be a big framework
26 
What do we need to do? 
● Consistent File Layout 
● Separate out functionality 
● Make things more testable
27 
Clean up our file structure
28 
Break up that big file 
● File for bootstrapping the app 
● File for routes 
● File for running the app
29 
web/index.php 
require_once __DIR__ . '/../vendor/autoload.php'; 
$app = new SilexApplication(); 
require_once __DIR__ . '/../app/bootstrap.php'; 
require_once __DIR__ . '/../app/routes.php'; 
$app->run();
30 
app/bootstrap.php 
$config = include_once 'config/config.php'; 
$app['config'] = $config; 
$app->register(new SilexProviderTwigServiceProvider(), array( 
'twig.path' => $app['config']['template_path'], 
)); 
$app->register(new SilexProviderFormServiceProvider()); 
$app->register(new SilexProviderTranslationServiceProvider());
31 
app/routes.php 
$app->get('/', function() use ($app) {/*...*/}); 
$app->get('/about, function() use ($app) {/*...*/}); 
$app->match('/contact', function(Request $request) use ($app) { 
$form = $app['form.factory']->createBuilder('form') 
->add('name') 
->add('email') 
->getForm() 
; 
$form->handleRequest($request); 
if($form->isValid()) { 
$data = $form->getData(); 
// Mail it 
} 
return $app['twig']->render('contact.html.twig', array('form' => $form->createView())); 
})->method('GET|POST'); 
$app->get('/services', function() use ($app) {/*..});
32 
Service Providers
33 
For Services 
● Implement 
SilexServiceProviderInterface 
● Implement boot() and 
register()
34 
For Controllers 
● Implement 
SilexControllerProviderInterface 
● Implement connect()
35 
Sample Controller 
use SilexApplication; 
use SilexControllerProviderInterface; 
class BlogController implements ControllerProviderInterface { 
public function connect(Application $app) { 
$controllers = $app['controllers_factory']; 
$controllers->get('/', array($this, 'index')); 
$controllers->get('/inline', function() use ($app) { 
/* Logic Here */ 
}); 
} 
protected function index(Application $app) { 
/* Logic Here */ 
} 
} 
$app->mount('/blog', new BlogController());
36 
Controllers as Services 
● Ships with Silex 
● Allows attaching objects to 
routes 
● Favors dependency injection 
● Framework Agnostic
37 
Create our Object 
// src/MyApp/Controller/IndexController.php 
namespace MyAppController; 
use SilexApplication; 
use SymfonyComponentHttpFoundationRequest; 
class IndexController 
{ 
public function indexAction(Application $app) 
{ 
return $app['twig']->render('Index/index.html.twig'); 
} 
public function aboutAction(Application $app) { /* … */ } 
public function servicesAction(Application $app) { /* … */ } 
public function contactAction(Application $app) { /* … */ } 
}
38 
Register Controllers as 
Services 
$config = include_once 'config/config.php'; 
$app['config'] = $config; 
$app->register(new SilexProviderTwigServiceProvider(), array( 
'twig.path' => $app['config']['template_path'], 
)); 
$app->register(new SilexProviderFormServiceProvider()); 
$app->register(new SilexProviderTranslationServiceProvider()); 
$app['controller.index'] = $app->share(function() use ($app) { 
return new MyAppControllerIndexController(); 
});
39 
Register our routes 
$app->get('/', 'controller.index:indexAction')->bind('homepage'); 
$app->get('/about', 'controller.index:aboutAction')->bind('about'); 
$app->get('/services', 'controller.index:servicesAction')->bind('services'); 
$app->match('/contact', 'controller.index:contactAction')->bind('contact')->method('GET|POST');
40 
A Little Planning goes a Long Way
41 
Questions?
42 
Thanks! 
● http://joind.in/talk/view/12080 
● @dragonmantank 
● chris@ctankersley.com

Complex Sites with Silex

  • 1.
    Complex Sites withSilex Chris Tankersley ZendCon 2014
  • 2.
    2 Who AmI? ● A PHP Developer for 10 Years ● Lots of projects no one uses, and a few some do ● https://github.com/dragonmantank
  • 3.
    3 What isSilex?
  • 4.
    4 Microframework ●Built off of the Symfony2 Components ● Micro Service Container ● Provides Routing ● Provides Extensions
  • 5.
    5 Why toNot Use Silex ● You have to piece things together ● Not great for large projects
  • 6.
    6 Why useSilex? ● You can use the libraries you want ● Great for small/medium projects ● Prototypers best friend
  • 7.
    7 Sites Changeover time
  • 8.
    8 Silex AllowsUs To Adapt
  • 9.
  • 10.
    10 Set upSilex composer require “silex/silex”:”~1.2”
  • 11.
    11 Sample SilexApp <?php require_once __DIR__ . '/../vendor/autoload.php'; $app = new SilexApplication(); $app->get('/', function() use ($app) { return 'Hello World'; }); $app->run();
  • 12.
  • 13.
    13 And youadd more pages <?php require_once __DIR__ . '/../vendor/autoload.php'; $app = new SilexApplication(); $app->get('/', function() use ($app) {/*...*/}); $app->get('/about, function() use ($app) {/*...*/}); $app->get('/contact', function() use ($app) {/*...*/}); $app->get('/services', function() use ($app) {/*...*/}); $app->run();
  • 14.
  • 15.
    15 What dothey do? ● Allows code reuse ● For both services and controllers
  • 16.
    16 Service Providers ● Twig ● URL Generator ● Session ● Validator ● Form ● HTTP Cache ● HTTP Fragments ● Security ● Remember Me ● Switftmailer ● Monolog ● Translation ● Serializer ● Doctrine ● Controllers as Services
  • 17.
    17 And youadd templating <?php require_once __DIR__ . '/../vendor/autoload.php'; $app = new SilexApplication(); $app->register(new SilexProviderTwigServiceProvider(), array( 'twig.path' => __DIR__.'/../views', )); $app->get('/', function() use ($app) { return $app['twig']->render('homepage.html.twig'); }); $app->get('/about, function() use ($app) {/*...*/}); $app->get('/contact', function() use ($app) {/*...*/}); $app->get('/services', function() use ($app) {/*...*/}); $app->run();
  • 18.
    18 Adding aForm ●We need to generate the form ●We need to accept the form ●We need to e-mail the form
  • 19.
    19 Now weadd a form <?php require_once __DIR__ . '/../vendor/autoload.php'; use SymfonyComponentHttpFoundationRequest; $app = new SilexApplication(); $app->register(new SilexProviderTwigServiceProvider(), array( 'twig.path' => __DIR__.'/views', )); $app->register(new SilexProviderFormServiceProvider()); $app->register(new SilexProviderTranslationServiceProvider()); $app->get('/', function() use ($app) {/*...*/}); $app->get('/about, function() use ($app) {/*...*/}); $app->get('/contact', function(Request $request) use ($app) { $form = $app['form.factory']->createBuilder('form') ->add('name') ->add('email') ->getForm() ; $form->handleRequest($request); if($form->isValid()) { $data = $form->getData(); // Mail it } return $app['twig']->render('contact.html.twig', array('form' => $form->createView())); }); $app->get('/services', function() use ($app) {/*..}); $app->run();
  • 20.
  • 21.
  • 22.
    22 Allow BothMethods <?php require_once __DIR__ . '/../vendor/autoload.php'; use SymfonyComponentHttpFoundationRequest; $app = new SilexApplication(); $app->register(new SilexProviderTwigServiceProvider(), array( 'twig.path' => __DIR__.'/views', )); $app->register(new SilexProviderFormServiceProvider()); $app->register(new SilexProviderTranslationServiceProvider()); $app->get('/', function() use ($app) {/*...*/}); $app->get('/about, function() use ($app) {/*...*/}); $app->match('/contact', function(Request $request) use ($app) { $form = $app['form.factory']->createBuilder('form') ->add('name') ->add('email') ->getForm() ; $form->handleRequest($request); if($form->isValid()) { $data = $form->getData(); // Mail it } return $app['twig']->render('contact.html.twig', array('form' => $form->createView())); })->method('GET|POST'); $app->get('/services', function() use ($app) {/*..}); $app->run();
  • 23.
    23 We've silentlyintroduced a problem into our code
  • 24.
    24 Code QuicklyTurns to Spaghetti http://commons.wikimedia.org/wiki/File:Spaghetti-prepared.jpg
  • 25.
    25 Pretend tobe a big framework
  • 26.
    26 What dowe need to do? ● Consistent File Layout ● Separate out functionality ● Make things more testable
  • 27.
    27 Clean upour file structure
  • 28.
    28 Break upthat big file ● File for bootstrapping the app ● File for routes ● File for running the app
  • 29.
    29 web/index.php require_once__DIR__ . '/../vendor/autoload.php'; $app = new SilexApplication(); require_once __DIR__ . '/../app/bootstrap.php'; require_once __DIR__ . '/../app/routes.php'; $app->run();
  • 30.
    30 app/bootstrap.php $config= include_once 'config/config.php'; $app['config'] = $config; $app->register(new SilexProviderTwigServiceProvider(), array( 'twig.path' => $app['config']['template_path'], )); $app->register(new SilexProviderFormServiceProvider()); $app->register(new SilexProviderTranslationServiceProvider());
  • 31.
    31 app/routes.php $app->get('/',function() use ($app) {/*...*/}); $app->get('/about, function() use ($app) {/*...*/}); $app->match('/contact', function(Request $request) use ($app) { $form = $app['form.factory']->createBuilder('form') ->add('name') ->add('email') ->getForm() ; $form->handleRequest($request); if($form->isValid()) { $data = $form->getData(); // Mail it } return $app['twig']->render('contact.html.twig', array('form' => $form->createView())); })->method('GET|POST'); $app->get('/services', function() use ($app) {/*..});
  • 32.
  • 33.
    33 For Services ● Implement SilexServiceProviderInterface ● Implement boot() and register()
  • 34.
    34 For Controllers ● Implement SilexControllerProviderInterface ● Implement connect()
  • 35.
    35 Sample Controller use SilexApplication; use SilexControllerProviderInterface; class BlogController implements ControllerProviderInterface { public function connect(Application $app) { $controllers = $app['controllers_factory']; $controllers->get('/', array($this, 'index')); $controllers->get('/inline', function() use ($app) { /* Logic Here */ }); } protected function index(Application $app) { /* Logic Here */ } } $app->mount('/blog', new BlogController());
  • 36.
    36 Controllers asServices ● Ships with Silex ● Allows attaching objects to routes ● Favors dependency injection ● Framework Agnostic
  • 37.
    37 Create ourObject // src/MyApp/Controller/IndexController.php namespace MyAppController; use SilexApplication; use SymfonyComponentHttpFoundationRequest; class IndexController { public function indexAction(Application $app) { return $app['twig']->render('Index/index.html.twig'); } public function aboutAction(Application $app) { /* … */ } public function servicesAction(Application $app) { /* … */ } public function contactAction(Application $app) { /* … */ } }
  • 38.
    38 Register Controllersas Services $config = include_once 'config/config.php'; $app['config'] = $config; $app->register(new SilexProviderTwigServiceProvider(), array( 'twig.path' => $app['config']['template_path'], )); $app->register(new SilexProviderFormServiceProvider()); $app->register(new SilexProviderTranslationServiceProvider()); $app['controller.index'] = $app->share(function() use ($app) { return new MyAppControllerIndexController(); });
  • 39.
    39 Register ourroutes $app->get('/', 'controller.index:indexAction')->bind('homepage'); $app->get('/about', 'controller.index:aboutAction')->bind('about'); $app->get('/services', 'controller.index:servicesAction')->bind('services'); $app->match('/contact', 'controller.index:contactAction')->bind('contact')->method('GET|POST');
  • 40.
    40 A LittlePlanning goes a Long Way
  • 41.
  • 42.
    42 Thanks! ●http://joind.in/talk/view/12080 ● @dragonmantank ● chris@ctankersley.com