Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Intro to Lithium

Nate Abele
December 06, 2012

Intro to Lithium

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

Nate Abele

December 06, 2012
Tweet

More Decks by Nate Abele

Other Decks in Programming

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. WTHAI? • Co-founder / lead dev, Lithium • Former lead

    dev, CakePHP • Founder, Union of RAD, LLC (let’s builds some apps!) • Startup junkie • @nateabele
  3. Cache::config([ 'distributed' => [ 'adapter' => 'Memcached', 'host' => [

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

    => 'localhost', 'database' => 'foo' ], 'production' => [ 'type' => 'MongoDb', 'host' => 'db.host', 'database' => 'foo.prod' ] ]]); // config/bootstrap/connections.php
  5. // 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); });
  6. 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
  7. 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); } }
  8. 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); });
  9. 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)]; } }
  10. 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
  11. // 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]); });
  12. 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
  13. 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
  14. class Media { $types = [ 'html' => [...], 'json'

    => [...], 'xml' => [...], '...' ]; } ['posts' => Posts::all()]
  15. // 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(); } }
  16. 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); ?>
  17. ... ... $tags = []; $posts->each(function($post) use (&$tags) { foreach

    ($post->tags as $tag) { $tags += [$tag => 0]; $tags[$tag]++; } return $post; }); // result: ['foo' => 7, 'bar' => 3]
  18. 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
  19. Plugins $ li3 library extract newproject $ cd newproject $

    curl -s https://raw.github.com/uor/bootstrap/master/init | sh // (not really a plugin)
  20. 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(); } }
  21. 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
  22. Community Open & inclusive (BSD code, BSD people) ~250 plugins,

    > 500 with forks Lithium 0.11 (2 months ago): ~700 commits ~60 contributors lithium101.com