Advertisement
Advertisement

More Related Content

Advertisement

Lithium Best

  1. Lithium (#li3) The framework with the best of both worlds
  2. Richard McIntyre: @mackstar ★ Lithium core team ★ Freelancer - Leeds/Manchester ★ Most recently at BBC, Mamas & Papas, UOR ★ Engineer on BBC Olympics App ★ Lived in Japan 15 years ★ Done PHP for donkeys
  3. Frameworks
  4. I’ve seen it ✦ Symfony 2 ✦ Merb ✦ Zend Framework ✦ Sinatra ✦ CakePHP ✦ Spring ✦ Code Igniter ✦ Stripes (Java) ✦ Drupal 6&7 ✦ Django Wordpress ✦ Silex ✦ ✦ Rails
  5. many frameworks are like this
  6. Each software solution should be in proportion to the problem being solved.
  7. Each software solution should be in proportion to the problem being solved. -some lazy bugger
  8. Each software solution should be in proportion to the problem being solved. -some lazy bugger me
  9. <?php use BehatMinkExtensionContextMinkContext; class FeatureContext extends MinkContext { /** * @Then /^I wait for the suggestion box to appear$/ */ public function myNameHasAMeaninglessExistance() { $this->getSession()->wait(5000, "$('.suggestions-results').children().length > 0"); } }
  10. All problems in computer science can be solved by another level of indirection. Except for the problem of too many layers of indirection. Butler Lampson, David Wheeler
  11. Code Sexy-ness There’s nothing like the look of beautiful Ruby code in the morning. Gracefully colored by TextMate and rendered in Bitstream Vera pt 12. @dhh
  12. Developer Happy-ness Any fool can write code that a computer can understand. Good programmers write code that humans can understand. –Martin Fowler
  13. Light Footprint
  14. Light Footprint
  15. Looking for a hackers Framework? Light Footprint
  16. Fast, Flexible and Rad
  17. World 1 Full Stack Framework Convention Over Configuration
  18. github.com/UnionOfRad/framework
  19. The Photo Blog Tutorial github.com/nateabele/photoblog
  20. Model namespace photoblogmodels; class Photos extends lithiumdataModel { public $validates = array(); protected $_meta = array('source' => 'fs.files'); public function save($entity, $data = null, array $options = array()) { if ($data) { $entity->set($data); } if ($entity->tags && !is_array($entity->tags)) { $entity->tags = array_map('trim', explode(',', $entity->tags)); } return parent::save($entity, null, $options); } }
  21. Model namespace photoblogmodels; class Photos extends lithiumdataModel { public $validates = array(); protected $_meta = array('source' => 'fs.files'); public function save($entity, $data = null, array $options = array()) { if ($data) { $entity->set($data); } if ($entity->tags && !is_array($entity->tags)) { $entity->tags = array_map('trim', explode(',', $entity->tags)); } return parent::save($entity, null, $options); } }
  22. Model namespace photoblogmodels; class Photos extends lithiumdataModel { public $validates = array(); protected $_meta = array('source' => 'fs.files'); public function save($entity, $data = null, array $options = array()) { if ($data) { $entity->set($data); } if ($entity->tags && !is_array($entity->tags)) { $entity->tags = array_map('trim', explode(',', $entity->tags)); } return parent::save($entity, null, $options); } }
  23. Views <?=$this->form->create($photo, array('type' => 'file')); ?> <?=$this->form->field('title'); ?> <?=$this->form->field('description'); ?> <?php if (!$photo->exists()): ?> <?=$this->form->field('file', array('type' => 'file')); ?> <?php endif; ?> <?=$this->form->field('tags', array('label' => 'Add tags separated by commas')); ?> <?=$this->form->submit('Save'); ?> <?=$this->form->end(); ?>
  24. Views <?=$this->form->create($photo, array('type' => 'file')); ?> <?=$this->form->field('title'); ?> <?=$this->form->field('description'); ?> <?php if (!$photo->exists()): ?> <?=$this->form->field('file', array('type' => 'file')); ?> <?php endif; ?> <?=$this->form->field('tags', array('label' => 'Add tags separated by commas')); ?> <?=$this->form->submit('Save'); ?> <?=$this->form->end(); ?>
  25. Views <?=$this->form->create($photo, array('type' => 'file')); ?> <?=$this->form->field('title'); ?> <?=$this->form->field('description'); ?> <?php if (!$photo->exists()): ?> <?=$this->form->field('file', array('type' => 'file')); ?> <?php endif; ?> <?=$this->form->field('tags', array('label' => 'Add tags separated by commas')); ?> <?=$this->form->submit('Save'); ?> <?=$this->form->end(); ?>
  26. <h1> <?=$this->title($photo->title); ?> <em>[ <?=$this->html->link( 'edit', array('Photos::edit', 'id' => $photo->_id) ); ?> ]</em> </h1> <p><?=$photo->description; ?></p> <?php if ($photo->tags): ?> Tags: <?php foreach ($photo->tags as $tag): ?> <?=$this->html->link( $tag, array('Photos::index', 'args' => array($tag)) ); ?> <?php endforeach; ?> <?php endif; ?> <?=$this->html->image( "/photos/view/{$photo->_id}.jpeg", array( 'alt' => $photo->title, 'width' => "100%" ) ); ?>
  27. <h1> <?=$this->title($photo->title); ?> <em>[ <?=$this->html->link( 'edit', array('Photos::edit', 'id' => $photo->_id) ); ?> ]</em> </h1> <p><?=$photo->description; ?></p> <?php if ($photo->tags): ?> Tags: <?php foreach ($photo->tags as $tag): ?> <?=$this->html->link( $tag, array('Photos::index', 'args' => array($tag)) ); ?> <?php endforeach; ?> <?php endif; ?> <?=$this->html->image( "/photos/view/{$photo->_id}.jpeg", array( 'alt' => $photo->title, 'width' => "100%" ) ); ?>
  28. <h1> <?=$this->title($photo->title); ?> <em>[ <?=$this->html->link( 'edit', array('Photos::edit', 'id' => $photo->_id) ); ?> ]</em> </h1> <p><?=$photo->description; ?></p> <?php if ($photo->tags): ?> Tags: <?php foreach ($photo->tags as $tag): ?> <?=$this->html->link( $tag, array('Photos::index', 'args' => array($tag)) ); ?> <?php endforeach; ?> <?php endif; ?> <?=$this->html->image( "/photos/view/{$photo->_id}.jpeg", array( 'alt' => $photo->title, 'width' => "100%" ) ); ?>
  29. Controller namespace photoblogcontrollers; use photoblogmodelsPhotos; use li3_geoextensionsGeocoder; class PhotosController extends lithiumactionController { public function index($tags = null) { $conditions = $tags ? compact('tags') : array(); $photos = Photos::all(compact('conditions')); return compact('photos'); } public function view() { $photo = Photos::first($this->request->id); return compact('photo'); }
  30. Controller namespace photoblogcontrollers; use photoblogmodelsPhotos; use li3_geoextensionsGeocoder; class PhotosController extends lithiumactionController { public function index($tags = null) { $conditions = $tags ? compact('tags') : array(); $photos = Photos::all(compact('conditions')); return compact('photos'); } public function view() { $photo = Photos::first($this->request->id); return compact('photo'); }
  31. Controller namespace photoblogcontrollers; use photoblogmodelsPhotos; use li3_geoextensionsGeocoder; class PhotosController extends lithiumactionController { public function index($tags = null) { $conditions = $tags ? compact('tags') : array(); $photos = Photos::all(compact('conditions')); return compact('photos'); } public function view() { $photo = Photos::first($this->request->id); return compact('photo'); }
  32. public function near($place = null) { $this->_render['template'] = 'index'; $coords = Geocoder::find('google', $place); $photos = Photos::within(array($coords, $coords), array('limit' => 1)); return compact('photos'); } public function add() { $photo = Photos::create(); if (($this->request->data) && $photo->save($this->request->data)) { $this->redirect(array('Photos::view', 'id' => $photo->_id)); } $this->_render['template'] = 'edit'; return compact('photo'); } public function edit() { $photo = Photos::find($this->request->id); if (!$photo) { $this->redirect('Photos::index'); } if (($this->request->data) && $photo->save($this->request->data)) { $this->redirect(array('Photos::view', 'id' => $photo->_id)); } return compact('photo'); }
  33. public function near($place = null) { $this->_render['template'] = 'index'; $coords = Geocoder::find('google', $place); $photos = Photos::within(array($coords, $coords), array('limit' => 1)); return compact('photos'); } public function add() { $photo = Photos::create(); Geo Location if (($this->request->data) && $photo->save($this->request->data)) { $this->redirect(array('Photos::view', 'id' => $photo->_id)); } $this->_render['template'] = 'edit'; return compact('photo'); } public function edit() { $photo = Photos::find($this->request->id); if (!$photo) { $this->redirect('Photos::index'); } if (($this->request->data) && $photo->save($this->request->data)) { $this->redirect(array('Photos::view', 'id' => $photo->_id)); } return compact('photo'); }
  34. Routes use lithiumnethttpRouter; use lithiumcoreEnvironment; use lithiumactionResponse; use photoblogmodelsPhotos; Router::connect('/photos/view/{:id:[0-9a-f]{24}}.jpeg', array(), function($request) { return new Response(array( 'headers' => array('Content-type' => 'image/jpeg'), 'body' => Photos::first($request->id)->file->getBytes() )); }); /** * Connect the testing routes. */ if (!Environment::is('production')) { Router::connect('/test/{:args}', array('controller' => 'lithiumtestController')); Router::connect('/test', array('controller' => 'lithiumtestController')); }
  35. Routes use lithiumnethttpRouter; use lithiumcoreEnvironment; use lithiumactionResponse; use photoblogmodelsPhotos; Router::connect('/photos/view/{:id:[0-9a-f]{24}}.jpeg', array(), function($request) { return new Response(array( 'headers' => array('Content-type' => 'image/jpeg'), 'body' => Photos::first($request->id)->file->getBytes() )); }); /** * Connect the testing routes. By-Passes */ if (!Environment::is('production')) { the framework Router::connect('/test/{:args}', array('controller' => 'lithiumtestController')); Router::connect('/test', array('controller' => 'lithiumtestController')); }
  36. Routes use lithiumnethttpRouter; use lithiumcoreEnvironment; use lithiumactionResponse; use It’s PHP you can photoblogmodelsPhotos; Router::connect('/photos/view/{:id:[0-9a-f]{24}}.jpeg', array(), function($request) { return new Response(array( you doarray('Content-type' => 'image/jpeg'), what 'headers' => want 'body' => Photos::first($request->id)->file->getBytes() )); }); /** * Connect the testing routes. */ if (!Environment::is('production')) { Router::connect('/test/{:args}', array('controller' => 'lithiumtestController')); Router::connect('/test', array('controller' => 'lithiumtestController')); }
  37. Structure
  38. Features ✦ Full stack MVC ✦ Object based record- sets ✦ Logger ✦ Command Line ✦ Caching Framework ✦ Sessions/Cookies ✦ Authentication ✦ Full templating suite ✦ Validator ✦ Integrated TDD suite ✦ Http Services
  39. Boring
  40. Now for some fun!
  41. World 2 Kick-ass de-coupled hacker happy framework
  42. Are you a slave to your framework?
  43. Flexible, transparent boostrap process
  44. Database namespace models; class Posts extends lithiumdataModel { }
  45. define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumdataConnections; Connections::add('default', array( 'type' => 'MongoDb', 'database' => 'phpnw', 'host' => 'localhost' )); Libraries::add('models', array('path' => __DIR__ . '/models')); use modelsPosts; $posts = Posts::create(array('title' => 'Hi Guys')); $posts->save();
  46. define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); Simple lithium setup use lithiumdataConnections; Connections::add('default', array( 'type' => 'MongoDb', 'database' => 'phpnw', 'host' => 'localhost' )); Libraries::add('models', array('path' => __DIR__ . '/models')); use modelsPosts; $posts = Posts::create(array('title' => 'Hi Guys')); $posts->save();
  47. define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumdataConnections; Connections::add('default', array( 'type' => 'MongoDb', 'database' => 'phpnw', DB Config 'host' => 'localhost' )); Libraries::add('models', array('path' => __DIR__ . '/models')); use modelsPosts; $posts = Posts::create(array('title' => 'Hi Guys')); $posts->save();
  48. define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumdataConnections; Connections::add('default', array( 'type' => 'MongoDb', 'database' => 'phpnw', 'host' => 'localhost' )); Add models ‘app’ Libraries::add('models', array('path' => __DIR__ . '/models')); use modelsPosts; $posts = Posts::create(array('title' => 'Hi Guys')); $posts->save();
  49. define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumdataConnections; Connections::add('default', array( 'type' => 'MongoDb', 'database' => 'phpnw', 'host' => 'localhost' )); DB Save Libraries::add('models', array('path' => __DIR__ . '/models')); use modelsPosts; $posts = Posts::create(array('title' => 'Hi Guys')); $posts->save();
  50. Routes define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumnethttpRouter; use lithiumactionRequest; $request = new Request(); $router = new Router(); $router->connect('/cool-root', array('controller' => 'Yea')); $router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea')); $router->parse($request); echo Router::match('Yea::index'); echo Router::match(array('Yea::index', 'application_id' => 1));
  51. Routes define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumnethttpRouter; use lithiumactionRequest; $request = new Request(); $router = new Router(); $router->connect('/cool-root', array('controller' => 'Yea')); $router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea')); $router->parse($request); echo Router::match('Yea::index'); echo Router::match(array('Yea::index', 'application_id' => 1));
  52. Routes define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumnethttpRouter; use lithiumactionRequest; $request = new Request(); $router = new Router(); $router->connect('/cool-root', array('controller' => 'Yea')); $router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea')); $router->parse($request); echo Router::match('Yea::index'); echo Router::match(array('Yea::index', 'application_id' => 1));
  53. Routes define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumnethttpRouter; use lithiumactionRequest; $request = new Request(); $router = new Router(); $router->connect('/cool-root', array('controller' => 'Yea')); $router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea')); $router->parse($request); echo Router::match('Yea::index'); echo Router::match(array('Yea::index', 'application_id' => 1));
  54. /cool-root/4
  55. /cool-root/4 $router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea'));
  56. /cool-root/4 $router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea')); object(lithiumactionRequest)[1] public 'url' => string '/cool-root/4' (length=12) public 'params' => array (size=3) 'application_id' => string '4' (length=1) 'controller' => string 'Yea' (length=3) 'action' => string 'index' (length=5)
  57. Adaptable Classes define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); define('LITHIUM_APP_PATH', __DIR__); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/StaticObject.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/Environment.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/Adaptable.php'; use lithiumcoreAdaptable; class Email extends Adaptable{ protected static $_configurations = array(); protected static $_adapters = 'emails'; }
  58. Adaptable Classes define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); define('LITHIUM_APP_PATH', __DIR__); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/StaticObject.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/Environment.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/Adaptable.php'; use lithiumcoreAdaptable; class Email extends Adaptable{ protected static $_configurations = array(); protected static $_adapters = 'emails'; }
  59. namespace adapters; use lithiumcoreLibraries; Libraries::add('lithium'); class EmailTest Libraries::add('adapters', { array('path' => __DIR__ . '/adapters')); public function send() { Libraries::paths( echo 'email test send'; array('emails' => array( } '{:library}{:name}'))); } Email::config(array( 'development' => array( 'adapter' => 'EmailReal' ), namespace adapters; 'test' => array( 'adapter' => 'EmailTest' class EmailReal ), { )); public function send() { echo 'email real send'; use lithiumcoreEnvironment; } } Environment::set('test'); $env = Environment::get(); Email::adapter($env)->send();
  60. namespace adapters; use lithiumcoreLibraries; Libraries::add('lithium'); class EmailTest Libraries::add('adapters', { array('path' => __DIR__ . '/adapters')); public function send() { Libraries::paths( echo 'email test send'; array('emails' => array( } '{:library}{:name}'))); } Email::config(array( 'development' => array( 'adapter' => 'EmailReal' ), namespace adapters; 'test' => array( 'adapter' => 'EmailTest' class EmailReal ), { )); public function send() { echo 'email real send'; use lithiumcoreEnvironment; } } Environment::set('test'); $env = Environment::get(); Email::adapter($env)->send();
  61. namespace adapters; use lithiumcoreLibraries; Libraries::add('lithium'); class EmailTest Libraries::add('adapters', { array('path' => __DIR__ . '/adapters')); public function send() { Libraries::paths( echo 'email test send'; array('emails' => array( } '{:library}{:name}'))); } Email::config(array( 'development' => array( 'adapter' => 'EmailReal' ), namespace adapters; 'test' => array( 'adapter' => 'EmailTest' class EmailReal ), { )); public function send() { echo 'email real send'; use lithiumcoreEnvironment; } } Environment::set('test'); $env = Environment::get(); Email::adapter($env)->send();
  62. namespace adapters; use lithiumcoreLibraries; Libraries::add('lithium'); class EmailTest Libraries::add('adapters', { array('path' => __DIR__ . '/adapters')); public function send() { Libraries::paths( echo 'email test send'; array('emails' => array( } '{:library}{:name}'))); } Email::config(array( 'development' => array( 'adapter' => 'EmailReal' ), namespace adapters; 'test' => array( 'adapter' => 'EmailTest' class EmailReal ), { )); public function send() { echo 'email real send'; use lithiumcoreEnvironment; } } Environment::set('test'); $env = Environment::get(); Email::adapter($env)->send();
  63. Environment::set('development'); class EmailAnother extends Adaptable{ Already protected static $_configurations = array(); protected static $_adapters = 'emails'; Environment public static function send() { static::adapter('default')->send(); Aware } } EmailAnother::config(array( 'default' => array( 'test' => array ( 'adapter' => 'EmailTest' ), 'development' => array( 'adapter' => 'EmailReal' ), ) )); EmailAnother::send();
  64. Environment::set('development'); class EmailAnother extends Adaptable{ Already protected static $_configurations = array(); protected static $_adapters = 'emails'; Environment public static function send() { static::adapter('default')->send(); Aware } } EmailAnother::config(array( 'default' => array( 'test' => array ( 'adapter' => 'EmailTest' ), 'development' => array( 'adapter' => 'EmailReal' ), ) )); EmailAnother::send();
  65. Environment::set('development'); class EmailAnother extends Adaptable{ Already protected static $_configurations = array(); protected static $_adapters = 'emails'; Environment public static function send() { static::adapter('default')->send(); Aware } } EmailAnother::config(array( 'default' => array( 'test' => array ( 'adapter' => 'EmailTest' ), 'development' => array( 'adapter' => 'EmailReal' ), ) )); EmailAnother::send();
  66. Environment::set('development'); class EmailAnother extends Adaptable{ Already protected static $_configurations = array(); protected static $_adapters = 'emails'; Environment public static function send() { static::adapter('default')->send(); Aware } } EmailAnother::config(array( 'default' => array( 'test' => array ( 'adapter' => 'EmailTest' ), 'development' => array( 'adapter' => 'EmailReal' ), ) )); EmailAnother::send();
  67. Filters
  68. define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php'; require LITHIUM_LIBRARY_PATH . '/lithium/util/Collection.php'; require LITHIUM_LIBRARY_PATH . '/lithium/util/collection/Filters.php'; class Action extends lithiumcoreObject { public function doSomethingStupid() { return $this->_filter(__METHOD__, $params, function($self, $params) { $result = ‘Your girlfriend is angry at you’; return $result; }); } } $action = new Action; $action->applyFilter('doSomethingStupid', function($self, $params, $chain) { echo 'About to do something stupid,'; $result = $chain->next($self, $params, $chain); echo 'You IDIOT!!!!!'; return $result; }); $action->doSomthingStupid();
  69. define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php'; require LITHIUM_LIBRARY_PATH . '/lithium/util/Collection.php'; require LITHIUM_LIBRARY_PATH . '/lithium/util/collection/Filters.php'; class Action extends lithiumcoreObject { public function doSomethingStupid() { return $this->_filter(__METHOD__, $params, function($self, $params) { $result = ‘Your girlfriend is angry at you’; return $result; } }); Make } Filterable $action = new Action; $action->applyFilter('doSomethingStupid', function($self, $params, $chain) { echo 'About to do something stupid,'; $result = $chain->next($self, $params, $chain); echo 'You IDIOT!!!!!'; return $result; }); $action->doSomthingStupid();
  70. define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php'; require LITHIUM_LIBRARY_PATH . '/lithium/util/Collection.php'; require LITHIUM_LIBRARY_PATH . '/lithium/util/collection/Filters.php'; class Action extends lithiumcoreObject { public function doSomethingStupid() { return $this->_filter(__METHOD__, $params, function($self, $params) { $result = ‘Your girlfriend is angry at you’; return $result; }); } } $action = new Action; $action->applyFilter('doSomethingStupid', function($self, $params, $chain) { echo 'About to do something stupid,'; $result = $chain->next($self, $params, $chain); echo 'You IDIOT!!!!!'; return $result; }); $action->doSomthingStupid();
  71. use lithiumanalysisLogger; Logger::config(array( 'default' => array( 'adapter' => 'File', 'path' => __DIR__ . '/crap/logs/' ) )); $action = new Action; $action->applyFilter('doSomethingStupid', function($self, $params, $chain) { Logger::debug(date("D M j G:i:s") . " " . 'About to do something stupid,'); $result = $chain->next($self, $params, $chain); Logger::debug(date("D M j G:i:s") . " " . 'You total moron!'); return $result; }); $action->doSomthingStupid();
  72. use lithiumanalysisLogger; Logger::config(array( 'default' => array( 'adapter' => 'File', 'path' => __DIR__ . '/crap/logs/' ) )); $action = new Action; $action->applyFilter('doSomethingStupid', function($self, $params, $chain) { Logger::debug(date("D M j G:i:s") . " " . 'About to do something stupid,'); $result = $chain->next($self, $params, $chain); Logger::debug(date("D M j G:i:s") . " " . 'You total moron!'); return $result; }); $action->doSomthingStupid();
  73. use lithiumanalysisLogger; Logger::config(array( 'default' => array( 'adapter' => 'File', 'path' => __DIR__ . '/crap/logs/' ) )); $action = new Action; $action->applyFilter('doSomethingStupid', function($self, $params, $chain) { Logger::debug(date("D M j G:i:s") . " " . 'About to do something stupid,'); $result = $chain->next($self, $params, $chain); Logger::debug(date("D M j G:i:s") . " " . 'You total moron!'); return $result; }); $action->doSomthingStupid();
  74. use lithiumanalysisLogger; Logger::config(array( 'default' => array( 'adapter' => 'File', 'path' => __DIR__ . '/crap/logs/' ) )); $action = new Action; $action->applyFilter('doSomethingStupid', function($self, $params, $chain) { Logger::debug(date("D M j G:i:s") . " " . 'About to do something stupid,'); $result = $chain->next($self, $params, $chain); Logger::debug(date("D M j G:i:s") . " " . 'You total moron!'); return $result; }); $action->doSomthingStupid();
  75. Find
  76. Cache Find
  77. Log Cache Find
  78. Log Cache Find
  79. Log Cache Find
  80. Log Cache Find
  81. Posts::applyFilter('find', function($self, $params, $chain) { $result = $chain->next($self, $params, $chain); $search = http_build_query($params['options']); Logger::debug( 'Search for: ' . $search . 'returned' . $result->to('json') ); return $result; }); Posts::applyFilter('find', function($self, $params, $chain) { $key = // Make a cache key from params['options'] if ($result = Cache::read('default', $key)) { return $result; } $result = $chain->next($self, $params, $chain); Cache::write('default', $key, $result)); return $result; });
  82. Posts::applyFilter('find', function($self, $params, $chain) { $result = $chain->next($self, $params, $chain); $search = http_build_query($params['options']); Logger::debug( 'Search for: ' . $search . 'returned' . $result->to('json') ); return $result; }); Logging Filter Posts::applyFilter('find', function($self, $params, $chain) { $key = // Make a cache key from params['options'] if ($result = Cache::read('default', $key)) { return $result; } $result = $chain->next($self, $params, $chain); Cache::write('default', $key, $result)); return $result; });
  83. Posts::applyFilter('find', function($self, $params, $chain) { $result = $chain->next($self, $params, $chain); $search = http_build_query($params['options']); Logger::debug( ); Caching Filter 'Search for: ' . $search . 'returned' . $result->to('json') return $result; }); Posts::applyFilter('find', function($self, $params, $chain) { $key = // Make a cache key from params['options'] if ($result = Cache::read('default', $key)) { return $result; } $result = $chain->next($self, $params, $chain); Cache::write('default', $key, $result)); return $result; });
  84. Full Page Caching Cache::config(array( 'default' => array( 'adapter' => 'lithiumstoragecacheadapter' . ($apcEnabled ? 'Apc' : 'File') ) )); Dispatcher::applyFilter('run', function($self, $params, $chain) { $key = md5(LITHIUM_APP_PATH) . '.app.cache.'.md5($params['request']->url); if($cache = Cache::read('default', $key)) { return $cache; } $result = $chain->next($self, $params, $chain); Cache::write('default', $key, $result, '+1 day'); return $result; });
  85. Full Page Caching Set Cache Configuration Cache::config(array( 'default' => array( 'adapter' => 'lithiumstoragecacheadapter' . ($apcEnabled ? 'Apc' : 'File') ) )); Dispatcher::applyFilter('run', function($self, $params, $chain) { $key = md5(LITHIUM_APP_PATH) . '.app.cache.'.md5($params['request']->url); if($cache = Cache::read('default', $key)) { return $cache; } $result = $chain->next($self, $params, $chain); Cache::write('default', $key, $result, '+1 day'); return $result; });
  86. Full Page Caching Cache::config(array( 'default' => array( 'adapter' => 'lithiumstoragecacheadapter' . ($apcEnabled ? 'Apc' : 'File') ) )); Dispatcher::applyFilter('run', function($self, $params, $chain) { $key = md5(LITHIUM_APP_PATH) . '.app.cache.'.md5($params['request']->url); if($cache = Cache::read('default', $key)) { return $cache; } $result = $chain->next($self, $params, $chain); Filter on Dispatcher::run Cache::write('default', $key, $result, '+1 day'); Checks for cache w/key return $result; });
  87. Full Page Caching Cache::config(array( 'default' => array( 'adapter' => 'lithiumstoragecacheadapter' . ($apcEnabled ? 'Apc' : 'File') ) )); w/out cache content carry on Dispatcher::applyFilter('run', function($self, $params, $chain) { $key = md5(LITHIUM_APP_PATH) . '.app.cache.'.md5($params['request']->url); running - saving to cache if($cache = Cache::read('default', $key)) { return $cache; } $result = $chain->next($self, $params, $chain); Cache::write('default', $key, $result, '+1 day'); return $result; });
  88. Content Rendering namespace dispatcher_appcontrollers; class YeaController extends lithiumactionController{ public function index() { $yeah = true; return compact('yeah'); } public function render(array $options = array()) { return 'The response of Yeah is: ' . $this->_render['data']['yeah']; } }
  89. Content Rendering namespace dispatcher_appcontrollers; class YeaController extends lithiumactionController{ public function index() { $yeah = true; return compact('yeah'); } public function render(array $options = array()) { return 'The response of Yeah is: ' . $this->_render['data']['yeah']; } }
  90. Content Rendering namespace dispatcher_appcontrollers; class YeaController extends lithiumactionController{ public function index() { $yeah = true; Overwrite ‘render’ method return compact('yeah'); } public function render(array $options = array()) { return 'The response of Yeah is: ' . $this->_render['data']['yeah']; } }
  91. define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); Libraries::add('dispatcher_app', array('path' => __DIR__ . '/dispatcher_app')); use lithiumnethttpRouter; $router = new Router(); Router::connect('/cool-root', array('controller' => 'Yea', 'library' => 'dispatcher_app')); echo lithiumactionDispatcher::run( new lithiumactionRequest() );
  92. define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); Create Routing Libraries::add('dispatcher_app', array('path' => __DIR__ . '/dispatcher_app')); use lithiumnethttpRouter; $router = new Router(); Router::connect('/cool-root', array('controller' => 'Yea', 'library' => 'dispatcher_app')); echo lithiumactionDispatcher::run( new lithiumactionRequest() );
  93. define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); Libraries::add('dispatcher_app', array('path' => __DIR__ . '/dispatcher_app')); use lithiumnethttpRouter; $router = new Router(); Run Dispatcher Router::connect('/cool-root', array('controller' => 'Yea', 'library' => 'dispatcher_app')); echo lithiumactionDispatcher::run( new lithiumactionRequest() );
  94. Media Class
  95. Media::type('ajax', array('application/xhtml+xml', 'text/html'), array( 'view' => 'lithiumtemplateView', 'paths' => array( 'template' => array( '{:library}/views/{:controller}/{:template}.ajax.php', '{:library}/views/{:controller}/{:template}.html.php' ), 'layout' => '{:library}/views/layouts/default.ajax.php' ), 'conditions' => array('ajax' => true) )); Easily setup template paths
  96. Media::type('jpg', 'image/jpeg', array('cast' => false, 'encode' => function($data) { return $data['photo']->file->getBytes(); })); Media::type('json', 'application/json', array('cast' => false, 'encode' => function($data) { return json_encode($data); })); Media::type('json', 'application/json', array( 'cast' => false, 'encode' => 'json_encode') );
  97. Media::type('jpg', 'image/jpeg', array('cast' => false, 'encode' => function($data) { return $data['photo']->file->getBytes(); })); Handle Images Media::type('json', 'application/json', array('cast' => false, 'encode' => function($data) { return json_encode($data); })); Media::type('json', 'application/json', array( 'cast' => false, 'encode' => 'json_encode') );
  98. Media::type('jpg', 'image/jpeg', array('cast' => false, 'encode' => function($data) { return $data['photo']->file->getBytes(); })); Media::type('json', 'application/json', array('cast' => false, 'encode' => function($data) { return json_encode($data); })); Media::type('json', 'application/json', array( 'cast' => false, 'encode' => 'json_encode') ); Handle JSON
  99. Error Handling
  100. Error Handling use lithiumcoreErrorHandler; use lithiumcoreStaticObject; class IThrowAnException { public function method() { throw new Exception('Yo'); } } class IDontThrow extends StaticObject { public static function method($bar) { static::_filter(__FUNCTION__, $params, function($self, $params) { $object = new IThrowAnException; $object->method(); }); } } ErrorHandler::apply('IDontThrow::method', array(), function($info, $params) { echo 'Exception raised with message - ' . $info['exception']->getMessage(); }); IDontThrowAnException::method();
  101. Error Handling use lithiumcoreErrorHandler; use lithiumcoreStaticObject; class IThrowAnException { public function method() { throw new Exception('Yo'); } } class IDontThrow extends StaticObject { public static function method($bar) { static::_filter(__FUNCTION__, $params, function($self, $params) { $object = new IThrowAnException; $object->method(); }); } } ErrorHandler::apply('IDontThrow::method', array(), function($info, $params) { echo 'Exception raised with message - ' . $info['exception']->getMessage(); }); IDontThrowAnException::method();
  102. Error Handling use lithiumcoreErrorHandler; use lithiumcoreStaticObject; class IThrowAnException { public function method() { throw new Exception('Yo'); } Filterable } class IDontThrow extends StaticObject { public static function method($bar) { static::_filter(__FUNCTION__, $params, function($self, $params) { $object = new IThrowAnException; $object->method(); }); } } ErrorHandler::apply('IDontThrow::method', array(), function($info, $params) { echo 'Exception raised with message - ' . $info['exception']->getMessage(); }); IDontThrowAnException::method();
  103. Error Handling use lithiumcoreErrorHandler; use lithiumcoreStaticObject; class IThrowAnException { public function method() { throw new Exception('Yo'); } } class IDontThrow extends StaticObject { public static function method($bar) { static::_filter(__FUNCTION__, $params, function($self, $params) { $object = new IThrowAnException; $object->method(); }); } } ErrorHandler::apply('IDontThrow::method', array(), function($info, $params) { echo 'Exception raised with message - ' . $info['exception']->getMessage(); }); IDontThrowAnException::method();
  104. ErrorHandler::apply('lithiumactionDispatcher::run', array(), function($info, $params) { $response = new Response(array( 'request' => $params['request'], 'status' => $info['exception']->getCode(), 'trace' => $info['exception']->getTrace(), )); Media::render($response, compact('info', 'params'), array( 'controller' => 'errors', 'template' => 'default', 'layout' => 'default', 'library' => 'golf', 'request' => $params['request'], )); return $response; }); Example
  105. Create ErrorHandler::apply('lithiumactionDispatcher::run', array(), function($info, $params) { Response $response = new Response(array( 'request' => $params['request'], 'status' => $info['exception']->getCode(), 'trace' => $info['exception']->getTrace(), )); Media::render($response, compact('info', 'params'), array( 'controller' => 'errors', 'template' => 'default', 'layout' => 'default', 'library' => 'golf', 'request' => $params['request'], )); return $response; }); Example
  106. ErrorHandler::apply('lithiumactionDispatcher::run', array(), function($info, $params) { $response = new Response(array( 'request' => $params['request'], 'status' => $info['exception']->getCode(), 'trace' => $info['exception']->getTrace(), )); Media::render($response, compact('info', 'params'), array( 'controller' => 'errors', 'template' => 'default', 'layout' => 'default', 'library' => 'golf', Media 'request' => $params['request'], Render )); return $response; }); Example
  107. Zendy Love Libraryies::add( 'prefix' => 'Zend_', 'includePath' => '/htdocs/libraries/Zend/trunk/library', 'bootstrap' => 'Loader/Autoloader.php', 'loader' => array('Zend_Loader_Autoloader', 'autoload'), 'transform' => function($class) { return str_replace('_', '/', $class) . '.php'; } );
  108. define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); define('LITHIUM_APP_PATH', __DIR__ . '/templates'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; use lithiumcoreEnvironment; use lithiumactionDispatcher; use lithiumg11nMessage; use lithiumnethttpMedia; Libraries::add('lithium'); Libraries::add('li3_docs'); Libraries::add('templates', array('path' => __DIR__ . '/templates', 'default' => true)); Libraries::add('adapters', array('path' => __DIR__ . '/adapters')); Libraries::add('li3_docs', array( 'index' => array('lithium', 'adapters') )); Libraries Example
  109. define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries'); define('LITHIUM_APP_PATH', __DIR__ . '/templates'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Sets up use lithiumcoreEnvironment; use use lithiumactionDispatcher; lithiumg11nMessage; Libraries use lithiumnethttpMedia; Libraries::add('lithium'); Libraries::add('li3_docs'); Libraries::add('templates', array('path' => __DIR__ . '/templates', 'default' => true)); Libraries::add('adapters', array('path' => __DIR__ . '/adapters')); Libraries::add('li3_docs', array( 'index' => array('lithium', 'adapters') )); Libraries Example
  110. $locale = 'en'; $locales = array('en' => 'English'); Environment::set('development', compact('locale', 'locales')); Environment::set('development'); } $file = "{$config['path']}/config/routes.php"; file_exists($file) ? call_user_func(function() use ($file) { include $file; }) : null; } return $chain->next($self, $params, $chain); }); Media::applyFilter('_handle', function($self, $params, $chain) { $params['handler'] += array('outputFilters' => array()); $params['handler']['outputFilters'] += Message::aliases(); return $chain->next($self, $params, $chain); }); echo lithiumactionDispatcher::run(new lithiumactionRequest());
  111. Locale & $locale = 'en'; Routes $locales = array('en' => 'English'); Environment::set('development', compact('locale', 'locales')); Environment::set('development'); } $file = "{$config['path']}/config/routes.php"; file_exists($file) ? call_user_func(function() use ($file) { include $file; }) : null; } return $chain->next($self, $params, $chain); }); Media::applyFilter('_handle', function($self, $params, $chain) { $params['handler'] += array('outputFilters' => array()); $params['handler']['outputFilters'] += Message::aliases(); return $chain->next($self, $params, $chain); }); echo lithiumactionDispatcher::run(new lithiumactionRequest());
  112. $locale = 'en'; $locales = array('en' => 'English'); Environment::set('development', compact('locale', 'locales')); Environment::set('development'); } $file = "{$config['path']}/config/routes.php"; file_exists($file) ? call_user_func(function() use ($file) { include $file; }) : null; } return $chain->next($self, $params, $chain); }); Media::applyFilter('_handle', function($self, $params, $chain) { $params['handler'] += array('outputFilters' => array()); $params['handler']['outputFilters'] += Message::aliases(); return $chain->next($self, $params, $chain); Gets }); translations echo lithiumactionDispatcher::run(new lithiumactionRequest());
  113. $locale = 'en'; $locales = array('en' => 'English'); Environment::set('development', compact('locale', 'locales')); Environment::set('development'); } $file = "{$config['path']}/config/routes.php"; file_exists($file) ? call_user_func(function() use ($file) { include $file; }) : null; } return $chain->next($self, $params, $chain); }); Media::applyFilter('_handle', function($self, $params, $chain) { $params['handler'] += array('outputFilters' => array()); $params['handler']['outputFilters'] += Message::aliases(); return $chain->next($self, $params, $chain); }); echo lithiumactionDispatcher::run(new lithiumactionRequest()); Go
  114. Recap
  115. ★ Hackers framework Recap
  116. ★ Hackers framework Recap ★ Light, fast & fun
  117. ★ Hackers framework Recap ★ Light, fast & fun ★ Services have adaptable base
  118. ★ Hackers framework Recap ★ Light, fast & fun ★ Services have adaptable base ★ Filters are awesome/powerful
  119. ★ Hackers framework Recap ★ Light, fast & fun ★ Services have adaptable base ★ Filters are awesome/powerful ★ Everything is a library
  120. ★ Hackers framework Recap ★ Light, fast & fun ★ Services have adaptable base ★ Filters are awesome/powerful ★ Everything is a library ★ As much or as little framework as you need
  121. ★ Hackers framework Recap ★ Light, fast & fun ★ Services have adaptable base ★ Filters are awesome/powerful ★ Everything is a library ★ As much or as little framework as you need ★ Great balance weight/power/simplicity
  122. Thanks github.com/mackstar/phpnw-li3 @mackstar | #mackstar

Editor's Notes

  1. \n
  2. Union of Rad - worked with Nate\nTalk to me about japan\n
  3. Thank you - engine yard sponsors Lithium - php 5.4\n
  4. Lets talk about frameworks - there are too many\n
  5. People like to fight about their framework choice\n
  6. My favorites\nMy Least favorites\n
  7. Bloated\nCarry too much bulk - Symfony/Doctrine - VHeavy\nJava programmers\n
  8. \n
  9. \n
  10. Any body else find this odd?\n
  11. \n
  12. What drives us -\nIt is important to love the code you write - rails has had a big influence\n
  13. Our feelings count\nI don&amp;#x2019;t think we should need a high powered ide\n
  14. Lithium is really light 1mb tests 1mb - no bulk\n
  15. Lithium is really light 1mb tests 1mb - no bulk\n
  16. It is fast to develop in, it will speed up your development time. \nCompare Symfony, ZF, Drupal\n
  17. World one - every one familiar with this phrase?\n
  18. The repo - skeleton\n
  19. Great way to familiarize yourself - next few slides are taken from this project\n
  20. Example model - Save override to save tags\n
  21. Example model - Save override to save tags\n
  22. Example model - Save override to save tags\n
  23. View helpers\nCan override these\n
  24. View helpers\nCan override these\n
  25. View helpers\nCan override these\n
  26. &lt;?= escaping\nLink routes\nimage url helper\n
  27. &lt;?= escaping\nLink routes\nimage url helper\n
  28. &lt;?= escaping\nLink routes\nimage url helper\n
  29. Easy to read controllers - simple methods\n
  30. Easy to read controllers - simple methods\n
  31. Easy to read controllers - simple methods\n
  32. Geo-code is here - still really simple\n
  33. 1.Completely avoids the framework stack\nCan also use with JSON end points\n2. Its PHP, you can do what you want \n
  34. 1.Completely avoids the framework stack\nCan also use with JSON end points\n2. Its PHP, you can do what you want \n
  35. 1.Completely avoids the framework stack\nCan also use with JSON end points\n2. Its PHP, you can do what you want \n
  36. testing important\neverything is a library\n
  37. command line\n
  38. But this alone is not massively different to what is out there - like fuel\n
  39. It gets more interesting from here\n
  40. world 2\n
  41. Sky - no framework - create it from scratch - framework constraints\n
  42. maybe we feel a bit like this\n
  43. Not just 1 programming paradigm - php is very flexible\n
  44. \n
  45. Make a simple model\n
  46. Decoupled example\njust add lithium library - low overhead - all the code you need\nEasily added to DB - These are working examples\n
  47. Decoupled example\njust add lithium library - low overhead - all the code you need\nEasily added to DB - These are working examples\n
  48. Decoupled example\njust add lithium library - low overhead - all the code you need\nEasily added to DB - These are working examples\n
  49. Decoupled example\njust add lithium library - low overhead - all the code you need\nEasily added to DB - These are working examples\n
  50. Decoupled example\njust add lithium library - low overhead - all the code you need\nEasily added to DB - These are working examples\n
  51. Decoupled example\njust add lithium library - low overhead - all the code you need\nEasily added to DB - These are working examples\n
  52. Decoupled example\njust add lithium library - low overhead - all the code you need\nEasily added to DB - These are working examples\n
  53. Instantiate request and router\nSet routes - regex - now works (request contains correct controller)\nFull path matching\n\n\n
  54. Instantiate request and router\nSet routes - regex - now works (request contains correct controller)\nFull path matching\n\n\n
  55. Instantiate request and router\nSet routes - regex - now works (request contains correct controller)\nFull path matching\n\n\n
  56. Instantiate request and router\nSet routes - regex - now works (request contains correct controller)\nFull path matching\n\n\n
  57. Instantiate request and router\nSet routes - regex - now works (request contains correct controller)\nFull path matching\n\n\n
  58. We have an end point, \nfollowing route, \nparses the request object to contain\n
  59. We have an end point, \nfollowing route, \nparses the request object to contain\n
  60. We have an end point, \nfollowing route, \nparses the request object to contain\n
  61. Make simplest adaptable class\n
  62. We tell the Lithium where the app and adapters live -normally not needed\n-note emails same as where highlighted in the class \nSet the environment and use the adapter\n
  63. We tell the Lithium where the app and adapters live -normally not needed\n-note emails same as where highlighted in the class \nSet the environment and use the adapter\n
  64. We tell the Lithium where the app and adapters live -normally not needed\n-note emails same as where highlighted in the class \nSet the environment and use the adapter\n
  65. We tell the Lithium where the app and adapters live -normally not needed\n-note emails same as where highlighted in the class \nSet the environment and use the adapter\n
  66. We tell the Lithium where the app and adapters live -normally not needed\n-note emails same as where highlighted in the class \nSet the environment and use the adapter\n
  67. This time we write it a little more differently\nset up environment aware config\nSending is easy. Base of lithium services. Strategy implementation, very testable. DI in proportion\n
  68. This time we write it a little more differently\nset up environment aware config\nSending is easy. Base of lithium services. Strategy implementation, very testable. DI in proportion\n
  69. This time we write it a little more differently\nset up environment aware config\nSending is easy. Base of lithium services. Strategy implementation, very testable. DI in proportion\n
  70. This time we write it a little more differently\nset up environment aware config\nSending is easy. Base of lithium services. Strategy implementation, very testable. DI in proportion\n
  71. This time we write it a little more differently\nset up environment aware config\nSending is easy. Base of lithium services. Strategy implementation, very testable. DI in proportion\n
  72. The real fun part, this is a real high point of lithiums architecture.\n
  73. Write a method that doesn&amp;#x2019;t really do anything, but this is a filterable class\nEcho-ing is non-sense\n
  74. Write a method that doesn&amp;#x2019;t really do anything, but this is a filterable class\nEcho-ing is non-sense\n
  75. Write a method that doesn&amp;#x2019;t really do anything, but this is a filterable class\nEcho-ing is non-sense\n
  76. We deal with dependencies that have nothing to do with role\nLogger\n
  77. We deal with dependencies that have nothing to do with role\nLogger\n
  78. We deal with dependencies that have nothing to do with role\nLogger\n
  79. We deal with dependencies that have nothing to do with role\nLogger\n
  80. We deal with dependencies that have nothing to do with role\nLogger\n
  81. Find Example\n
  82. Find Example\n
  83. Find Example\n
  84. Find Example\n
  85. Find Example\n
  86. Find Example\n
  87. It would look like this\n
  88. It would look like this\n
  89. It would look like this\n
  90. Cache is an adaptable class\nBAD - filter classes that relates to itself\n
  91. Cache is an adaptable class\nBAD - filter classes that relates to itself\n
  92. Cache is an adaptable class\nBAD - filter classes that relates to itself\n
  93. Cache is an adaptable class\nBAD - filter classes that relates to itself\n
  94. Cache is an adaptable class\nBAD - filter classes that relates to itself\n
  95. Override content render - I am doing this\n
  96. Override content render - I am doing this\n
  97. Override content render - I am doing this\n
  98. \n
  99. \n
  100. \n
  101. How controller render works\n
  102. Ajax paths - types \n
  103. Handle media returned from the controller - images, json, encoding handlers\nHappens in controllers render method\n
  104. Handle media returned from the controller - images, json, encoding handlers\nHappens in controllers render method\n
  105. Handle media returned from the controller - images, json, encoding handlers\nHappens in controllers render method\n
  106. Granular control of exception handling\n
  107. Granular control of exception handling\n
  108. Granular control of exception handling\n
  109. Granular control of exception handling\n
  110. Granular control of exception handling\n
  111. Granular control of exception handling\n
  112. Real example - setting up error handling\n
  113. Real example - setting up error handling\n
  114. Real example - setting up error handling\n
  115. \n
  116. \n
  117. Set up stuff required by library - do demo\n
  118. Set up stuff required by library - do demo\n
  119. Set up stuff required by library - do demo\n
  120. Set up stuff required by library - do demo\n
  121. Set up stuff required by library - do demo\n
  122. Recap -really fast development\n
  123. Recap -really fast development\n
  124. Recap -really fast development\n
  125. Recap -really fast development\n
  126. Recap -really fast development\n
  127. Recap -really fast development\n
  128. Recap -really fast development\n
  129. \n
Advertisement