Intro to Lithium

7fca546408cc6d46ab158f06baed2535?s=47 Nate Abele
December 06, 2012

Intro to Lithium

A modern introduction to the Lithium PHP framework and ecosystem, as given at CodeWorks 2012.

7fca546408cc6d46ab158f06baed2535?s=128

Nate Abele

December 06, 2012
Tweet

Transcript

  1. CodeWorks 2012 Madison, Wisconsin December 6th, 2012 UN ION OF

    R AD RAPID A P PLICATION DEVEL O PMENT UN ION OF R AD RAPID A PMENT INTRO TO LITH I U M ˛ ˛ ·
  2. None
  3. None
  4. WTHAI? • Co-founder / lead dev, Lithium • Former lead

    dev, CakePHP • Founder, Union of RAD, LLC (let’s builds some apps!) • Startup junkie • @nateabele
  5. A short history of software architecture in PHP

  6. None
  7. Procedural vs. OOP

  8. Procedural vs. OOP

  9. PARADIGMS Event-Driven Aspect-Oriented Declarative Procedural Functional Object-Oriented

  10. Bootstrap & Dispatch

  11. index.php /posts ?url=/posts config/bootstrap.php config/bootstrap/libraries.php config/bootstrap/cache.php config/bootstrap/connections.php config/bootstrap/action.php ... lithium\action\Dispatcher::run(

    new lithium\action\Request() )
  12. new lithium\action\Request() $_GET $_POST $_SERVER

  13. Cache::config([ 'distributed' => [ 'adapter' => 'Memcached', 'host' => [

    '127.0.0.1’ => 100, '127.0.0.2:11211' => 200 ] ], 'local' => ['adapter' => 'Apc'] ]); // config/bootstrap/cache.php
  14. Connections::config(['default' => [ 'development' => [ 'type' => 'MongoDb', 'host'

    => 'localhost', 'database' => 'foo' ], 'production' => [ 'type' => 'MongoDb', 'host' => 'db.host', 'database' => 'foo.prod' ] ]]); // config/bootstrap/connections.php
  15. // config/bootstrap/action.php Dispatcher::applyFilter('run', function($self, $params, $chain) { // Detect the

    current environment Environment::set($params['request']); // Load routes include Libraries::get(true, 'path') . "/config/routes.php"; // Continue return $chain->next($self, $params, $chain); });
  16. Filters

  17. a.k.a. Aspect-Oriented Programming

  18. namespace lithium\action; class Dispatcher { //... public static function run($request,

    ...) { // Pass the request to the Router $params = $classes['router']::process($request); // Do some checking... // Get a controller... return static::call($controller); } } There are no routes here yet
  19. namespace lithium\action; class Dispatcher { //... public static function run($request,

    ...) { // Pass the request to the Router $params = $classes['router']::process($request); // Do some checking... // Get a controller... return static::call($controller); } }
  20. namespace lithium\action; class Dispatcher { //... public static function run($request,

    ...) { // Pass the request to the Router $params = $classes['router']::process($request); // Do some checking... // Get a controller... return static::call($controller); } } Dispatcher::applyFilter('run', function($self, $params, $chain) { // Detect the current environment Environment::set($params['request']); // Load routes include Libraries::get(true, 'path') . "/config/routes.php"; // Continue return $chain->next($self, $params, $chain); });
  21. Controllers

  22. namespace blog\controllers; use blog\models\Posts; class PostsController extends \lithium\action\Controller { public

    function index() { return ['posts' => Posts::all()]; } public function view() { return ['post' => Posts::first($this->request->id)]; } }
  23. use blog\models\Posts; use lithium\net\http\Router; use lithium\action\Response; use lithium\action\DispatchException; Router::connect('/posts', ['http:method'

    => 'GET'], function($request) { return new Response([ 'type' => 'json', 'body' => Posts::all()->to('json') ]); }); Router::connect('/posts', ['http:method' => 'POST'], function($request) { $post = Posts::create($request->data); $success = $post->save(); return new Response([ 'status' => $success ? 201 : 422, 'type' => 'json', 'body' => $success ? null : json_encode($post->errors()), 'location' => $success ? Router::match(['id' => $post->_id], $request) : null ]); }); ... // config/routes.php
  24. // config/routes.php (cont’d) Router::connect('/posts/{:id}', ['http:method' => 'GET'], function($request) { if

    (!$post = Posts::first($request->id)) { throw new DispatchException("Post not found"); } return new Response(['type' => 'json', 'body' => $post->to('json')]); }); Router::connect('/posts/{:id}', ['http:method' => 'PATCH'], function($request) { if (!$post = Posts::first($request->id)) { throw new DispatchException("Post not found"); } $status = $post->save($request->data) ? 200 : 422; $body = ($status == 200) ? json_encode($post->errors()) : $post->to('json'); return new Response(['type' => 'json'] + compact('status', 'body')); }); Router::connect('/posts/{:id}', ['http:method' => 'DELETE'], function($request) { if (!$post = Posts::first($request->id)) { throw new DispatchException("Post not found"); } return new Response(['status' => $post->delete() ? 204 : 422]); });
  25. class WeblogController < ActionController::Base def index @posts = Post.find :all

    respond_to do |format| format.html format.xml { render :xml => @posts.to_xml } format.rss { render :action => "feed.rxml" } end end end
  26. class WeblogController < ActionController::Base def index @posts = Post.find :all

    respond_to do |format| format.html format.xml { render :xml => @posts.to_xml } format.rss { render :action => "feed.rxml" } end end end
  27. class Media { $types = [ 'html' => [...], 'json'

    => [...], 'xml' => [...], '...' ]; } ['posts' => Posts::all()]
  28. Models

  29. // models/Posts.php namespace blog\models; class Posts extends \lithium\data\Model { protected

    $_schema = [ '_id' => ['type' => 'id'], 'author' => ['type' => 'id'], 'title' => ['type' => 'string'], 'body' => ['type' => 'string'], 'tags' => ['type' => 'string', 'array' => true], 'hits' => ['type' => 'integer', 'default' => 0] ]; public $validates = [ 'title' => ['notEmpty', 'message' => 'Please to be including a title?'], 'body' => ['notEmpty', 'message' => 'Now you\'re just defeating the purpose'] ]; public static function resetHits() { return static::update(['$set' => ['hits' => 0]], []); } public function timestamp($post) { return $post->_id->getTimestamp(); } }
  30. use blog\models\Posts; $recent = Posts::find('recent'); $posts = Posts::find('all'); foreach ($posts

    as $post) { echo " - {$post->title}\n"; } $post = Posts::find('first'); $post->tags = ['foo', 'bar']; // or just $post->save(['tags' => ['foo', 'bar']]) $titles = Posts::find('list'); ... <?=$this->form->select('post', $titles); ?>
  31. ... ... $tags = []; $posts->each(function($post) use (&$tags) { foreach

    ($post->tags as $tag) { $tags += [$tag => 0]; $tags[$tag]++; } return $post; }); // result: ['foo' => 7, 'bar' => 3]
  32. Plugins

  33. Plugins li3_docs: Generates API docs from API & Markdown li3_quality:

    Comprehensive code QA tool li3_access: Simple (and non-simple) access control li3_oauth: OAuth integration for various services (currently undergoing an overhaul to be even more badass) li3_facebook: Tools for writing Facebook apps
  34. Plugins $ li3 library extract newproject $ cd newproject $

    curl -s https://raw.github.com/uor/bootstrap/master/init | sh // (not really a plugin)
  35. Plugins li3_resources: A declarative resource framework for Lithium

  36. namespace blog\controllers; class Posts extends \li3_resources\action\Resource { public function index($request,

    $posts) { return $posts; } public function view($request, $post) { return $post; } public function add($request, $post) { return ($request->data) ? $post->save() : $post; } public function edit($request, $post) { return ($request->data) ? $post->save($request->data) : $post; } public function delete($request, $post) { return $post->delete(); } }
  37. public function add($request, $post) { return ($request->data) ? $post->save() :

    $post; } Data? Yep Nope Browser? Render add form Success? Yep Nope HTTP 201 Created Content-Type: application/json Location: http://app/posts/50c0a83e... { "_id": "50c0a83e37b2a...", ... } Browser? HTTP 422 Unprocessable Entity Content-Type: application/json { "title": "You didn’t include one, yo.", ... } Yep Nope Show form + errors Browser? Nope Yep Redirect POST /posts or GET /posts/add or POST /posts/add
  38. Community

  39. Community Open & inclusive (BSD code, BSD people) ~250 plugins,

    > 500 with forks Lithium 0.11 (2 months ago): ~700 commits ~60 contributors lithium101.com
  40. Resources http://lithify.me http://github.com/UnionOfRAD http://github.com/uor irc://irc.freenode.net/#li3 https://twitter.com/search?q=%23Li3 (#Li3) http://li3-angular.orchestra.io/