SlideShare a Scribd company logo
1 of 135
Keeping it small
Getting to know the Slim micro framework
             @JeremyKendall
Jeremy
                 Kendall




raventools.com
Jeremy
                 Kendall
                 I love to code




raventools.com
Jeremy
                 Kendall
                 I love to code
                 I’m terribly forgetful


raventools.com
Jeremy
                 Kendall
                 I love to code
                 I’m terribly forgetful
                 I take pictures
raventools.com
Jeremy
                 Kendall
                 I love to code
                 I’m terribly forgetful
                 I take pictures
raventools.com
                 I work at Raven
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
“The purpose of middleware is to 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

What's hot

Webrtc mojo
Webrtc mojoWebrtc mojo
Webrtc mojobpmedley
 
Perl: Hate it for the Right Reasons
Perl: Hate it for the Right ReasonsPerl: Hate it for the Right Reasons
Perl: Hate it for the Right ReasonsMatt Follett
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony TechniquesKris Wallsmith
 
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'tCosimo Streppone
 
Bullet: The Functional PHP Micro-Framework
Bullet: The Functional PHP Micro-FrameworkBullet: The Functional PHP Micro-Framework
Bullet: The Functional PHP Micro-FrameworkVance Lucas
 
Building Cloud Castles
Building Cloud CastlesBuilding Cloud Castles
Building Cloud CastlesBen Scofield
 
With a Mighty Hammer
With a Mighty HammerWith a Mighty Hammer
With a Mighty HammerBen Scofield
 
Mojolicious - A new hope
Mojolicious - A new hopeMojolicious - A new hope
Mojolicious - A new hopeMarcus Ramberg
 
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)Dotan Dimet
 
Web Apps in Perl - HTTP 101
Web Apps in Perl - HTTP 101Web Apps in Perl - HTTP 101
Web Apps in Perl - HTTP 101hendrikvb
 
Great Developers Steal
Great Developers StealGreat Developers Steal
Great Developers StealBen Scofield
 
Mojolicious, real-time web framework
Mojolicious, real-time web frameworkMojolicious, real-time web framework
Mojolicious, real-time web frameworktaggg
 
Building Cloud Castles - LRUG
Building Cloud Castles - LRUGBuilding Cloud Castles - LRUG
Building Cloud Castles - LRUGBen Scofield
 
Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!Anatoly Sharifulin
 
Asynchronous programming patterns in Perl
Asynchronous programming patterns in PerlAsynchronous programming patterns in Perl
Asynchronous programming patterns in Perldeepfountainconsulting
 
Hello World on Slim Framework 3.x
Hello World on Slim Framework 3.xHello World on Slim Framework 3.x
Hello World on Slim Framework 3.xRyan Szrama
 
Scaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
Scaling Symfony2 apps with RabbitMQ - Symfony UK MeetupScaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
Scaling Symfony2 apps with RabbitMQ - Symfony UK MeetupKacper Gunia
 

What's hot (20)

RESTful web services
RESTful web servicesRESTful web services
RESTful web services
 
Webrtc mojo
Webrtc mojoWebrtc mojo
Webrtc mojo
 
Perl: Hate it for the Right Reasons
Perl: Hate it for the Right ReasonsPerl: Hate it for the Right Reasons
Perl: Hate it for the Right Reasons
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
 
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
 
Bullet: The Functional PHP Micro-Framework
Bullet: The Functional PHP Micro-FrameworkBullet: The Functional PHP Micro-Framework
Bullet: The Functional PHP Micro-Framework
 
Mojolicious
MojoliciousMojolicious
Mojolicious
 
Building Cloud Castles
Building Cloud CastlesBuilding Cloud Castles
Building Cloud Castles
 
With a Mighty Hammer
With a Mighty HammerWith a Mighty Hammer
With a Mighty Hammer
 
Mojolicious - A new hope
Mojolicious - A new hopeMojolicious - A new hope
Mojolicious - A new hope
 
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)
 
Web Apps in Perl - HTTP 101
Web Apps in Perl - HTTP 101Web Apps in Perl - HTTP 101
Web Apps in Perl - HTTP 101
 
Great Developers Steal
Great Developers StealGreat Developers Steal
Great Developers Steal
 
Mojolicious, real-time web framework
Mojolicious, real-time web frameworkMojolicious, real-time web framework
Mojolicious, real-time web framework
 
Building Cloud Castles - LRUG
Building Cloud Castles - LRUGBuilding Cloud Castles - LRUG
Building Cloud Castles - LRUG
 
Mojolicious on Steroids
Mojolicious on SteroidsMojolicious on Steroids
Mojolicious on Steroids
 
Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!
 
Asynchronous programming patterns in Perl
Asynchronous programming patterns in PerlAsynchronous programming patterns in Perl
Asynchronous programming patterns in Perl
 
Hello World on Slim Framework 3.x
Hello World on Slim Framework 3.xHello World on Slim Framework 3.x
Hello World on Slim Framework 3.x
 
Scaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
Scaling Symfony2 apps with RabbitMQ - Symfony UK MeetupScaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
Scaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
 

Similar to Keeping it small: Getting to know the Slim micro framework

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 2012D
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐいHisateru Tanaka
 
The Best (and Worst) of Django
The Best (and Worst) of DjangoThe Best (and Worst) of Django
The Best (and Worst) of DjangoJacob Kaplan-Moss
 
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"Ralf Eggert
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)tompunk
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207patter
 
Apostrophe
ApostropheApostrophe
Apostrophetompunk
 
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜崇之 清水
 
Simple Photo Processing and Web Display with Perl
Simple Photo Processing and Web Display with PerlSimple Photo Processing and Web Display with Perl
Simple Photo Processing and Web Display with PerlKent Cowgill
 
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?Alexandru Badiu
 
Building Lithium Apps
Building Lithium AppsBuilding Lithium Apps
Building Lithium AppsNate Abele
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of LithiumNate Abele
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Fabien Potencier
 
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 FrameworksNate Abele
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Shinya Ohyanagi
 
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.3Kris Wallsmith
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ EtsyNishan Subedi
 

Similar to Keeping it small: Getting to know the Slim micro framework (20)

Lithium Best
Lithium Best Lithium Best
Lithium Best
 
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
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい
 
The Best (and Worst) of Django
The Best (and Worst) of DjangoThe Best (and Worst) of Django
The Best (and Worst) of Django
 
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"
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207
 
Apostrophe
ApostropheApostrophe
Apostrophe
 
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
 
Simple Photo Processing and Web Display with Perl
Simple Photo Processing and Web Display with PerlSimple Photo Processing and Web Display with Perl
Simple Photo Processing and Web Display with Perl
 
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
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of Lithium
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
 
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
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2
 
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
 
Complex Sites with Silex
Complex Sites with SilexComplex Sites with Silex
Complex Sites with Silex
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 

More from 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 PHPJeremy 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 PHPJeremy Kendall
 
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) CodeJeremy Kendall
 
Game Changing Dependency Management
Game Changing Dependency ManagementGame Changing Dependency Management
Game Changing Dependency ManagementJeremy Kendall
 
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 GoodJeremy Kendall
 
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 GoodJeremy 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-05Jeremy 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-25Jeremy 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_FormJeremy Kendall
 
Zero to ZF in 10 Minutes
Zero to ZF in 10 MinutesZero to ZF in 10 Minutes
Zero to ZF in 10 MinutesJeremy Kendall
 
Tdd in php a brief example
Tdd in php   a brief exampleTdd in php   a brief example
Tdd in php a brief exampleJeremy Kendall
 
A Brief Introduction to Zend_Form
A Brief Introduction to Zend_FormA Brief Introduction to Zend_Form
A Brief Introduction to Zend_FormJeremy 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 minutesJeremy Kendall
 

More from 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
 

Recently uploaded

Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfRankYa
 
The Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfThe Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfSeasiaInfotech2
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsMiki Katsuragi
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesZilliz
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 

Recently uploaded (20)

Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdf
 
The Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfThe Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdf
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering Tips
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector Databases
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 

Keeping it small: Getting to know the Slim micro framework

  • 1. Keeping it small Getting to know the Slim micro framework @JeremyKendall
  • 2. Jeremy Kendall raventools.com
  • 3. Jeremy Kendall I love to code raventools.com
  • 4. Jeremy Kendall I love to code I’m terribly forgetful raventools.com
  • 5. Jeremy Kendall I love to code I’m terribly forgetful I take pictures raventools.com
  • 6. Jeremy Kendall I love to code I’m terribly forgetful I take pictures raventools.com I work at Raven
  • 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 “The purpose of middleware is to 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

Editor's Notes

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. WTF?\n
  25. Blame it on github and their repo name suggetsions\n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. Four views\n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. Can be anything that returns true for is_callable()\n\n
  46. Can be anything that returns true for is_callable()\n\n
  47. Can be anything that returns true for is_callable()\n\n
  48. Can be anything that returns true for is_callable()\n\n
  49. Can be anything that returns true for is_callable()\n\n
  50. Can be anything that returns true for is_callable()\n\n
  51. Can be anything that returns true for is_callable()\n\n
  52. Can be anything that returns true for is_callable()\n\n
  53. GET, including route parameters and conditions\n
  54. GET, including route parameters and conditions\n
  55. GET, including route parameters and conditions\n
  56. GET, including route parameters and conditions\n
  57. GET, including route parameters and conditions\n
  58. GET, including route parameters and conditions\n
  59. GET, including route parameters and conditions\n
  60. GET, including route parameters and conditions\n
  61. GET, including route parameters and conditions\n
  62. GET, including route parameters and conditions\n
  63. \n
  64. \n
  65. \n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. \n
  72. slim.before.router: after output buffering is turned on and before the router is dispatched. Once.\n\n
  73. slim.before.router: after output buffering is turned on and before the router is dispatched. Once.\n\n
  74. slim.before.router: after output buffering is turned on and before the router is dispatched. Once.\n\n
  75. slim.before.router: after output buffering is turned on and before the router is dispatched. Once.\n\n
  76. slim.before.router: after output buffering is turned on and before the router is dispatched. Once.\n\n
  77. slim.before.router: after output buffering is turned on and before the router is dispatched. Once.\n\n
  78. slim.before.router: after output buffering is turned on and before the router is dispatched. Once.\n\n
  79. Has access to app, environment, request &amp; response object\n
  80. Has access to app, environment, request &amp; response object\n
  81. Has access to app, environment, request &amp; response object\n
  82. Has access to app, environment, request &amp; response object\n
  83. Has access to app, environment, request &amp; response object\n
  84. \n
  85. \n
  86. \n
  87. \n
  88. \n
  89. \n
  90. \n
  91. \n
  92. \n
  93. \n
  94. \n
  95. I won&amp;#x2019;t cover bootstrap, but it kicks ass\n
  96. \n
  97. \n
  98. \n
  99. \n
  100. \n
  101. \n
  102. Too much boilerplate to show off, but here come the important parts\n
  103. \n
  104. \n
  105. \n
  106. \n
  107. \n
  108. \n
  109. \n
  110. \n
  111. \n
  112. \n
  113. \n
  114. \n
  115. \n
  116. \n
  117. \n
  118. \n
  119. \n
  120. \n
  121. \n
  122. \n
  123. \n
  124. \n