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
<?phprequire_once __DIR__ . '/../vendor/autoload.php';$app = new Silex\Application();$app->get('/', function() use ($app) { return 'Hello World';});$app->run();
12
Congrats!
13
And you add more pages
<?phprequire_once __DIR__ . '/../vendor/autoload.php';$app = new Silex\Application();$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
<?phprequire_once __DIR__ . '/../vendor/autoload.php';$app = new Silex\Application();$app->register(new Silex\Provider\TwigServiceProvider(), 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<?phprequire_once __DIR__ . '/../vendor/autoload.php';use Symfony\Component\HttpFoundation\Request;$app = new Silex\Application();$app->register(new Silex\Provider\TwigServiceProvider(), array( 'twig.path' => __DIR__.'/views',));$app->register(new Silex\Provider\FormServiceProvider());$app->register(new Silex\Provider\TranslationServiceProvider());$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<?phprequire_once __DIR__ . '/../vendor/autoload.php';use Symfony\Component\HttpFoundation\Request;$app = new Silex\Application();$app->register(new Silex\Provider\TwigServiceProvider(), array( 'twig.path' => __DIR__.'/views',));$app->register(new Silex\Provider\FormServiceProvider());$app->register(new Silex\Provider\TranslationServiceProvider());$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 Silex\Application();
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 Silex\Provider\TwigServiceProvider(), array( 'twig.path' => $app['config']['template_path'],));
$app->register(new Silex\Provider\FormServiceProvider());
$app->register(new Silex\Provider\TranslationServiceProvider());
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 Silex\ServiceProviderInterface
● Implement boot() and register()
34
For Controllers
● Implement Silex\ControllerProviderInterface
● Implement connect()
35
Sample Controller
use Silex\Application;use Silex\ControllerProviderInterface;
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.phpnamespace MyApp\Controller;
use Silex\Application;use Symfony\Component\HttpFoundation\Request;
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 Silex\Provider\TwigServiceProvider(), array( 'twig.path' => $app['config']['template_path'],));
$app->register(new Silex\Provider\FormServiceProvider());
$app->register(new Silex\Provider\TranslationServiceProvider());
$app['controller.index'] = $app->share(function() use ($app) {return new MyApp\Controller\IndexController();
});
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?
Top Related