Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Building Lithium Apps

10,840 views

Published on

Building Lithium Apps (Like a Boss) was a workshop presented on the structure and philosophy of the Lithium framework and its applications, and how best to take advantage of them.

Published in: Technology
  • And thanks for the event and posting slides Nate!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • If a filter is being applied to the Save function across all models, it shouldn't be a filter. Create a base class for your model classes and do it there.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Would love to hear an explanation of slide 49.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Building Lithium Apps

  1. 1. BUILDING LITHIUM APPS LIKE A BOSS Pace University, NYC · 10-19-2010
  2. 2. ARCHITECTURE
  3. 3. architecture
  4. 4. noun ! the complex or carefully designed structure of ! something
  5. 5. noun ! Orderly arrangement of parts; structure
  6. 6. noun ! the conceptual structure and logical organization of a computer ! or computer-based system
  7. 7. “ARCHITECTURE” IN PHP
  8. 8. “PROCEDURAL”
  9. 9. “OBJECT-ORIENTED”
  10. 10. Procedural Object-Oriented
  11. 11. Procedural Object-Oriented
  12. 12. Aspect-Oriented Event-Driven Procedural PARADIGMS Declarative Functional Object-Oriented
  13. 13. Aspect-Oriented Event-Driven Procedural PARADIGMS Declarative Functional Object-Oriented
  14. 14. Aspect-Oriented Event-Driven Procedural PARADIGMS Declarative Functional Object-Oriented
  15. 15. Aspect-Oriented Event-Driven Procedural PARADIGMS Declarative Functional Object-Oriented
  16. 16. BOOTSTRAP / DISPATCH
  17. 17. index.php?url=/posts /posts
  18. 18. index.php?url=/posts config/bootstrap.php config/bootstrap/libraries.php /posts config/bootstrap/cache.php config/bootstrap/connections.php config/bootstrap/action.php ...
  19. 19. index.php?url=/posts config/bootstrap.php config/bootstrap/libraries.php /posts config/bootstrap/cache.php config/bootstrap/connections.php config/bootstrap/action.php ... lithiumactionDispatcher::run( new lithiumactionRequest() )
  20. 20. config/bootstrap/cache.php Cache::config(array( 'local' => array( 'adapter' => 'Apc' ), 'distributed' => array( 'adapter' => 'Memcached', 'servers' => array( array('127.0.0.1', 11211, 100) array('127.0.0.2', 11211, 100) ) ) ));
  21. 21. config/bootstrap/connections.php Connections::add('default', array( 'development' => array( 'type' => 'MongoDb', 'host' => 'localhost', 'database' => 'foo' ), 'production' => array( 'type' => 'MongoDb', 'host' => 'db.host', 'database' => 'foo.prod' ) )); Connections::add('cassandra', array( 'type' => 'Cassandra', 'keyspace' => 'foo' ));
  22. 22. STATICS & STATE
  23. 23. new lithiumactionRequest() $_GET $_POST $_SERVER
  24. 24. CONFIGURATION REQUEST
  25. 25. FILTERS
  26. 26. A.K.A. ASPECT-ORIENTED PROGRAMMING
  27. 27. There are no routes here yet lithiumactionDispatcher { //... 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); } }
  28. 28. config/bootstrap/action.php Dispatcher::applyFilter('run', function($self, $params, $chain) { foreach (Libraries::get() as $name => $config) { // Some sanity checking goes here... include "{$config['path']}/config/routes.php"; } return $chain->next($self, $params, $chain); });
  29. 29. lithiumactionDispatcher { //... public static function run($request, ...) { // Pass the request to the Router $params = $classes['router']::process($request); // Do some checking... // Get a controller... // $controller = ... return static::call($controller); } } Dispatcher::applyFilter('run', function($self, $params, $chain) { foreach (Libraries::get() as $name => $config) { // Some sanity checking goes here... include "{$config['path']}/config/routes.php"; } return $chain->next($self, $params, $chain); });
  30. 30. CONTROLLERS
  31. 31. use appmodelsPosts; appcontrollersPostsController { public function index() { $posts = Posts::all(); return compact('posts'); } }
  32. 32. 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
  33. 33. 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
  34. 34. lithiumnethttpMedia { $formats = array( array( 'html' => array(...), 'posts' => ... 'json' => array(...), ) 'xml' => array(...), '...' ); }
  35. 35. new lithiumactionResponse()
  36. 36. Dispatcher index.php Controller new lithiumactionResponse() View
  37. 37. REQUEST / RESPONSE
  38. 38. Router::connect('/photos/{:id:[0-9a-f]{24}}.jpg', array(), function($request) { return new Response(array( 'type' => 'jpg', 'body' => Photo::first($request->id)->file->getBytes() )); });
  39. 39. /** * This handles both the home page and the archives pages. */ Router::connect('/{:page}', array('page' => 1), array( 'pattern' => '@^/?(?P<page>d+)$|^/?$@', 'keys' => array('page' => true), 'handler' => function($request) use ($render) { $page = intval($request->page); $posts = Posts::recent(compact('page')); return new Response(array( 'body' => $render('index', compact('posts'), compact('request')) )); }) ); /** * Handles adding new posts. */ Router::connect('/add', array(), function($request) use ($render) { $post = Posts::create(); if (($request->data) && $post->save($request->data)) { return new Response(array('location' => '/')); } return new Response(array( 'body' => $render('edit', compact('post'), compact('request')) )); }); /** * Edits existing pages. */ Router::connect('/{:slug:[a-z0-9-]+}/edit', array('edit' => true), function($request) use ($render) { $conditions = array('slug' => $request->slug); $post = Posts::first(compact('conditions')); if (($request->data) && $post->save($request->data)) { return new Response(compact('request') + array('location' => array('slug' => $post->slug))); } return new Response(array( 'body' => $render('edit', compact('post'), compact('request')) )); }); /** * Handles single page views. */ Router::connect('/{:slug:[a-z0-9-]+}', array(), function($request) use ($render) { $conditions = array('slug' => $request->slug, 'published' => true); $post = Posts::first(compact('conditions')); return new Response(array( 'status' => $post ? 200 : 404, 'body' => $render($post ? 'view' : 'error', compact('post'), compact('request')) )); });
  40. 40. WAX ON, WAX OFF
  41. 41. DEPENDENCIES
  42. 42. class User { public function somethingUseful() { Logger::info("Something useful is happening"); // ... } }
  43. 43. FALE class User { public function somethingUseful() { Logger::info("Something useful is happening"); // ... } b!! } Super dum
  44. 44. COUPLING RELEVANCE
  45. 45. Post::applyFilter('save', function($self, $params, $chain) { $entity =& $params['entity']; if (!$entity->exists()) { $entity->created = time(); } return $chain->next($self, $params, $chain); });
  46. 46. FALE Post::applyFilter('save', function($self, $params, $chain) { $entity =& $params['entity']; if (!$entity->exists()) { $entity->created = time(); } return $chain->next($self, $params, $chain); });
  47. 47. class WebService { protected $_classes = array( 'socket' => 'lithiumnethttpsocketContext', 'request' => 'lithiumnethttpRequest', 'response' => 'lithiumnethttpResponse' ); }
  48. 48. use appmodelsPost; use li3_elasticsearchextensionsdatabehaviorSearchable; Post::applyFilter('save', function($self, $params, $chain) { $entity =& $params['entity']; $id = $entity->guid; Searchable::add('public_posts', 'stream', $id, $entity->to('array')); return $chain->next($self, $params, $chain); });
  49. 49. REUSABILITY
  50. 50. config/bootstrap/libraries.php Make conditional define('LITHIUM_APP_PATH', dirname(dirname(__DIR__))); define('LITHIUM_LIBRARY_PATH', LITHIUM_APP_PATH . '/../libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/StaticObject.php'; Remove when require LITHIUM_LIBRARY_PATH . '/lithium/util/Collection.php'; require LITHIUM_LIBRARY_PATH . '/lithium/util/collection/Filters.php'; require LITHIUM_LIBRARY_PATH . '/lithium/util/Inflector.php'; require require LITHIUM_LIBRARY_PATH LITHIUM_LIBRARY_PATH . . '/lithium/util/String.php'; '/lithium/core/Adaptable.php'; plugin-izing require LITHIUM_LIBRARY_PATH . '/lithium/core/Environment.php'; require LITHIUM_LIBRARY_PATH . '/lithium/net/Message.php'; require LITHIUM_LIBRARY_PATH . '/lithium/net/http/Message.php'; require LITHIUM_LIBRARY_PATH . '/lithium/net/http/Media.php'; require LITHIUM_LIBRARY_PATH . '/lithium/net/http/Request.php'; require ....
  51. 51. Libraries::add('my_application', array( 'default' => true ));
  52. 52. my_awesome_plugin/ config/ bootstrap.php Required routes.php Optional anything_in_an_app/ Happens in the filter we saw in action.php Unless you Libraries::add('my_awesome_plugin', array( 'bootstrap' => false disable it ));
  53. 53. DEMO...

×