complex sites with silex
DESCRIPTION
Everyone knows that Silex is a great microframework for APIs and small sites, but what do you do when you want to build a large site, or your little tiny site has grown up? Silex has many different ways to let you build larger, complex websites that might still be too small for Symfony, but have outgrown the single page app it once was. We’ll look at what Silex offers us, and different ways we can structure our site.TRANSCRIPT
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?