SlideShare a Scribd company logo
Keeping it small
Getting to know the Slim micro framework
             @JeremyKendall
Jeremy
Kendall
Jeremy
Kendall
I love to code
Jeremy
Kendall
I love to code
I’m terribly forgetful
Jeremy
Kendall
I love to code
I’m terribly forgetful
I take pictures
Jeremy
Kendall
I love to code
I’m terribly forgetful
I take pictures
I work at OpenSky
Micro framework?
Micro framework?

Concise codebase
Micro framework?

Concise codebase
Clear codebase
Micro framework?

Concise codebase
Clear codebase
Addresses a small set of use cases
Micro framework?

Concise codebase
Clear codebase
Addresses a small set of use cases
Addresses those use cases well
What is Slim?
What is Slim?

Inspired by Sinatra
What is Slim?

Inspired by Sinatra
Favors cleanliness over terseness
What is Slim?

Inspired by Sinatra
Favors cleanliness over terseness
Favors common cases over edge cases
Installing Slim
RTFM
RTFM ;-)
Don’t forget .htaccess!

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]


http://docs.slimframework.com/pages/routing-url-rewriting/
Hello world
<?php

require '../vendor/autoload.php';

$app = new SlimSlim();

$app->get('/hello/:name', function ($name) {
    echo "Hello, $name";
});

$app->run();
Hello world
<?php

require '../vendor/autoload.php';

$app = new SlimSlim();

$app->get('/hello/:name', function ($name) {
    echo "Hello, $name";
});

$app->run();
Hello world
<?php

require '../vendor/autoload.php';

$app = new SlimSlim();

$app->get('/hello/:name', function ($name) {
    echo "Hello, $name";
});

$app->run();
Hello world
<?php

require '../vendor/autoload.php';

$app = new SlimSlim();

$app->get('/hello/:name', function ($name) {
    echo "Hello, $name";
});

$app->run();
Hello world
<?php

require '../vendor/autoload.php';

$app = new SlimSlim();

$app->get('/hello/:name', function ($name) {
    echo "Hello, $name";
});

$app->run();
Let’s look at a
Slim application
Flaming Archer
Flaming Archer

    wat
“Great repository names are short and memorable.
 Need inspiration? How about flaming-archer.”
Flaming Archer
Flaming Archer
Photo 365 project
Flaming Archer
Photo 365 project
Built in 4 days (Saturday through Tuesday)
Flaming Archer
Photo 365 project
Built in 4 days (Saturday through Tuesday)
Basic application — a few bells, no whistles
Flaming Archer
Photo 365 project
Built in 4 days (Saturday through Tuesday)
Basic application — a few bells, no whistles
   Routing
Flaming Archer
Photo 365 project
Built in 4 days (Saturday through Tuesday)
Basic application — a few bells, no whistles
   Routing
   Twig views
Flaming Archer
Photo 365 project
Built in 4 days (Saturday through Tuesday)
Basic application — a few bells, no whistles
   Routing
   Twig views
   Middleware
4 views
Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro Framework
phploc --exclude vendor,tests,templates .

phploc 1.6.4 by Sebastian Bergmann.

Directories:                                   7
Files:                                        13

Lines of Code (LOC):                         876
  Cyclomatic Complexity / Lines of Code:    0.04
Comment Lines of Code (CLOC):                272
Non-Comment Lines of Code (NCLOC):           604
Configuration
return array(
    'slim' => array(
        'templates.path' => __DIR__ . '/templates',
        'log.level' => 4,
        'log.enabled' => true,
        'log.writer' => new SlimExtrasLogDateTimeFileWriter(
            array(
                 'path' => __DIR__ . '/logs',
                 'name_format' => 'y-m-d'
            )
        )
    ),
    'twig' => array(
        // . . .
    ),
    'cookies' => array(
        // . . .
    ),
    'flickr.api.key' => 'FLICKR API KEY',
    'pdo' => array(
        // . . .
    )
);
return array(
    'slim' => array(
        'templates.path' => __DIR__ . '/templates',
        'log.level' => 4,                                         Slim
        'log.enabled' => true,
        'log.writer' => new SlimExtrasLogDateTimeFileWriter(
            array(
                 'path' => __DIR__ . '/logs',
                 'name_format' => 'y-m-d'
            )
        )
    ),
    'twig' => array(
        // . . .
    ),
    'cookies' => array(
        // . . .
    ),
    'flickr.api.key' => 'FLICKR API KEY',
    'pdo' => array(
        // . . .
    )
);
return array(
    'slim' => array(
        'templates.path' => __DIR__ . '/templates',
        'log.level' => 4,                                         Slim
        'log.enabled' => true,
        'log.writer' => new SlimExtrasLogDateTimeFileWriter(
            array(
                 'path' => __DIR__ . '/logs',
                 'name_format' => 'y-m-d'
            )
        )                                            Views
    ),
    'twig' => array(
        // . . .
    ),
    'cookies' => array(
        // . . .
    ),
    'flickr.api.key' => 'FLICKR API KEY',
    'pdo' => array(
        // . . .
    )
);
return array(
    'slim' => array(
        'templates.path' => __DIR__ . '/templates',
        'log.level' => 4,                                         Slim
        'log.enabled' => true,
        'log.writer' => new SlimExtrasLogDateTimeFileWriter(
            array(
                 'path' => __DIR__ . '/logs',
                 'name_format' => 'y-m-d'
            )
        )                                            Views
    ),
    'twig' => array(
        // . . .
    ),
    'cookies' => array(                          Cookies
        // . . .
    ),
    'flickr.api.key' => 'FLICKR API KEY',
    'pdo' => array(
        // . . .
    )
);
return array(
    'slim' => array(
        'templates.path' => __DIR__ . '/templates',
        'log.level' => 4,                                         Slim
        'log.enabled' => true,
        'log.writer' => new SlimExtrasLogDateTimeFileWriter(
            array(
                 'path' => __DIR__ . '/logs',
                 'name_format' => 'y-m-d'
            )
        )                                            Views
    ),
    'twig' => array(
        // . . .
    ),
    'cookies' => array(                          Cookies
        // . . .
    ),
    'flickr.api.key' => 'FLICKR API KEY',
    'pdo' => array(
        // . . .
    )                                           My stuff
);
$config = require_once __DIR__ . '/../config.php';

// Prepare app
$app = new SlimSlim($config['slim']);
$config = require_once __DIR__ . '/../config.php';

// Prepare app
$app = new SlimSlim($config['slim']);



                        Config array
                         goes here
Routing
Routing


$app->get('/', function () use ($app, $service) {
        $images = $service->findAll();
        $app->render('index.html', array('images' => $images));
    }
);
Routing

HTTP Method



  $app->get('/', function () use ($app, $service) {
          $images = $service->findAll();
          $app->render('index.html', array('images' => $images));
      }
  );
Routing

                   Resource URI
HTTP Method



  $app->get('/', function () use ($app, $service) {
          $images = $service->findAll();
          $app->render('index.html', array('images' => $images));
      }
  );
Routing

                   Resource URI
HTTP Method                            Anonymous Function



  $app->get('/', function () use ($app, $service) {
          $images = $service->findAll();
          $app->render('index.html', array('images' => $images));
      }
  );
Routing


$app->get('/', function () use ($app, $service) {
        $images = $service->findAll();
        $app->render('index.html', array('images' => $images));
    }
);
Routing


$app->get('/', function () use ($app, $service) {      Grabs all the pics
        $images = $service->findAll();
        $app->render('index.html', array('images' => $images));
    }
);
Routing


$app->get('/', function () use ($app, $service) {      Grabs all the pics
        $images = $service->findAll();
        $app->render('index.html', array('images' => $images));
    }
);


             Passes array of image data to index.html
GET
$app->get('/:day', function($day) use ($app, $service) {
        $image = $service->find($day);

        if (!$image) {
            $app->notFound();
        }

        $app->render('images.html', $image);
    }
)->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));
GET
          URL parameter

$app->get('/:day', function($day) use ($app, $service) {
        $image = $service->find($day);

        if (!$image) {
            $app->notFound();
        }

        $app->render('images.html', $image);
    }
)->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));
GET            ... gets passed as an
          URL parameter                               argument

$app->get('/:day', function($day) use ($app, $service) {
        $image = $service->find($day);

        if (!$image) {
            $app->notFound();
        }

        $app->render('images.html', $image);
    }
)->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));
GET            ... gets passed as an
          URL parameter                               argument

$app->get('/:day', function($day) use ($app, $service) {
        $image = $service->find($day);

        if (!$image) {
            $app->notFound();
        }

        $app->render('images.html', $image);
    }
)->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));


          Condition
GET             ... gets passed as an
          URL parameter                                argument

$app->get('/:day', function($day) use ($app, $service) {
        $image = $service->find($day);

        if (!$image) {
            $app->notFound();
        }

        $app->render('images.html', $image);
    }
)->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));


          Condition                            1 to 366
GET
$app->get('/:day', function($day) use ($app, $service) {
        $image = $service->find($day);

        if (!$image) {
            $app->notFound();            404!
        }

        $app->render('images.html', $image);
    }
)->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));
POST (with redirect)
$app->post('/admin/add-photo', function() use ($app, $service) {
        $data = $app->request()->post();
        $service->save($data);
        $app->redirect('/admin');
    }
);
POST (with redirect)
$app->post('/admin/add-photo', function() use ($app, $service) {
        $data = $app->request()->post();
        $service->save($data);
        $app->redirect('/admin');
    }                                          $_POST data is in
);                                             the request object
POST (with redirect)
$app->post('/admin/add-photo', function() use ($app, $service) {
        $data = $app->request()->post();
        $service->save($data);
        $app->redirect('/admin');
    }                                          $_POST data is in
);                                             the request object

            302 Redirect
Multiple methods

$app->map('/login', function() {
        // Login
    }
)->via('GET', 'POST');
Multiple methods
    Not an HTTP Method



$app->map('/login', function() {
        // Login
    }
)->via('GET', 'POST');
Multiple methods
    Not an HTTP Method



$app->map('/login', function() {
        // Login
    }
)->via('GET', 'POST');


              via() is the
            awesome sauce
Logging and flash messaging
$app->post('/admin/clear-cache', function() use ($app) {

         $log = $app->getLog();
         $cleared = null;
         $clear = $app->request()->post('clear');

         if ($clear == 1) {
             if (apc_clear_cache('user')) {
                 $cleared = 'Cache was successfully cleared!';
             } else {
                 $cleared = 'Cache was not cleared!';
                 $log->error('Cache not cleared');
             }
         }

         $app->flash('cleared', $cleared);
         $app->redirect('/admin');
     }
);
$app->post('/admin/clear-cache', function() use ($app) {

         $log = $app->getLog();       Get the log from   $app
         $cleared = null;
         $clear = $app->request()->post('clear');

         if ($clear == 1) {
             if (apc_clear_cache('user')) {
                 $cleared = 'Cache was successfully cleared!';
             } else {
                 $cleared = 'Cache was not cleared!';
                 $log->error('Cache not cleared');
             }
         }

         $app->flash('cleared', $cleared);
         $app->redirect('/admin');
     }
);
$app->post('/admin/clear-cache', function() use ($app) {

         $log = $app->getLog();       Get the log from   $app
         $cleared = null;
         $clear = $app->request()->post('clear');

         if ($clear == 1) {
             if (apc_clear_cache('user')) {
                 $cleared = 'Cache was successfully cleared!';
             } else {
                 $cleared = 'Cache was not cleared!';
                 $log->error('Cache not cleared');              Error!
             }
         }

         $app->flash('cleared', $cleared);
         $app->redirect('/admin');
     }
);
$app->post('/admin/clear-cache', function() use ($app) {

         $log = $app->getLog();       Get the log from   $app
         $cleared = null;
         $clear = $app->request()->post('clear');

         if ($clear == 1) {
             if (apc_clear_cache('user')) {
                 $cleared = 'Cache was successfully cleared!';
             } else {
                 $cleared = 'Cache was not cleared!';
                 $log->error('Cache not cleared');              Error!
             }
         }

         $app->flash('cleared', $cleared);     Flash message available in
         $app->redirect('/admin');                 the next request.
     }
);
Middleware
“. . . a Slim application can have middleware
that may inspect, analyze, or modify
the application environment, request, and
response before and/or after the Slim
application is invoked.”
 http://docs.slimframework.com/pages/middleware-overview/
Hooks
Hooks

slim.before
Hooks

slim.before
slim.before.router
Hooks

slim.before
slim.before.router
slim.before.dispatch
Hooks

slim.before            slim.after.dispatch
slim.before.router
slim.before.dispatch
Hooks

slim.before            slim.after.dispatch
slim.before.router     slim.after.router
slim.before.dispatch
Hooks

slim.before            slim.after.dispatch
slim.before.router     slim.after.router
slim.before.dispatch   slim.after
Hooks

slim.before            slim.after.dispatch
slim.before.router     slim.after.router
slim.before.dispatch   slim.after
class MyMiddleware extends SlimMiddleware
{
    public function call()
    {
        //The Slim application
        $app = $this->app;

        //The Environment object
        $env = $app->environment();

        //The Request object
        $req = $app->request();

        //The Response object
        $res = $app->response();


        //Optionally call the next middleware
        $this->next->call();
    }
}
class MyMiddleware extends SlimMiddleware     Extend this
{
    public function call()
    {
        //The Slim application
        $app = $this->app;

        //The Environment object
        $env = $app->environment();

        //The Request object
        $req = $app->request();

        //The Response object
        $res = $app->response();


        //Optionally call the next middleware
        $this->next->call();
    }
}
class MyMiddleware extends SlimMiddleware            Extend this
{
    public function call()
    {                                       Define   call()
        //The Slim application
        $app = $this->app;

        //The Environment object
        $env = $app->environment();

        //The Request object
        $req = $app->request();

        //The Response object
        $res = $app->response();


        //Optionally call the next middleware
        $this->next->call();
    }
}
class MyMiddleware extends SlimMiddleware            Extend this
{
    public function call()
    {                                       Define   call()
        //The Slim application
        $app = $this->app;

        //The Environment object
        $env = $app->environment();         Inspect, analyze,
                                              and modify!
        //The Request object
        $req = $app->request();

        //The Response object
        $res = $app->response();


        //Optionally call the next middleware
        $this->next->call();
    }
}
class MyMiddleware extends SlimMiddleware            Extend this
{
    public function call()
    {                                       Define   call()
        //The Slim application
        $app = $this->app;

        //The Environment object
        $env = $app->environment();         Inspect, analyze,
                                              and modify!
        //The Request object
        $req = $app->request();

        //The Response object
        $res = $app->response();


        //Optionally call the next middleware
        $this->next->call();                        On to the next!
    }
}
Middleware + Hooks = WIN
Navigation example
namespace TsfMiddleware;

use ZendAuthenticationAuthenticationService;

class Navigation extends SlimMiddleware
{

    /**
     * @var ZendAuthenticationAuthenticationService
     */
    private $auth;

    public function __construct(AuthenticationService $auth)
    {
        $this->auth = $auth;
    }

    public function call()
    {
        // . . .
    }

}
namespace TsfMiddleware;

use ZendAuthenticationAuthenticationService;

class Navigation extends SlimMiddleware          extends
{

    /**
     * @var ZendAuthenticationAuthenticationService
     */
    private $auth;

    public function __construct(AuthenticationService $auth)
    {
        $this->auth = $auth;
    }

    public function call()
    {
        // . . .
    }

}
namespace TsfMiddleware;

use ZendAuthenticationAuthenticationService;

class Navigation extends SlimMiddleware          extends
{

    /**
     * @var ZendAuthenticationAuthenticationService
     */
    private $auth;

    public function __construct(AuthenticationService $auth)
    {
        $this->auth = $auth;
    }
                                                  Constructor injection
    public function call()
                                                         FTW
    {
        // . . .
    }

}
public function call()
{
    $app = $this->app;
    $auth = $this->auth;
    $req = $app->request();

    $home = array('caption' => 'Home', 'href' => '/');
    $admin = array('caption' => 'Admin', 'href' => '/admin');
    $login = array('caption' => 'Login', 'href' => '/login');
    $logout = array('caption' => 'Logout', 'href' => '/logout');

    if ($auth->hasIdentity()) {
        $navigation = array($home, $admin, $logout);
    } else {
        $navigation = array($home, $login);
    }

    // . . .
}
public function call()
{
    $app = $this->app;                           Arrays of
    $auth = $this->auth;                         nav items
    $req = $app->request();

    $home = array('caption' => 'Home', 'href' => '/');
    $admin = array('caption' => 'Admin', 'href' => '/admin');
    $login = array('caption' => 'Login', 'href' => '/login');
    $logout = array('caption' => 'Logout', 'href' => '/logout');

    if ($auth->hasIdentity()) {
        $navigation = array($home, $admin, $logout);
    } else {
        $navigation = array($home, $login);
    }

    // . . .
}
public function call()
{
    $app = $this->app;                           Arrays of
    $auth = $this->auth;                         nav items
    $req = $app->request();

    $home = array('caption' => 'Home', 'href' => '/');
    $admin = array('caption' => 'Admin', 'href' => '/admin');
    $login = array('caption' => 'Login', 'href' => '/login');
    $logout = array('caption' => 'Logout', 'href' => '/logout');

    if ($auth->hasIdentity()) {
        $navigation = array($home, $admin, $logout);
    } else {
        $navigation = array($home, $login);
    }
                                                       Nav differs based
    // . . .                                            on auth status
}
public function call()
{
    // . . .

    $this->app->hook('slim.before.router', function () use (...) {

             foreach ($navigation as &$link) {
                 if ($link['href'] == $req->getPath()) {
                     $link['class'] = 'active';
                 } else {
                     $link['class'] = '';
                 }
             }

             $app->view()
                 ->appendData(array('navigation' => $navigation));
         }
    );

    $this->next->call();
}
public function call()
{                                                Delicious hook
    // . . .                                       goodness

    $this->app->hook('slim.before.router', function () use (...) {

             foreach ($navigation as &$link) {
                 if ($link['href'] == $req->getPath()) {
                     $link['class'] = 'active';
                 } else {
                     $link['class'] = '';
                 }
             }

             $app->view()
                 ->appendData(array('navigation' => $navigation));
         }
    );

    $this->next->call();
}
public function call()
{                                                 Delicious hook
    // . . .                                        goodness

    $this->app->hook('slim.before.router', function () use (...) {

             foreach ($navigation as &$link) {
                 if ($link['href'] == $req->getPath()) {
                     $link['class'] = 'active';
                 } else {
                     $link['class'] = '';
                 }                                            Match
             }                                           dispatched path
             $app->view()
                 ->appendData(array('navigation' => $navigation));
         }
    );

    $this->next->call();
}
public function call()
{                                                 Delicious hook
    // . . .                                        goodness

    $this->app->hook('slim.before.router', function () use (...) {

             foreach ($navigation as &$link) {
                 if ($link['href'] == $req->getPath()) {
                     $link['class'] = 'active';
                 } else {
                     $link['class'] = '';
                 }                                            Match
             }                                           dispatched path
             $app->view()
                 ->appendData(array('navigation' => $navigation));
         }
    );                                                        Append
                                                           $navigation to
    $this->next->call();
}
                                                               view
public function call()
{                                                 Delicious hook
    // . . .                                        goodness

    $this->app->hook('slim.before.router', function () use (...) {

             foreach ($navigation as &$link) {
                 if ($link['href'] == $req->getPath()) {
                     $link['class'] = 'active';
                 } else {
                     $link['class'] = '';
                 }                                            Match
             }                                           dispatched path
             $app->view()
                 ->appendData(array('navigation' => $navigation));
         }
    );                                                        Append
                                                           $navigation to
    $this->next->call();        On to the next!
}
                                                               view
Views
Two great tastes
that taste great together
Twig
Twig

Concise
Twig

Concise
Template oriented
Twig

Concise
Template oriented
Fast
Twig

Concise             Multiple inheritance
Template oriented
Fast
Twig

Concise             Multiple inheritance
Template oriented   Blocks
Fast
Twig

Concise             Multiple inheritance
Template oriented   Blocks
Fast                Automatic escaping
layout.html
    and
index.html
layout.html
<title>{% block page_title %} {% endblock %}</title>
<ul class="nav">
    {% for link in navigation %}
        <li class="{{link.class}}">
            <a href="{{link.href}}">{{link.caption}}</a>
        </li>
    {% endfor %}
</ul>
<h1>365 Days of Photography</h1>
<h3>Photographer: Jeremy Kendall</h3>
{% block content %} {% endblock %}
<hr />
index.html
{% extends 'layout.html' %}

{% block page_title %}365.jeremykendall.net{% endblock %}

{% block content %}
{% for image in images %}
<div class="row">
    <div class="span6">
        <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2>
        <p>
             <a href="/{{image.day}}">
                  <img src="{{image.sizes.size.5.source}}" />
             </a>
        </p>
        <p>Posted {{image.posted|date("m/d/Y")}}</p>
    </div>
</div>
{% else %}
<p>No images in project</p>
{% endfor %}
{% endblock %}
{% extends 'layout.html' %}      extends
{% block page_title %}365.jeremykendall.net{% endblock %}

{% block content %}
{% for image in images %}
<div class="row">
    <div class="span6">
        <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2>
        <p>
             <a href="/{{image.day}}">
                  <img src="{{image.sizes.size.5.source}}" />
             </a>
        </p>
        <p>Posted {{image.posted|date("m/d/Y")}}</p>
    </div>
</div>
{% else %}
<p>No images in project</p>
{% endfor %}
{% endblock %}
{% extends 'layout.html' %}      extends
{% block page_title %}365.jeremykendall.net{% endblock %}

{% block content %}                                     <title    />
{% for image in images %}
<div class="row">
    <div class="span6">
        <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2>
        <p>
             <a href="/{{image.day}}">
                  <img src="{{image.sizes.size.5.source}}" />
             </a>
        </p>
        <p>Posted {{image.posted|date("m/d/Y")}}</p>
    </div>
</div>
{% else %}
<p>No images in project</p>
{% endfor %}
{% endblock %}
{% extends 'layout.html' %}      extends
{% block page_title %}365.jeremykendall.net{% endblock %}

{% block content %}                                     <title    />
{% for image in images %}
<div class="row">
    <div class="span6">
        <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2>
        <p>
             <a href="/{{image.day}}">
                  <img src="{{image.sizes.size.5.source}}" />
             </a>
        </p>
        <p>Posted {{image.posted|date("m/d/Y")}}</p>
    </div>
</div>
{% else %}
<p>No images in project</p>
{% endfor %}
{% endblock %}
{% extends 'layout.html' %}      extends
{% block page_title %}365.jeremykendall.net{% endblock %}

{% block content %}                                     <title    />
{% for image in images %}        iterator
<div class="row">
    <div class="span6">
        <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2>
        <p>
             <a href="/{{image.day}}">
                  <img src="{{image.sizes.size.5.source}}" />
             </a>
        </p>
        <p>Posted {{image.posted|date("m/d/Y")}}</p>
    </div>
</div>
{% else %}
<p>No images in project</p>
{% endfor %}
{% endblock %}
{% extends 'layout.html' %}      extends
{% block page_title %}365.jeremykendall.net{% endblock %}

{% block content %}                                     <title    />
{% for image in images %}        iterator
<div class="row">
    <div class="span6">
        <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2>
        <p>
             <a href="/{{image.day}}">
                  <img src="{{image.sizes.size.5.source}}" />
             </a>
        </p>
        <p>Posted {{image.posted|date("m/d/Y")}}</p>
    </div>
</div>                   else
{% else %}
<p>No images in project</p>
{% endfor %}
{% endblock %}
{% extends 'layout.html' %}      extends
{% block page_title %}365.jeremykendall.net{% endblock %}

{% block content %}                                     <title    />
{% for image in images %}        iterator
<div class="row">
    <div class="span6">
        <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2>
        <p>
             <a href="/{{image.day}}">
                  <img src="{{image.sizes.size.5.source}}" />
             </a>
        </p>
        <p>Posted {{image.posted|date("m/d/Y")}}</p>
    </div>
</div>                   else
{% else %}
<p>No images in project</p>                         format
{% endfor %}
{% endblock %}
login.html
{% extends 'layout.html' %}

{% block page_title %}365.jeremykendall.net | Login{% endblock %}

{% block content %}
<div class="row">
    <div class="span4">
        <h2>Login</h2>
        {% if flash.error %}
        <p style="color: red;">{{flash.error}}</p>
        {% endif %}
        <form name="login" id="login" class="well" method="post">
            // Login form . . .
        </form>
    </div>
</div>

{% endblock %}
{% extends 'layout.html' %}

{% block page_title %}365.jeremykendall.net | Login{% endblock %}

{% block content %}
<div class="row">
    <div class="span4">
        <h2>Login</h2>
        {% if flash.error %}
        <p style="color: red;">{{flash.error}}</p>
        {% endif %}
        <form name="login" id="login" class="well" method="post">
            // Login form . . .
        </form>
    </div>
</div>

{% endblock %}
The other views
would be redundant
GOTO 0
Small but powerful
                     GOTO 0
Small but powerful
                         GOTO 0
Excellent tools to write elegant code
Small but powerful
                         GOTO 0
Excellent tools to write elegant code

Routing, middleware & hooks, views
Small but powerful
                          GOTO 0
Excellent tools to write elegant code

Routing, middleware & hooks, views

I just scratched the surface
Read
Slim: slimframework.com
Twig: twig.sensiolabs.org
Composer: getcomposer.org
MicroPHP Manifesto: microphp.org
Flaming Archer: http://git.io/rH0nrg
Questions?
Thanks!
jeremy@jeremykendall.net
    @jeremykendall

More Related Content

PDF
Keeping it small - Getting to know the Slim PHP micro framework
PPT
Slim RedBeanPHP and Knockout
KEY
Keeping it small: Getting to know the Slim micro framework
PDF
Bullet: The Functional PHP Micro-Framework
PDF
Building Modern and Secure PHP Applications – Codementor Office Hours with Be...
PDF
Hello World on Slim Framework 3.x
PDF
Developing apps using Perl
PDF
Inside Bokete: Web Application with Mojolicious and others
Keeping it small - Getting to know the Slim PHP micro framework
Slim RedBeanPHP and Knockout
Keeping it small: Getting to know the Slim micro framework
Bullet: The Functional PHP Micro-Framework
Building Modern and Secure PHP Applications – Codementor Office Hours with Be...
Hello World on Slim Framework 3.x
Developing apps using Perl
Inside Bokete: Web Application with Mojolicious and others

What's hot (20)

PDF
RESTful web services
PDF
With a Mighty Hammer
PDF
Mojolicious
PDF
Advanced symfony Techniques
PDF
Building Cloud Castles
PDF
Perl web frameworks
PPTX
Webrtc mojo
KEY
Mojo as a_client
KEY
Silex, the microframework
PDF
Introducing Assetic: Asset Management for PHP 5.3
PDF
Great Developers Steal
PDF
Using Sinatra to Build REST APIs in Ruby
PDF
Building Cloud Castles - LRUG
PPTX
New in php 7
PDF
Mojolicious: what works and what doesn't
PDF
Symfony tips and tricks
PDF
Silex: From nothing to an API
PDF
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
PDF
Lightweight Webservices with Sinatra and RestClient
PDF
Mojolicious. Веб в коробке!
RESTful web services
With a Mighty Hammer
Mojolicious
Advanced symfony Techniques
Building Cloud Castles
Perl web frameworks
Webrtc mojo
Mojo as a_client
Silex, the microframework
Introducing Assetic: Asset Management for PHP 5.3
Great Developers Steal
Using Sinatra to Build REST APIs in Ruby
Building Cloud Castles - LRUG
New in php 7
Mojolicious: what works and what doesn't
Symfony tips and tricks
Silex: From nothing to an API
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
Lightweight Webservices with Sinatra and RestClient
Mojolicious. Веб в коробке!
Ad

Similar to Keeping it Small: Getting to know the Slim Micro Framework (20)

KEY
Keeping It Small with Slim
KEY
Lithium Best
PDF
Be RESTful (Symfony Camp 2008)
PPTX
What mom never told you about bundle configurations - Symfony Live Paris 2012
 
PDF
Doctrine For Beginners
PDF
Nickolay Shmalenuk.Render api eng.DrupalCamp Kyiv 2011
PDF
関西PHP勉強会 php5.4つまみぐい
PDF
The Zen of Lithium
ZIP
What's new in the Drupal 7 API?
PDF
Building Lithium Apps
PDF
Drehbuch zum Talk "Rapid Prototyping mit PHP Frameworks"
PDF
Virtual Madness @ Etsy
PDF
The State of Lithium
PDF
Bag Of Tricks From Iusethis
PDF
Apostrophe
PDF
Silex meets SOAP & REST
PDF
前端MVC 豆瓣说
KEY
Apostrophe (improved Paris edition)
KEY
第49回Php勉強会@関東 Datasource
PDF
Lithium: The Framework for People Who Hate Frameworks
Keeping It Small with Slim
Lithium Best
Be RESTful (Symfony Camp 2008)
What mom never told you about bundle configurations - Symfony Live Paris 2012
 
Doctrine For Beginners
Nickolay Shmalenuk.Render api eng.DrupalCamp Kyiv 2011
関西PHP勉強会 php5.4つまみぐい
The Zen of Lithium
What's new in the Drupal 7 API?
Building Lithium Apps
Drehbuch zum Talk "Rapid Prototyping mit PHP Frameworks"
Virtual Madness @ Etsy
The State of Lithium
Bag Of Tricks From Iusethis
Apostrophe
Silex meets SOAP & REST
前端MVC 豆瓣说
Apostrophe (improved Paris edition)
第49回Php勉強会@関東 Datasource
Lithium: The Framework for People Who Hate Frameworks
Ad

More from Jeremy Kendall (14)

PDF
Leveraging the Power of Graph Databases in PHP
PDF
Leveraging the Power of Graph Databases in PHP
PDF
5 Ways to Awesome-ize Your (PHP) Code
PDF
Game Changing Dependency Management
ODP
Php 102: Out with the Bad, In with the Good
KEY
Php 101: PDO
ODP
PHP 102: Out with the Bad, In with the Good
ODP
Intro to #memtech PHP 2011-12-05
ODP
TDD in PHP - Memphis PHP 2011-08-25
ODP
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
PDF
Zero to ZF in 10 Minutes
ODP
Tdd in php a brief example
ODP
A Brief Introduction to Zend_Form
ODP
Zero to Zend Framework in 10 minutes
Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHP
5 Ways to Awesome-ize Your (PHP) Code
Game Changing Dependency Management
Php 102: Out with the Bad, In with the Good
Php 101: PDO
PHP 102: Out with the Bad, In with the Good
Intro to #memtech PHP 2011-12-05
TDD in PHP - Memphis PHP 2011-08-25
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
Zero to ZF in 10 Minutes
Tdd in php a brief example
A Brief Introduction to Zend_Form
Zero to Zend Framework in 10 minutes

Recently uploaded (20)

PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PPTX
Big Data Technologies - Introduction.pptx
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Modernizing your data center with Dell and AMD
PPTX
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
PDF
Review of recent advances in non-invasive hemoglobin estimation
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
cuic standard and advanced reporting.pdf
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
Diabetes mellitus diagnosis method based random forest with bat algorithm
Big Data Technologies - Introduction.pptx
The AUB Centre for AI in Media Proposal.docx
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Modernizing your data center with Dell and AMD
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
Review of recent advances in non-invasive hemoglobin estimation
20250228 LYD VKU AI Blended-Learning.pptx
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
NewMind AI Weekly Chronicles - August'25 Week I
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Unlocking AI with Model Context Protocol (MCP)
cuic standard and advanced reporting.pdf
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Per capita expenditure prediction using model stacking based on satellite ima...

Keeping it Small: Getting to know the Slim Micro Framework

  • 1. Keeping it small Getting to know the Slim micro framework @JeremyKendall
  • 4. Jeremy Kendall I love to code I’m terribly forgetful
  • 5. Jeremy Kendall I love to code I’m terribly forgetful I take pictures
  • 6. Jeremy Kendall I love to code I’m terribly forgetful I take pictures I work at OpenSky
  • 10. Micro framework? Concise codebase Clear codebase Addresses a small set of use cases
  • 11. Micro framework? Concise codebase Clear codebase Addresses a small set of use cases Addresses those use cases well
  • 14. What is Slim? Inspired by Sinatra Favors cleanliness over terseness
  • 15. What is Slim? Inspired by Sinatra Favors cleanliness over terseness Favors common cases over edge cases
  • 17. RTFM
  • 19. Don’t forget .htaccess! RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [QSA,L] http://docs.slimframework.com/pages/routing-url-rewriting/
  • 20. Hello world <?php require '../vendor/autoload.php'; $app = new SlimSlim(); $app->get('/hello/:name', function ($name) { echo "Hello, $name"; }); $app->run();
  • 21. Hello world <?php require '../vendor/autoload.php'; $app = new SlimSlim(); $app->get('/hello/:name', function ($name) { echo "Hello, $name"; }); $app->run();
  • 22. Hello world <?php require '../vendor/autoload.php'; $app = new SlimSlim(); $app->get('/hello/:name', function ($name) { echo "Hello, $name"; }); $app->run();
  • 23. Hello world <?php require '../vendor/autoload.php'; $app = new SlimSlim(); $app->get('/hello/:name', function ($name) { echo "Hello, $name"; }); $app->run();
  • 24. Hello world <?php require '../vendor/autoload.php'; $app = new SlimSlim(); $app->get('/hello/:name', function ($name) { echo "Hello, $name"; }); $app->run();
  • 25. Let’s look at a Slim application
  • 28. “Great repository names are short and memorable. Need inspiration? How about flaming-archer.”
  • 31. Flaming Archer Photo 365 project Built in 4 days (Saturday through Tuesday)
  • 32. Flaming Archer Photo 365 project Built in 4 days (Saturday through Tuesday) Basic application — a few bells, no whistles
  • 33. Flaming Archer Photo 365 project Built in 4 days (Saturday through Tuesday) Basic application — a few bells, no whistles Routing
  • 34. Flaming Archer Photo 365 project Built in 4 days (Saturday through Tuesday) Basic application — a few bells, no whistles Routing Twig views
  • 35. Flaming Archer Photo 365 project Built in 4 days (Saturday through Tuesday) Basic application — a few bells, no whistles Routing Twig views Middleware
  • 41. phploc --exclude vendor,tests,templates . phploc 1.6.4 by Sebastian Bergmann. Directories: 7 Files: 13 Lines of Code (LOC): 876 Cyclomatic Complexity / Lines of Code: 0.04 Comment Lines of Code (CLOC): 272 Non-Comment Lines of Code (NCLOC): 604
  • 43. return array( 'slim' => array( 'templates.path' => __DIR__ . '/templates', 'log.level' => 4, 'log.enabled' => true, 'log.writer' => new SlimExtrasLogDateTimeFileWriter( array( 'path' => __DIR__ . '/logs', 'name_format' => 'y-m-d' ) ) ), 'twig' => array( // . . . ), 'cookies' => array( // . . . ), 'flickr.api.key' => 'FLICKR API KEY', 'pdo' => array( // . . . ) );
  • 44. return array( 'slim' => array( 'templates.path' => __DIR__ . '/templates', 'log.level' => 4, Slim 'log.enabled' => true, 'log.writer' => new SlimExtrasLogDateTimeFileWriter( array( 'path' => __DIR__ . '/logs', 'name_format' => 'y-m-d' ) ) ), 'twig' => array( // . . . ), 'cookies' => array( // . . . ), 'flickr.api.key' => 'FLICKR API KEY', 'pdo' => array( // . . . ) );
  • 45. return array( 'slim' => array( 'templates.path' => __DIR__ . '/templates', 'log.level' => 4, Slim 'log.enabled' => true, 'log.writer' => new SlimExtrasLogDateTimeFileWriter( array( 'path' => __DIR__ . '/logs', 'name_format' => 'y-m-d' ) ) Views ), 'twig' => array( // . . . ), 'cookies' => array( // . . . ), 'flickr.api.key' => 'FLICKR API KEY', 'pdo' => array( // . . . ) );
  • 46. return array( 'slim' => array( 'templates.path' => __DIR__ . '/templates', 'log.level' => 4, Slim 'log.enabled' => true, 'log.writer' => new SlimExtrasLogDateTimeFileWriter( array( 'path' => __DIR__ . '/logs', 'name_format' => 'y-m-d' ) ) Views ), 'twig' => array( // . . . ), 'cookies' => array( Cookies // . . . ), 'flickr.api.key' => 'FLICKR API KEY', 'pdo' => array( // . . . ) );
  • 47. return array( 'slim' => array( 'templates.path' => __DIR__ . '/templates', 'log.level' => 4, Slim 'log.enabled' => true, 'log.writer' => new SlimExtrasLogDateTimeFileWriter( array( 'path' => __DIR__ . '/logs', 'name_format' => 'y-m-d' ) ) Views ), 'twig' => array( // . . . ), 'cookies' => array( Cookies // . . . ), 'flickr.api.key' => 'FLICKR API KEY', 'pdo' => array( // . . . ) My stuff );
  • 48. $config = require_once __DIR__ . '/../config.php'; // Prepare app $app = new SlimSlim($config['slim']);
  • 49. $config = require_once __DIR__ . '/../config.php'; // Prepare app $app = new SlimSlim($config['slim']); Config array goes here
  • 51. Routing $app->get('/', function () use ($app, $service) { $images = $service->findAll(); $app->render('index.html', array('images' => $images)); } );
  • 52. Routing HTTP Method $app->get('/', function () use ($app, $service) { $images = $service->findAll(); $app->render('index.html', array('images' => $images)); } );
  • 53. Routing Resource URI HTTP Method $app->get('/', function () use ($app, $service) { $images = $service->findAll(); $app->render('index.html', array('images' => $images)); } );
  • 54. Routing Resource URI HTTP Method Anonymous Function $app->get('/', function () use ($app, $service) { $images = $service->findAll(); $app->render('index.html', array('images' => $images)); } );
  • 55. Routing $app->get('/', function () use ($app, $service) { $images = $service->findAll(); $app->render('index.html', array('images' => $images)); } );
  • 56. Routing $app->get('/', function () use ($app, $service) { Grabs all the pics $images = $service->findAll(); $app->render('index.html', array('images' => $images)); } );
  • 57. Routing $app->get('/', function () use ($app, $service) { Grabs all the pics $images = $service->findAll(); $app->render('index.html', array('images' => $images)); } ); Passes array of image data to index.html
  • 58. GET $app->get('/:day', function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render('images.html', $image); } )->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));
  • 59. GET URL parameter $app->get('/:day', function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render('images.html', $image); } )->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));
  • 60. GET ... gets passed as an URL parameter argument $app->get('/:day', function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render('images.html', $image); } )->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));
  • 61. GET ... gets passed as an URL parameter argument $app->get('/:day', function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render('images.html', $image); } )->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])')); Condition
  • 62. GET ... gets passed as an URL parameter argument $app->get('/:day', function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render('images.html', $image); } )->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])')); Condition 1 to 366
  • 63. GET $app->get('/:day', function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); 404! } $app->render('images.html', $image); } )->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));
  • 64. POST (with redirect) $app->post('/admin/add-photo', function() use ($app, $service) { $data = $app->request()->post(); $service->save($data); $app->redirect('/admin'); } );
  • 65. POST (with redirect) $app->post('/admin/add-photo', function() use ($app, $service) { $data = $app->request()->post(); $service->save($data); $app->redirect('/admin'); } $_POST data is in ); the request object
  • 66. POST (with redirect) $app->post('/admin/add-photo', function() use ($app, $service) { $data = $app->request()->post(); $service->save($data); $app->redirect('/admin'); } $_POST data is in ); the request object 302 Redirect
  • 67. Multiple methods $app->map('/login', function() { // Login } )->via('GET', 'POST');
  • 68. Multiple methods Not an HTTP Method $app->map('/login', function() { // Login } )->via('GET', 'POST');
  • 69. Multiple methods Not an HTTP Method $app->map('/login', function() { // Login } )->via('GET', 'POST'); via() is the awesome sauce
  • 70. Logging and flash messaging
  • 71. $app->post('/admin/clear-cache', function() use ($app) { $log = $app->getLog(); $cleared = null; $clear = $app->request()->post('clear'); if ($clear == 1) { if (apc_clear_cache('user')) { $cleared = 'Cache was successfully cleared!'; } else { $cleared = 'Cache was not cleared!'; $log->error('Cache not cleared'); } } $app->flash('cleared', $cleared); $app->redirect('/admin'); } );
  • 72. $app->post('/admin/clear-cache', function() use ($app) { $log = $app->getLog(); Get the log from $app $cleared = null; $clear = $app->request()->post('clear'); if ($clear == 1) { if (apc_clear_cache('user')) { $cleared = 'Cache was successfully cleared!'; } else { $cleared = 'Cache was not cleared!'; $log->error('Cache not cleared'); } } $app->flash('cleared', $cleared); $app->redirect('/admin'); } );
  • 73. $app->post('/admin/clear-cache', function() use ($app) { $log = $app->getLog(); Get the log from $app $cleared = null; $clear = $app->request()->post('clear'); if ($clear == 1) { if (apc_clear_cache('user')) { $cleared = 'Cache was successfully cleared!'; } else { $cleared = 'Cache was not cleared!'; $log->error('Cache not cleared'); Error! } } $app->flash('cleared', $cleared); $app->redirect('/admin'); } );
  • 74. $app->post('/admin/clear-cache', function() use ($app) { $log = $app->getLog(); Get the log from $app $cleared = null; $clear = $app->request()->post('clear'); if ($clear == 1) { if (apc_clear_cache('user')) { $cleared = 'Cache was successfully cleared!'; } else { $cleared = 'Cache was not cleared!'; $log->error('Cache not cleared'); Error! } } $app->flash('cleared', $cleared); Flash message available in $app->redirect('/admin'); the next request. } );
  • 75. Middleware “. . . a Slim application can have middleware that may inspect, analyze, or modify the application environment, request, and response before and/or after the Slim application is invoked.” http://docs.slimframework.com/pages/middleware-overview/
  • 76. Hooks
  • 80. Hooks slim.before slim.after.dispatch slim.before.router slim.before.dispatch
  • 81. Hooks slim.before slim.after.dispatch slim.before.router slim.after.router slim.before.dispatch
  • 82. Hooks slim.before slim.after.dispatch slim.before.router slim.after.router slim.before.dispatch slim.after
  • 83. Hooks slim.before slim.after.dispatch slim.before.router slim.after.router slim.before.dispatch slim.after
  • 84. class MyMiddleware extends SlimMiddleware { public function call() { //The Slim application $app = $this->app; //The Environment object $env = $app->environment(); //The Request object $req = $app->request(); //The Response object $res = $app->response(); //Optionally call the next middleware $this->next->call(); } }
  • 85. class MyMiddleware extends SlimMiddleware Extend this { public function call() { //The Slim application $app = $this->app; //The Environment object $env = $app->environment(); //The Request object $req = $app->request(); //The Response object $res = $app->response(); //Optionally call the next middleware $this->next->call(); } }
  • 86. class MyMiddleware extends SlimMiddleware Extend this { public function call() { Define call() //The Slim application $app = $this->app; //The Environment object $env = $app->environment(); //The Request object $req = $app->request(); //The Response object $res = $app->response(); //Optionally call the next middleware $this->next->call(); } }
  • 87. class MyMiddleware extends SlimMiddleware Extend this { public function call() { Define call() //The Slim application $app = $this->app; //The Environment object $env = $app->environment(); Inspect, analyze, and modify! //The Request object $req = $app->request(); //The Response object $res = $app->response(); //Optionally call the next middleware $this->next->call(); } }
  • 88. class MyMiddleware extends SlimMiddleware Extend this { public function call() { Define call() //The Slim application $app = $this->app; //The Environment object $env = $app->environment(); Inspect, analyze, and modify! //The Request object $req = $app->request(); //The Response object $res = $app->response(); //Optionally call the next middleware $this->next->call(); On to the next! } }
  • 91. namespace TsfMiddleware; use ZendAuthenticationAuthenticationService; class Navigation extends SlimMiddleware { /** * @var ZendAuthenticationAuthenticationService */ private $auth; public function __construct(AuthenticationService $auth) { $this->auth = $auth; } public function call() { // . . . } }
  • 92. namespace TsfMiddleware; use ZendAuthenticationAuthenticationService; class Navigation extends SlimMiddleware extends { /** * @var ZendAuthenticationAuthenticationService */ private $auth; public function __construct(AuthenticationService $auth) { $this->auth = $auth; } public function call() { // . . . } }
  • 93. namespace TsfMiddleware; use ZendAuthenticationAuthenticationService; class Navigation extends SlimMiddleware extends { /** * @var ZendAuthenticationAuthenticationService */ private $auth; public function __construct(AuthenticationService $auth) { $this->auth = $auth; } Constructor injection public function call() FTW { // . . . } }
  • 94. public function call() { $app = $this->app; $auth = $this->auth; $req = $app->request(); $home = array('caption' => 'Home', 'href' => '/'); $admin = array('caption' => 'Admin', 'href' => '/admin'); $login = array('caption' => 'Login', 'href' => '/login'); $logout = array('caption' => 'Logout', 'href' => '/logout'); if ($auth->hasIdentity()) { $navigation = array($home, $admin, $logout); } else { $navigation = array($home, $login); } // . . . }
  • 95. public function call() { $app = $this->app; Arrays of $auth = $this->auth; nav items $req = $app->request(); $home = array('caption' => 'Home', 'href' => '/'); $admin = array('caption' => 'Admin', 'href' => '/admin'); $login = array('caption' => 'Login', 'href' => '/login'); $logout = array('caption' => 'Logout', 'href' => '/logout'); if ($auth->hasIdentity()) { $navigation = array($home, $admin, $logout); } else { $navigation = array($home, $login); } // . . . }
  • 96. public function call() { $app = $this->app; Arrays of $auth = $this->auth; nav items $req = $app->request(); $home = array('caption' => 'Home', 'href' => '/'); $admin = array('caption' => 'Admin', 'href' => '/admin'); $login = array('caption' => 'Login', 'href' => '/login'); $logout = array('caption' => 'Logout', 'href' => '/logout'); if ($auth->hasIdentity()) { $navigation = array($home, $admin, $logout); } else { $navigation = array($home, $login); } Nav differs based // . . . on auth status }
  • 97. public function call() { // . . . $this->app->hook('slim.before.router', function () use (...) { foreach ($navigation as &$link) { if ($link['href'] == $req->getPath()) { $link['class'] = 'active'; } else { $link['class'] = ''; } } $app->view() ->appendData(array('navigation' => $navigation)); } ); $this->next->call(); }
  • 98. public function call() { Delicious hook // . . . goodness $this->app->hook('slim.before.router', function () use (...) { foreach ($navigation as &$link) { if ($link['href'] == $req->getPath()) { $link['class'] = 'active'; } else { $link['class'] = ''; } } $app->view() ->appendData(array('navigation' => $navigation)); } ); $this->next->call(); }
  • 99. public function call() { Delicious hook // . . . goodness $this->app->hook('slim.before.router', function () use (...) { foreach ($navigation as &$link) { if ($link['href'] == $req->getPath()) { $link['class'] = 'active'; } else { $link['class'] = ''; } Match } dispatched path $app->view() ->appendData(array('navigation' => $navigation)); } ); $this->next->call(); }
  • 100. public function call() { Delicious hook // . . . goodness $this->app->hook('slim.before.router', function () use (...) { foreach ($navigation as &$link) { if ($link['href'] == $req->getPath()) { $link['class'] = 'active'; } else { $link['class'] = ''; } Match } dispatched path $app->view() ->appendData(array('navigation' => $navigation)); } ); Append $navigation to $this->next->call(); } view
  • 101. public function call() { Delicious hook // . . . goodness $this->app->hook('slim.before.router', function () use (...) { foreach ($navigation as &$link) { if ($link['href'] == $req->getPath()) { $link['class'] = 'active'; } else { $link['class'] = ''; } Match } dispatched path $app->view() ->appendData(array('navigation' => $navigation)); } ); Append $navigation to $this->next->call(); On to the next! } view
  • 102. Views
  • 103. Two great tastes that taste great together
  • 104. Twig
  • 108. Twig Concise Multiple inheritance Template oriented Fast
  • 109. Twig Concise Multiple inheritance Template oriented Blocks Fast
  • 110. Twig Concise Multiple inheritance Template oriented Blocks Fast Automatic escaping
  • 111. layout.html and index.html
  • 113. <title>{% block page_title %} {% endblock %}</title>
  • 114. <ul class="nav"> {% for link in navigation %} <li class="{{link.class}}"> <a href="{{link.href}}">{{link.caption}}</a> </li> {% endfor %} </ul>
  • 115. <h1>365 Days of Photography</h1> <h3>Photographer: Jeremy Kendall</h3> {% block content %} {% endblock %} <hr />
  • 117. {% extends 'layout.html' %} {% block page_title %}365.jeremykendall.net{% endblock %} {% block content %} {% for image in images %} <div class="row"> <div class="span6"> <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2> <p> <a href="/{{image.day}}"> <img src="{{image.sizes.size.5.source}}" /> </a> </p> <p>Posted {{image.posted|date("m/d/Y")}}</p> </div> </div> {% else %} <p>No images in project</p> {% endfor %} {% endblock %}
  • 118. {% extends 'layout.html' %} extends {% block page_title %}365.jeremykendall.net{% endblock %} {% block content %} {% for image in images %} <div class="row"> <div class="span6"> <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2> <p> <a href="/{{image.day}}"> <img src="{{image.sizes.size.5.source}}" /> </a> </p> <p>Posted {{image.posted|date("m/d/Y")}}</p> </div> </div> {% else %} <p>No images in project</p> {% endfor %} {% endblock %}
  • 119. {% extends 'layout.html' %} extends {% block page_title %}365.jeremykendall.net{% endblock %} {% block content %} <title /> {% for image in images %} <div class="row"> <div class="span6"> <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2> <p> <a href="/{{image.day}}"> <img src="{{image.sizes.size.5.source}}" /> </a> </p> <p>Posted {{image.posted|date("m/d/Y")}}</p> </div> </div> {% else %} <p>No images in project</p> {% endfor %} {% endblock %}
  • 120. {% extends 'layout.html' %} extends {% block page_title %}365.jeremykendall.net{% endblock %} {% block content %} <title /> {% for image in images %} <div class="row"> <div class="span6"> <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2> <p> <a href="/{{image.day}}"> <img src="{{image.sizes.size.5.source}}" /> </a> </p> <p>Posted {{image.posted|date("m/d/Y")}}</p> </div> </div> {% else %} <p>No images in project</p> {% endfor %} {% endblock %}
  • 121. {% extends 'layout.html' %} extends {% block page_title %}365.jeremykendall.net{% endblock %} {% block content %} <title /> {% for image in images %} iterator <div class="row"> <div class="span6"> <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2> <p> <a href="/{{image.day}}"> <img src="{{image.sizes.size.5.source}}" /> </a> </p> <p>Posted {{image.posted|date("m/d/Y")}}</p> </div> </div> {% else %} <p>No images in project</p> {% endfor %} {% endblock %}
  • 122. {% extends 'layout.html' %} extends {% block page_title %}365.jeremykendall.net{% endblock %} {% block content %} <title /> {% for image in images %} iterator <div class="row"> <div class="span6"> <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2> <p> <a href="/{{image.day}}"> <img src="{{image.sizes.size.5.source}}" /> </a> </p> <p>Posted {{image.posted|date("m/d/Y")}}</p> </div> </div> else {% else %} <p>No images in project</p> {% endfor %} {% endblock %}
  • 123. {% extends 'layout.html' %} extends {% block page_title %}365.jeremykendall.net{% endblock %} {% block content %} <title /> {% for image in images %} iterator <div class="row"> <div class="span6"> <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2> <p> <a href="/{{image.day}}"> <img src="{{image.sizes.size.5.source}}" /> </a> </p> <p>Posted {{image.posted|date("m/d/Y")}}</p> </div> </div> else {% else %} <p>No images in project</p> format {% endfor %} {% endblock %}
  • 125. {% extends 'layout.html' %} {% block page_title %}365.jeremykendall.net | Login{% endblock %} {% block content %} <div class="row"> <div class="span4"> <h2>Login</h2> {% if flash.error %} <p style="color: red;">{{flash.error}}</p> {% endif %} <form name="login" id="login" class="well" method="post"> // Login form . . . </form> </div> </div> {% endblock %}
  • 126. {% extends 'layout.html' %} {% block page_title %}365.jeremykendall.net | Login{% endblock %} {% block content %} <div class="row"> <div class="span4"> <h2>Login</h2> {% if flash.error %} <p style="color: red;">{{flash.error}}</p> {% endif %} <form name="login" id="login" class="well" method="post"> // Login form . . . </form> </div> </div> {% endblock %}
  • 127. The other views would be redundant
  • 128. GOTO 0
  • 130. Small but powerful GOTO 0 Excellent tools to write elegant code
  • 131. Small but powerful GOTO 0 Excellent tools to write elegant code Routing, middleware & hooks, views
  • 132. Small but powerful GOTO 0 Excellent tools to write elegant code Routing, middleware & hooks, views I just scratched the surface
  • 133. Read Slim: slimframework.com Twig: twig.sensiolabs.org Composer: getcomposer.org MicroPHP Manifesto: microphp.org Flaming Archer: http://git.io/rH0nrg