SlideShare ist ein Scribd-Unternehmen logo
1 von 135
Keeping it small
Getting to know the Slim micro framework
          kendall@raventools.com
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!

kendall@raventools.com

Weitere ähnliche Inhalte

Was ist angesagt?

Dependency Injection IPC 201
Dependency Injection IPC 201Dependency Injection IPC 201
Dependency Injection IPC 201
Fabien Potencier
 
Crazy things done on PHP
Crazy things done on PHPCrazy things done on PHP
Crazy things done on PHP
Taras Kalapun
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 World
Fabien Potencier
 
Speed up your developments with Symfony2
Speed up your developments with Symfony2Speed up your developments with Symfony2
Speed up your developments with Symfony2
Hugo Hamon
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
Bill Chang
 

Was ist angesagt? (20)

The State of Lithium
The State of LithiumThe State of Lithium
The State of Lithium
 
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
 
The jQuery Divide
The jQuery DivideThe jQuery Divide
The jQuery Divide
 
The Origin of Lithium
The Origin of LithiumThe Origin of Lithium
The Origin of Lithium
 
Nubilus Perl
Nubilus PerlNubilus Perl
Nubilus Perl
 
Dependency Injection IPC 201
Dependency Injection IPC 201Dependency Injection IPC 201
Dependency Injection IPC 201
 
Current state-of-php
Current state-of-phpCurrent state-of-php
Current state-of-php
 
Perl Web Client
Perl Web ClientPerl Web Client
Perl Web Client
 
Crazy things done on PHP
Crazy things done on PHPCrazy things done on PHP
Crazy things done on PHP
 
Corinna Status 2022.pptx
Corinna Status 2022.pptxCorinna Status 2022.pptx
Corinna Status 2022.pptx
 
Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 World
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et Pimple
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
Your JavaScript Library
Your JavaScript LibraryYour JavaScript Library
Your JavaScript Library
 
All I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web FrameworkAll I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web Framework
 
Speed up your developments with Symfony2
Speed up your developments with Symfony2Speed up your developments with Symfony2
Speed up your developments with Symfony2
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
 

Andere mochten auch (6)

Examen de tecnologia
Examen de tecnologiaExamen de tecnologia
Examen de tecnologia
 
Examen de tecnologia
Examen de tecnologiaExamen de tecnologia
Examen de tecnologia
 
Himpervinculos
HimpervinculosHimpervinculos
Himpervinculos
 
Link Building: The Best Marketing Strategy You've Never Heard Of
Link Building: The Best Marketing Strategy You've Never Heard OfLink Building: The Best Marketing Strategy You've Never Heard Of
Link Building: The Best Marketing Strategy You've Never Heard Of
 
Los valores
Los valoresLos valores
Los valores
 
Optimizing your site for contextual ads: SEO, Design and Content
Optimizing your site for contextual ads: SEO, Design and ContentOptimizing your site for contextual ads: SEO, Design and Content
Optimizing your site for contextual ads: SEO, Design and Content
 

Ähnlich wie Keeping It Small with Slim

Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkKeeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro Framework
Jeremy Kendall
 
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
 
The Best (and Worst) of Django
The Best (and Worst) of DjangoThe Best (and Worst) of Django
The Best (and Worst) of Django
Jacob Kaplan-Moss
 
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 〜
崇之 清水
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207
patter
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Paulo Ragonha
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
Fabien Potencier
 
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
 

Ähnlich wie Keeping It Small with Slim (20)

Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkKeeping it Small: Getting to know the Slim Micro Framework
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 2012
 
The Best (and Worst) of Django
The Best (and Worst) of DjangoThe Best (and Worst) of Django
The Best (and Worst) of Django
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい
 
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"
 
Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)
 
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 〜
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207
 
Apostrophe
ApostropheApostrophe
Apostrophe
 
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?
 
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
 
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
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
Mojolicious
MojoliciousMojolicious
Mojolicious
 
QConSP 2015 - Dicas de Performance para Aplicações Web
QConSP 2015 - Dicas de Performance para Aplicações WebQConSP 2015 - Dicas de Performance para Aplicações Web
QConSP 2015 - Dicas de Performance para Aplicações Web
 
Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12
 
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
 

Mehr von Raven Tools

How to create a traffic machine for your content
How to create a traffic machine for your contentHow to create a traffic machine for your content
How to create a traffic machine for your content
Raven Tools
 

Mehr von Raven Tools (20)

Fully Optimized
Fully OptimizedFully Optimized
Fully Optimized
 
Essential On-Page SEO
Essential On-Page SEOEssential On-Page SEO
Essential On-Page SEO
 
On-Page SEO for Mobile
On-Page SEO for MobileOn-Page SEO for Mobile
On-Page SEO for Mobile
 
How to Establish Social Proof Using Social Love
How to Establish Social Proof Using Social LoveHow to Establish Social Proof Using Social Love
How to Establish Social Proof Using Social Love
 
How to Discover Marketing Opportunities For Any Website
How to Discover Marketing Opportunities For Any WebsiteHow to Discover Marketing Opportunities For Any Website
How to Discover Marketing Opportunities For Any Website
 
The 5 Most Important Benchmarks for a New SEO Client
The 5 Most Important Benchmarks for a New SEO ClientThe 5 Most Important Benchmarks for a New SEO Client
The 5 Most Important Benchmarks for a New SEO Client
 
Mobile Marketing Misconceptions and the Real Truth
Mobile Marketing Misconceptions and the Real TruthMobile Marketing Misconceptions and the Real Truth
Mobile Marketing Misconceptions and the Real Truth
 
Eliminate Google Analytics Confusion with Raven
Eliminate Google Analytics Confusion with RavenEliminate Google Analytics Confusion with Raven
Eliminate Google Analytics Confusion with Raven
 
How to create a traffic machine for your content
How to create a traffic machine for your contentHow to create a traffic machine for your content
How to create a traffic machine for your content
 
Bootstrap Marketing for Entrepreneurs and Startups
Bootstrap Marketing for Entrepreneurs and StartupsBootstrap Marketing for Entrepreneurs and Startups
Bootstrap Marketing for Entrepreneurs and Startups
 
Content Marketing: 50 Words That Will Help Improve Your Next 500 Blog Posts
Content Marketing: 50 Words That Will Help Improve Your Next 500 Blog PostsContent Marketing: 50 Words That Will Help Improve Your Next 500 Blog Posts
Content Marketing: 50 Words That Will Help Improve Your Next 500 Blog Posts
 
The Future of Search in a Social World
The Future of Search in a Social WorldThe Future of Search in a Social World
The Future of Search in a Social World
 
Measuring Social Media: A Tweet Is Worth WHAT?
Measuring Social Media: A Tweet Is Worth WHAT?Measuring Social Media: A Tweet Is Worth WHAT?
Measuring Social Media: A Tweet Is Worth WHAT?
 
Essential Content Marketing Templates
Essential Content Marketing TemplatesEssential Content Marketing Templates
Essential Content Marketing Templates
 
Why Press Releases Are (Still) Useful for Marketers
Why Press Releases Are (Still) Useful for MarketersWhy Press Releases Are (Still) Useful for Marketers
Why Press Releases Are (Still) Useful for Marketers
 
Measure It! Social media metrics made simple
Measure It! Social media metrics made simpleMeasure It! Social media metrics made simple
Measure It! Social media metrics made simple
 
Social Media Crimes of Passion
Social Media Crimes of PassionSocial Media Crimes of Passion
Social Media Crimes of Passion
 
SEO performance metrics that actually matter
SEO performance metrics that actually matterSEO performance metrics that actually matter
SEO performance metrics that actually matter
 
Tools Shoot Out - SEMPDX SearchFest
Tools Shoot Out - SEMPDX SearchFestTools Shoot Out - SEMPDX SearchFest
Tools Shoot Out - SEMPDX SearchFest
 
Social Media and the Art of Being Interested
Social Media and the Art of Being InterestedSocial Media and the Art of Being Interested
Social Media and the Art of Being Interested
 

Kürzlich hochgeladen

Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Victor Rentea
 

Kürzlich hochgeladen (20)

Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptx
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 

Keeping It Small with Slim

  • 1. Keeping it small Getting to know the Slim micro framework kendall@raventools.com
  • 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

Hinweis der Redaktion

  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