SlideShare une entreprise Scribd logo
1  sur  135
Télécharger pour lire hors ligne
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
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

Contenu connexe

Tendances

Perl web frameworks
Perl web frameworksPerl web frameworks
Perl web frameworks
diego_k
 
Silex: From nothing to an API
Silex: From nothing to an APISilex: From nothing to an API
Silex: From nothing to an API
chrisdkemper
 

Tendances (20)

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

Similaire à Keeping it Small: Getting to know the Slim Micro Framework

Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
Fabien Potencier
 
What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012
D
 
Nickolay Shmalenuk.Render api eng.DrupalCamp Kyiv 2011
Nickolay Shmalenuk.Render api eng.DrupalCamp Kyiv 2011Nickolay Shmalenuk.Render api eng.DrupalCamp Kyiv 2011
Nickolay Shmalenuk.Render api eng.DrupalCamp Kyiv 2011
camp_drupal_ua
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说
Ting Lv
 
第49回Php勉強会@関東 Datasource
第49回Php勉強会@関東 Datasource第49回Php勉強会@関東 Datasource
第49回Php勉強会@関東 Datasource
Kaz Watanabe
 

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

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

Plus de Jeremy Kendall

Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPLeveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHP
Jeremy Kendall
 
Game Changing Dependency Management
Game Changing Dependency ManagementGame Changing Dependency Management
Game Changing Dependency Management
Jeremy Kendall
 
Intro to #memtech PHP 2011-12-05
Intro to #memtech PHP   2011-12-05Intro to #memtech PHP   2011-12-05
Intro to #memtech PHP 2011-12-05
Jeremy Kendall
 
TDD in PHP - Memphis PHP 2011-08-25
TDD in PHP - Memphis PHP 2011-08-25TDD in PHP - Memphis PHP 2011-08-25
TDD in PHP - Memphis PHP 2011-08-25
Jeremy Kendall
 
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
Zend_Form to the Rescue - A Brief Introduction to Zend_FormZend_Form to the Rescue - A Brief Introduction to Zend_Form
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
Jeremy Kendall
 
Tdd in php a brief example
Tdd in php   a brief exampleTdd in php   a brief example
Tdd in php a brief example
Jeremy Kendall
 
Zero to Zend Framework in 10 minutes
Zero to Zend Framework in 10 minutesZero to Zend Framework in 10 minutes
Zero to Zend Framework in 10 minutes
Jeremy Kendall
 

Plus de Jeremy Kendall (14)

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

Dernier

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Dernier (20)

Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 

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
  • 37.
  • 38.
  • 39.
  • 40.
  • 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