• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Keeping it Small: Getting to know the Slim Micro Framework
 

Keeping it Small: Getting to know the Slim Micro Framework

on

  • 851 views

 

Statistics

Views

Total Views
851
Views on SlideShare
849
Embed Views
2

Actions

Likes
2
Downloads
43
Comments
0

1 Embed 2

https://twitter.com 2

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Keeping it Small: Getting to know the Slim Micro Framework Keeping it Small: Getting to know the Slim Micro Framework Presentation Transcript

    • Keeping it smallGetting to know the Slim micro framework @JeremyKendall
    • JeremyKendall
    • JeremyKendallI love to code
    • JeremyKendallI love to codeI’m terribly forgetful
    • JeremyKendallI love to codeI’m terribly forgetfulI take pictures
    • JeremyKendallI love to codeI’m terribly forgetfulI take picturesI work at OpenSky
    • Micro framework?
    • Micro framework?Concise codebase
    • Micro framework?Concise codebaseClear codebase
    • Micro framework?Concise codebaseClear codebaseAddresses a small set of use cases
    • Micro framework?Concise codebaseClear codebaseAddresses a small set of use casesAddresses those use cases well
    • What is Slim?
    • What is Slim?Inspired by Sinatra
    • What is Slim?Inspired by SinatraFavors cleanliness over terseness
    • What is Slim?Inspired by SinatraFavors cleanliness over tersenessFavors common cases over edge cases
    • Installing Slim
    • RTFM
    • RTFM ;-)
    • Don’t forget .htaccess!RewriteEngine OnRewriteCond %{REQUEST_FILENAME} !-fRewriteRule ^ index.php [QSA,L]http://docs.slimframework.com/pages/routing-url-rewriting/
    • Hello world<?phprequire ../vendor/autoload.php;$app = new SlimSlim();$app->get(/hello/:name, function ($name) { echo "Hello, $name";});$app->run();
    • Hello world<?phprequire ../vendor/autoload.php;$app = new SlimSlim();$app->get(/hello/:name, function ($name) { echo "Hello, $name";});$app->run();
    • Hello world<?phprequire ../vendor/autoload.php;$app = new SlimSlim();$app->get(/hello/:name, function ($name) { echo "Hello, $name";});$app->run();
    • Hello world<?phprequire ../vendor/autoload.php;$app = new SlimSlim();$app->get(/hello/:name, function ($name) { echo "Hello, $name";});$app->run();
    • Hello world<?phprequire ../vendor/autoload.php;$app = new SlimSlim();$app->get(/hello/:name, function ($name) { echo "Hello, $name";});$app->run();
    • Let’s look at aSlim application
    • Flaming Archer
    • Flaming Archer wat
    • “Great repository names are short and memorable. Need inspiration? How about flaming-archer.”
    • Flaming Archer
    • Flaming ArcherPhoto 365 project
    • Flaming ArcherPhoto 365 projectBuilt in 4 days (Saturday through Tuesday)
    • Flaming ArcherPhoto 365 projectBuilt in 4 days (Saturday through Tuesday)Basic application — a few bells, no whistles
    • Flaming ArcherPhoto 365 projectBuilt in 4 days (Saturday through Tuesday)Basic application — a few bells, no whistles Routing
    • Flaming ArcherPhoto 365 projectBuilt in 4 days (Saturday through Tuesday)Basic application — a few bells, no whistles Routing Twig views
    • Flaming ArcherPhoto 365 projectBuilt 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: 7Files: 13Lines of Code (LOC): 876 Cyclomatic Complexity / Lines of Code: 0.04Comment Lines of Code (CLOC): 272Non-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)); });
    • RoutingHTTP Method $app->get(/, function () use ($app, $service) { $images = $service->findAll(); $app->render(index.html, array(images => $images)); } );
    • Routing Resource URIHTTP Method $app->get(/, function () use ($app, $service) { $images = $service->findAll(); $app->render(index.html, array(images => $images)); } );
    • Routing Resource URIHTTP Method Anonymous Function $app->get(/, function () use ($app, $service) { $images = $service->findAll(); $app->render(index.html, array(images => $images)); } );
    • Routing$app->get(/, function () use ($app, $service) { $images = $service->findAll(); $app->render(index.html, array(images => $images)); });
    • Routing$app->get(/, function () use ($app, $service) { Grabs all the pics $images = $service->findAll(); $app->render(index.html, array(images => $images)); });
    • Routing$app->get(/, function () use ($app, $service) { Grabs all the pics $images = $service->findAll(); $app->render(index.html, array(images => $images)); }); Passes array of image data to index.html
    • GET$app->get(/:day, function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render(images.html, $image); })->conditions(array(day => ([1-9]d?|[12]dd|3[0-5]d|36[0-6])));
    • GET URL parameter$app->get(/:day, function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render(images.html, $image); })->conditions(array(day => ([1-9]d?|[12]dd|3[0-5]d|36[0-6])));
    • GET ... gets passed as an URL parameter argument$app->get(/:day, function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render(images.html, $image); })->conditions(array(day => ([1-9]d?|[12]dd|3[0-5]d|36[0-6])));
    • GET ... gets passed as an URL parameter argument$app->get(/:day, function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render(images.html, $image); })->conditions(array(day => ([1-9]d?|[12]dd|3[0-5]d|36[0-6]))); Condition
    • GET ... gets passed as an URL parameter argument$app->get(/:day, function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render(images.html, $image); })->conditions(array(day => ([1-9]d?|[12]dd|3[0-5]d|36[0-6]))); Condition 1 to 366
    • GET$app->get(/:day, function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); 404! } $app->render(images.html, $image); })->conditions(array(day => ([1-9]d?|[12]dd|3[0-5]d|36[0-6])));
    • POST (with redirect)$app->post(/admin/add-photo, function() use ($app, $service) { $data = $app->request()->post(); $service->save($data); $app->redirect(/admin); });
    • POST (with redirect)$app->post(/admin/add-photo, function() use ($app, $service) { $data = $app->request()->post(); $service->save($data); $app->redirect(/admin); } $_POST data is in); the request object
    • POST (with redirect)$app->post(/admin/add-photo, function() use ($app, $service) { $data = $app->request()->post(); $service->save($data); $app->redirect(/admin); } $_POST data is in); the request object 302 Redirect
    • Multiple methods$app->map(/login, function() { // Login })->via(GET, POST);
    • Multiple methods Not an HTTP Method$app->map(/login, function() { // Login })->via(GET, POST);
    • Multiple methods Not an HTTP Method$app->map(/login, function() { // Login })->via(GET, POST); via() is the awesome sauce
    • Logging and flash messaging
    • $app->post(/admin/clear-cache, function() use ($app) { $log = $app->getLog(); $cleared = null; $clear = $app->request()->post(clear); if ($clear == 1) { if (apc_clear_cache(user)) { $cleared = Cache was successfully cleared!; } else { $cleared = Cache was not cleared!; $log->error(Cache not cleared); } } $app->flash(cleared, $cleared); $app->redirect(/admin); });
    • $app->post(/admin/clear-cache, function() use ($app) { $log = $app->getLog(); Get the log from $app $cleared = null; $clear = $app->request()->post(clear); if ($clear == 1) { if (apc_clear_cache(user)) { $cleared = Cache was successfully cleared!; } else { $cleared = Cache was not cleared!; $log->error(Cache not cleared); } } $app->flash(cleared, $cleared); $app->redirect(/admin); });
    • $app->post(/admin/clear-cache, function() use ($app) { $log = $app->getLog(); Get the log from $app $cleared = null; $clear = $app->request()->post(clear); if ($clear == 1) { if (apc_clear_cache(user)) { $cleared = Cache was successfully cleared!; } else { $cleared = Cache was not cleared!; $log->error(Cache not cleared); Error! } } $app->flash(cleared, $cleared); $app->redirect(/admin); });
    • $app->post(/admin/clear-cache, function() use ($app) { $log = $app->getLog(); Get the log from $app $cleared = null; $clear = $app->request()->post(clear); if ($clear == 1) { if (apc_clear_cache(user)) { $cleared = Cache was successfully cleared!; } else { $cleared = Cache was not cleared!; $log->error(Cache not cleared); Error! } } $app->flash(cleared, $cleared); Flash message available in $app->redirect(/admin); the next request. });
    • Middleware“. . . a Slim application can have middlewarethat may inspect, analyze, or modifythe application environment, request, andresponse before and/or after the Slimapplication is invoked.” http://docs.slimframework.com/pages/middleware-overview/
    • Hooks
    • Hooksslim.before
    • Hooksslim.beforeslim.before.router
    • Hooksslim.beforeslim.before.routerslim.before.dispatch
    • Hooksslim.before slim.after.dispatchslim.before.routerslim.before.dispatch
    • Hooksslim.before slim.after.dispatchslim.before.router slim.after.routerslim.before.dispatch
    • Hooksslim.before slim.after.dispatchslim.before.router slim.after.routerslim.before.dispatch slim.after
    • Hooksslim.before slim.after.dispatchslim.before.router slim.after.routerslim.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 tastesthat taste great together
    • Twig
    • TwigConcise
    • TwigConciseTemplate oriented
    • TwigConciseTemplate orientedFast
    • TwigConcise Multiple inheritanceTemplate orientedFast
    • TwigConcise Multiple inheritanceTemplate oriented BlocksFast
    • TwigConcise Multiple inheritanceTemplate oriented BlocksFast Automatic escaping
    • layout.html andindex.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 viewswould be redundant
    • GOTO 0
    • Small but powerful GOTO 0
    • Small but powerful GOTO 0Excellent tools to write elegant code
    • Small but powerful GOTO 0Excellent tools to write elegant codeRouting, middleware & hooks, views
    • Small but powerful GOTO 0Excellent tools to write elegant codeRouting, middleware & hooks, viewsI just scratched the surface
    • ReadSlim: slimframework.comTwig: twig.sensiolabs.orgComposer: getcomposer.orgMicroPHP Manifesto: microphp.orgFlaming Archer: http://git.io/rH0nrg
    • Questions?
    • Thanks!jeremy@jeremykendall.net @jeremykendall