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.
Upcoming SlideShare
Laravel 5
Laravel 5
Loading in …3
×
1 of 125

LazyRecord: The Fast ORM for PHP

17

Share

Download to read offline

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

LazyRecord: The Fast ORM for PHP

  1. 1. LazyRecord The Fast PHP ORM 林佑安 Yo-An Lin (c9s) 12年4月22⽇日星期⽇日
  2. 2. .metadata • 林佑安 (c9s) • 190 repo/projects on GitHub • Perl programming since 2008 • PHP programming since last year • c, c++, javascript, obj-c, ruby, python, haskell, java, c#,VB .NET ... 12年4月22⽇日星期⽇日
  3. 3. Why another PHP ORM ? 12年4月22⽇日星期⽇日
  4. 4. PHP ORMs • Doctrine 12年4月22⽇日星期⽇日
  5. 5. PHP ORMs • Doctrine • Propel 12年4月22⽇日星期⽇日
  6. 6. PHP ORMs • Doctrine • Propel • Idiorm / Paris 12年4月22⽇日星期⽇日
  7. 7. Propel / Doctrine • Propel uses XML Schema file. 12年4月22⽇日星期⽇日
  8. 8. Propel / Doctrine • Propel uses XML Schema file. • Doctrine uses XML/YAML/Annotations. 12年4月22⽇日星期⽇日
  9. 9. Propel / Doctrine • Propel uses XML Schema file. • Doctrine uses XML/YAML/Annotations. • Slow & Fat. 12年4月22⽇日星期⽇日
  10. 10. Propel / Doctrine • Propel uses XML Schema file. • Doctrine uses XML/YAML/Annotations. • Slow & Fat. • Doctrine is too complicated. 12年4月22⽇日星期⽇日
  11. 11. Common characteristic 12年4月22⽇日星期⽇日
  12. 12. • XML for configuration file. • XML for schema file. • XML for everything. • Concepts are from Java, too complicated. 12年4月22⽇日星期⽇日
  13. 13. Propel XML runtime.conf 12年4月22⽇日星期⽇日
  14. 14. <?xml version="1.0"?> <config> <log> <ident>propel-bookstore</ident> <type>console</type> <level>7</level> </log> <propel> <datasources default="bookstore"> <datasource id="bookstore"> <adapter>sqlite</adapter> <connection> <classname>DebugPDO</classname> <dsn>mysql:host=localhost;dbname=bookstore</dsn> <user>testuser</user> <password>password</password> <options> <option id="ATTR_PERSISTENT">false</option> </options> <attributes> <option id="ATTR_EMULATE_PREPARES">true</option> </attributes> <settings> <setting id="charset">utf8</setting> <setting id="queries"> <query>set search_path myschema, public</query><!-- automatically set postgresql's search_path --> <query>INSERT INTO BAR ('hey', 'there')</query><!-- execute some other query --> </setting> </settings> </connection> <slaves> <connection> <dsn>mysql:host=slave-server1; dbname=bookstore</dsn> </connection> <connection> <dsn>mysql:host=slave-server2; dbname=bookstore</dsn> </connection> </slaves> </datasource> </datasources> <debugpdo> <logging> <details> <method> <enabled>true</enabled> </method> <time> <enabled>true</enabled> <precision>3</precision> </time> <mem> <enabled>true</enabled> <precision>1</precision> 12年4月22⽇日星期⽇日 </mem>
  15. 15. <datasource id="bookstore"> <adapter>sqlite</adapter> <connection> <classname>DebugPDO</classname> <dsn>mysql:host=localhost;dbname=bookstore</dsn> <user>testuser</user> <password>password</password> <options> <option id="ATTR_PERSISTENT">false</option> </options> <attributes> <option id="ATTR_EMULATE_PREPARES">true</option> </attributes> <settings> <setting id="charset">utf8</setting> <setting id="queries"> <query>set search_path myschema, public</query><!-- automatically set postgresql's search_path --> <query>INSERT INTO BAR ('hey', 'there')</query><!-- execute some other query --> </setting> </settings> </connection> <slaves> <connection> <dsn>mysql:host=slave-server1; dbname=bookstore</dsn> </connection> <connection> <dsn>mysql:host=slave-server2; dbname=bookstore</dsn> </connection> </slaves> </datasource> </datasources> <debugpdo> <logging> <details> <method> <enabled>true</enabled> </method> <time> <enabled>true</enabled> <precision>3</precision> </time> <mem> <enabled>true</enabled> <precision>1</precision> </mem> </details> </logging> </debugpdo> </propel> </config> 12年4月22⽇日星期⽇日
  16. 16. 12年4月22⽇日星期⽇日
  17. 17. It should be simpler 12年4月22⽇日星期⽇日
  18. 18. Inspirations • JiftyDBI / Perl • KiokuDB / Perl • ActiveRecord / Ruby • Propel / PHP 12年4月22⽇日星期⽇日
  19. 19. ActiveRecord Pattern 12年4月22⽇日星期⽇日
  20. 20. client = Client.find(10) client = Client.first Client.where("orders_count = ?", params[:orders]) Client.where("created_at >= :start_date AND created_at <= :end_date", {:start_date => params[:start_date], :end_date => params[:end_date]}) Client.order("created_at DESC") Client.limit(5).offset(30) 12年4月22⽇日星期⽇日
  21. 21. Object::Declare use Object::Declare ['MyApp::Column', 'MyApp::Param']; my %objects = declare { param foo => !is global, is immutable, valid_values are qw( more values ); column bar => field1 is 'value', field2 is 'some_other_value', sub_params are param( is happy ), param ( is sad ); }; print $objects{foo}; # a MyApp::Param object print $objects{bar}; # a MyApp::Column object # Assuming that MyApp::Column::new simply blesses into a hash... print $objects{bar}{sub_params}[0]; # a MyApp::Param object print $objects{bar}{sub_params}[1]; # a MyApp::Param object Audrey Tang 唐鳳 2006 12年4月22⽇日星期⽇日
  22. 22. Jifty::DBI package Simple; use Jifty::DBI::Schema; use Jifty::DBI::Record schema { column foo => type is 'text'; column bar => type is 'text'; }; 12年4月22⽇日星期⽇日
  23. 23. Jifty::DBI package TestApp::Model::Phone; use Jifty::DBI::Schema; use Jifty::DBI::Record schema { column user => references TestApp::Model::User by 'id', is mandatory; column type => ...; column value => validator is sub { ... }, default is sub { } ; }; 12年4月22⽇日星期⽇日
  24. 24. Jifty Model Schema 12年4月22⽇日星期⽇日
  25. 25. Jifty Model Schema ⇛ Action 12年4月22⽇日星期⽇日
  26. 26. Jifty Model Schema ⇛ Action ⇛ CRUD 12年4月22⽇日星期⽇日
  27. 27. Jifty App::Model::Phone ☚ write once 12年4月22⽇日星期⽇日
  28. 28. Jifty App::Model::Phone App::Model::PhoneCollection 12年4月22⽇日星期⽇日
  29. 29. Jifty App::Model::Phone App::Model::PhoneCollection App::Action::CreatePhone 12年4月22⽇日星期⽇日
  30. 30. Jifty App::Model::Phone App::Model::PhoneCollection App::Action::CreatePhone App::Action::UpdatePhone 12年4月22⽇日星期⽇日
  31. 31. Jifty App::Model::Phone App::Model::PhoneCollection App::Action::CreatePhone App::Action::UpdatePhone App::Action::DeletePhone 12年4月22⽇日星期⽇日
  32. 32. Jifty App::Model::Phone App::Model::PhoneCollection App::Action::CreatePhone App::Action::UpdatePhone App::Action::DeletePhone $phone->as_create_action()->render(); 12年4月22⽇日星期⽇日
  33. 33. 12年4月22⽇日星期⽇日
  34. 34. What we need • Can use PHP closures for validation, default value, completion ..etc. • Everything should be lazy. • Simple API • No overdesign. • Mixin schema • CRUD generation. • Front-end CRUD integration. 12年4月22⽇日星期⽇日
  35. 35. PHP 5.3 Characteristic • APC is fast. • json_encode / json_decode (file) are slower than require a simple array from php source code. • function is faster than class method. • class method is slower than properties. • magic method is slower than normal class method. • array is faster than object. 12年4月22⽇日星期⽇日
  36. 36. $array[] vs array_push https://github.com/c9s/SimpleBench 12年4月22⽇日星期⽇日
  37. 37. Function calls https://github.com/c9s/SimpleBench 12年4月22⽇日星期⽇日
  38. 38. PHP ORM v1 12年4月22⽇日星期⽇日
  39. 39. EteDB • Initialize model schema in runtime. • Schema is defined in Model (in __constructor). • MySQL only. • dynamic class generator (using eval) • too slow. 12年4月22⽇日星期⽇日
  40. 40. PHP ORM v2 12年4月22⽇日星期⽇日
  41. 41. LazyRecord • Lazy schema loader • SQL Generator • Lazy attribute for MySQL, PgSQL, SQLite • Lazy class loader • Lazy connection • SplClassLoader • Static class • ... etc generator 12年4月22⽇日星期⽇日
  42. 42. Based on SQLBuilder 12年4月22⽇日星期⽇日
  43. 43. SQLBuilder • A Simple SQL Generator. • Prevent Injection. • Migration generator. (index, alter table...etc) • Support SQLite, Pgsql, Mysql syntax. • Pure SQL or with named-parameters. 12年4月22⽇日星期⽇日
  44. 44. <?php $sqlbuilder = new SQLBuilderQueryBuilder( $driver ); $sql = $sqlbuilder->table('authors')->insert([ 'name' => 'Mary', 'address' => 'Paris', ])->build(); 12年4月22⽇日星期⽇日
  45. 45. -- General syntax INSERT INTO authors ( name , address ) VALUES ( 'Name' , 'Address' ); -- PgSQL INSERT INTO "Authors" ( "Name" , "Address" ) VALUES ( 'Name' , 'Address' ); -- MySQL INSERT INTO `Authors` ( `Name` , `Address` ) VALUES ( 'Name' , 'Address' ); -- PDO INSERT INTO authors ( name , address ) VALUES ( ? , ? ); INSERT INTO authors ( name , address ) VALUES ( :name , :address ); 12年4月22⽇日星期⽇日
  46. 46. <?php $sql = $builder->table('Member')->select('*') ->where() ->equal( 'a' , 'bar' ) // a = 'bar' ->notEqual( 'a' , 'bar' ) // a != 'bar' ->is( 'a' , 'null' ) // a is null ->isNot( 'a' , 'null' ) // a is not equal ->greater( 'a' , '2011-01-01' ); ->greater( 'a' , ['date(2011-01-01)'] ); // do not escape ->or()->less( 'a' , 123 ) ->and()->like( 'content' , '%content%' ); ->group() // and ( a = 123 or b != 123 ) ->is( 'a' , 123 ) ->isNot( 'b', 123 ) ->ungroup() ->build(); 12年4月22⽇日星期⽇日
  47. 47. Overview 12年4月22⽇日星期⽇日
  48. 48. Model Overview 12年4月22⽇日星期⽇日
  49. 49. <?php $author = new Author; $ret = $author->create([ 'name' => "Deflator Test $i", 'country' => 'Tokyo', 'confirmed' => true, 'date' => new DateTime('2011-01-01 00:00:00'), ]); if( $ret->success ) { echo "Created!"; } 12年4月22⽇日星期⽇日
  50. 50. <?php $ret = $author->update(array( 'name' => 'Bar' )); if( $ret->success ) { echo "Updated!"; } else { echo $ret; // __toString support } 12年4月22⽇日星期⽇日
  51. 51. <?php $record = Author::load(array( 'name' => 'Foo' )); // To find a record with primary key: $record = Author::load( 1 ); // To update a record (static): $ret = Author::update( array( 'name' => 'Author' ))->where() ->equal('id',3) ->execute(); 12年4月22⽇日星期⽇日
  52. 52. $author->toJson(); $author->toArray(); $author->toXml(); $author->toYaml(); 12年4月22⽇日星期⽇日
  53. 53. Collection Overview 12年4月22⽇日星期⽇日
  54. 54. Iterator <?php $authors = new AuthorCollection; foreach( $authors as $author ) { echo $author->name , "n" } 12年4月22⽇日星期⽇日
  55. 55. SQLBuilder Mix-In <?php $names = new NameCollection; $names->where() ->equal('name','Foo') ->groupBy('name','address'); ?> 12年4月22⽇日星期⽇日
  56. 56. Filter <?php $newCollection = $names->filter(function($item) { // do something else })->filter(function($item) { return $item->confirmed; }); ?> 12年4月22⽇日星期⽇日
  57. 57. Each <?php $names->each(function($item) { $item->update([ .... ]); }); ?> 12年4月22⽇日星期⽇日
  58. 58. Collection Pager <?php /* page 1, 10 per page */ $authors = new AuthorCollection; $pager = $authors->pager(1,10); $pager = $authors->pager(); $items = $pager->items(); $pager->next(); // next page ?> Integrate with OFFSET & LIMIT 12年4月22⽇日星期⽇日
  59. 59. Relationship <?php // has many $address = $author->addresses->create([ 'address' => 'farfaraway' ]); // create related address $author->addresses[] = [ 'address' => 'Harvard' ]; $addresses = $author->addresses->items(); foreach( $author->addresses as $address ) { echo $address->address , "n"; } 12年4月22⽇日星期⽇日
  60. 60. Schema 12年4月22⽇日星期⽇日
  61. 61. Powered by CascadingAttribute.php 12年4月22⽇日星期⽇日
  62. 62. <?php use LazyRecordSchemaSchemaDeclare; class AddressSchema extends SchemaDeclare { function schema() { } } 12年4月22⽇日星期⽇日
  63. 63. <?php use LazyRecordSchemaSchemaDeclare; class AddressSchema extends SchemaDeclare { function schema() { $this->column('address') ->varchar(128); } } 12年4月22⽇日星期⽇日
  64. 64. <?php use LazyRecordSchemaSchemaDeclare; class AddressSchema extends SchemaDeclare { function schema() { $this->column('address') ->integer(); } } 12年4月22⽇日星期⽇日
  65. 65. <?php use LazyRecordSchemaSchemaDeclare; class AddressSchema extends SchemaDeclare { function schema() { $this->column('address') ->timestamp(); } } 12年4月22⽇日星期⽇日
  66. 66. Default value & builder 12年4月22⽇日星期⽇日
  67. 67. $this->column('name') ->varchar(30) ->default('Default'); 12年4月22⽇日星期⽇日
  68. 68. $this->column('name') ->varchar(30) ->default( array('current_timestamp') ); 12年4月22⽇日星期⽇日
  69. 69. $this->column('name') ->varchar(30) ->defaultBuilder(function() { return date('c'); }) 12年4月22⽇日星期⽇日
  70. 70. $this->column('name') ->varchar(30) ->default('Default') ->default( array('current_timestamp') ) ->defaultBuilder(function() { return date('c'); }) 12年4月22⽇日星期⽇日
  71. 71. Validator 12年4月22⽇日星期⽇日
  72. 72. $this->column('name') ->varchar(30) ->validator('ValidatorClass') 12年4月22⽇日星期⽇日
  73. 73. $this->column('name') ->varchar(30) ->validator( array('ValidatorClass','method') ) 12年4月22⽇日星期⽇日
  74. 74. $this->column('name') ->varchar(30) ->validator('function_name') 12年4月22⽇日星期⽇日
  75. 75. $this->column('name') ->varchar(30) ->validator(function($val) { .... }) 12年4月22⽇日星期⽇日
  76. 76. Filter 12年4月22⽇日星期⽇日
  77. 77. $this->column('name') ->varchar(30) ->filter( function($val) { return preg_replace('#word#','zz',$val); }); 12年4月22⽇日星期⽇日
  78. 78. Deflator / Inflator 12年4月22⽇日星期⽇日
  79. 79. use LazyRecordSchemaSchemaDeclare; class NameSchema extends SchemaDeclare { function schema() { $this->column('created_on') ->date() ->isa('DateTime') ->deflator( function($val) { if( is_a( $val, 'DateTime' ) ) return $val->format('Y-m-d'); elseif( is_integer($val) ) { return strftime( '%Y-%m-%d' , $val ); } return $val; }) ->inflator( function($val) { return new DateTime( $val ); }); } } 12年4月22⽇日星期⽇日
  80. 80. $name->created_on; // DateTime object $name->created_on->format('Y-m-d'); $name->create([ 'created_on' => new DateTime; ]); 12年4月22⽇日星期⽇日
  81. 81. Mixin schema 12年4月22⽇日星期⽇日
  82. 82. $this->mixin('MetadataMixinSchema'); $this->mixin('I18nMixinSchema'); $this->mixin('CommentMinxSchema'); 12年4月22⽇日星期⽇日
  83. 83. Multiple data source 12年4月22⽇日星期⽇日
  84. 84. database.yml data_sources: master: dsn: 'mysql:host=localhost;dbname=lazy_test' user: root pass: 123123 12年4月22⽇日星期⽇日
  85. 85. database.yml data_sources: master: dsn: 'mysql:host=localhost;dbname=lazy_test' user: root pass: 123123 slave: dsn: 'mysql:dbname=lazy_test' query_options: { quote_column: true, quote_table: true } 12年4月22⽇日星期⽇日
  86. 86. database.yml data_sources: master: dsn: 'mysql:host=localhost;dbname=lazy_test' user: root pass: 123123 slave: dsn: 'mysql:dbname=lazy_test' query_options: { quote_column: true, quote_table: true } schema // data source for writing $this->writeTo('master'); // data source for reading $this->readFrom('slave'); 12年4月22⽇日星期⽇日
  87. 87. 拼裝時刻 12年4月22⽇日星期⽇日
  88. 88. LazyBone http://github.com/c9s/LazyBone.git 12年4月22⽇日星期⽇日
  89. 89. LazyBone = 12年4月22⽇日星期⽇日
  90. 90. LazyRecord + Roller Router + RESTful Plugin + Backbone.js 12年4月22⽇日星期⽇日
  91. 91. Install LazyRecord 12年4月22⽇日星期⽇日
  92. 92. sudo bash -c "$(curl -s -L https://raw.github.com/c9s/LazyRecord/master/install.sh)" 12年4月22⽇日星期⽇日
  93. 93. Define config file 12年4月22⽇日星期⽇日
  94. 94. config/database.yml --- bootstrap: - bootstrap.php schema: paths: - model data_sources: default: dsn: 'sqlite:/tmp/todos.db' 12年4月22⽇日星期⽇日
  95. 95. $ lazy build-conf config/database.yml Convert YAML to PHP. <?php $config = require '.lazy.php'; APC caches this automatically. 12年4月22⽇日星期⽇日
  96. 96. Define model 12年4月22⽇日星期⽇日
  97. 97. <?php class TodoSchema extends LazyRecordSchemaSchemaDeclare { function schema() { $this->column('id') ->primary() ->autoIncrement() ->integer(); $this->column('title') ->text(); $this->column('done') ->boolean() ->default(false); $this->column('created_on') ->defaultBuilder( function() { return date('c'); } ) ->timestamp(); } function bootstrap($model) { $model->create(array( 'title' => 'Foo', )); } } 12年4月22⽇日星期⽇日
  98. 98. Create static schema files 12年4月22⽇日星期⽇日
  99. 99. 12年4月22⽇日星期⽇日
  100. 100. $ lazy build-schema model/TodoSchema.php 12年4月22⽇日星期⽇日
  101. 101. $ lazy build-schema model/TodoSchema.php ... Classmap: ! TodoSchemaProxy => model/TodoSchemaProxy.php ! TodoBase => model/TodoBase.php ! Todo => model/Todo.php ! TodoCollectionBase => model/TodoCollectionBase.php ! TodoCollection => model/TodoCollection.php Done 12年4月22⽇日星期⽇日
  102. 102. Initialize database 12年4月22⽇日星期⽇日
  103. 103. 12年4月22⽇日星期⽇日
  104. 104. $ lazy build-sql model/TodoSchema.php 12年4月22⽇日星期⽇日
  105. 105. $ lazy build-sql model/TodoSchema.php Building SQL for TodoSchema --- SQL for TodoSchema CREATE TABLE todos ( id integer primary key autoincrement, title text, done boolean default 0, created_on timestamp ); 12年4月22⽇日星期⽇日
  106. 106. Integrate with your application 12年4月22⽇日星期⽇日
  107. 107. <?php use LazyRecordConfigLoader; $config = new ConfigLoader; $config->load( __DIR__ . '/.lazy.php'); $config->init(); 12年4月22⽇日星期⽇日
  108. 108. Define routes 12年4月22⽇日星期⽇日
  109. 109. Roller Router High performance router for PHP 12年4月22⽇日星期⽇日
  110. 110. Roller Router • APC cache • FileSystem cache • Use Array to store routes • through PHP extension, can dispatch 1607% faster than pure php version • Annotation reader support • RESTful plugin 12年4月22⽇日星期⽇日
  111. 111. $router = new RollerRouter; $router->get( '/blog/:id/:title' , function($id,$title) { return 'Blog'; }); $router->post( '/blog/:year/:month/:id/:title', array('Controller','method') ); $router->any( '/path/to/:year' , array('Callback','method') , array( 'year' => 'd+', )); 12年4月22⽇日星期⽇日
  112. 112. RouteSet <?php $subroutes = new RollerRouteSet; $subroutes->add( '/subitem' , $cb ); $routes = new RollerRouteSet; $routes->mount( '/item' , $subroutes ); /item/subitem => $cb 12年4月22⽇日星期⽇日
  113. 113. Dispatch $r = $router->dispatch( isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/' ); if( $r ) { echo $r(); } else { die('Page not found'); } 12年4月22⽇日星期⽇日
  114. 114. RESTful Plugin 12年4月22⽇日星期⽇日
  115. 115. <?php $router = new RollerRouter( null, array( 'cache_id' => 'router_demo' )); $restful = new RollerPluginRESTful(array( 'prefix' => '/=/restful' )); $restful->setGenericHandler( 'MyGenericHandler' ); $router->addPlugin($restful); 12年4月22⽇日星期⽇日
  116. 116. Auto-generated routes GET /=/restful/posts GET /=/restful/posts.json GET /=/restful/posts.yml GET /=/restful/posts/23 GET /=/restful/posts/23.json POST /=/restful/posts/23 DELETE /=/restful/posts/23 12年4月22⽇日星期⽇日
  117. 117. Define Your Resource Handler 12年4月22⽇日星期⽇日
  118. 118. <?php use RollerPluginRESTfulResourceHandler; use RollerPluginRESTfulGenericHandler; class MyGenericHandler extends GenericHandler { public function create($resource) { } public function load($resource,$id) { } public function update($resource,$id) { } public function delete($resource,$id) { } public function find($resource) { } } 12年4月22⽇日星期⽇日
  119. 119. <?php namespace LazyBoneResource; use RollerPluginRESTfulResourceHandler; use Todo; use TodoCollection; class TodoResource extends ResourceHandler { public function create() { $vars = json_decode($this->readInput(),true); $todo = new Todo; $ret = $todo->create($vars); if( $ret->success ) { return $todo->toArray(); } $this->codeBadRequest(); return array( 'error' => $ret->message ); } public function update($id) { $todo = new Todo( $id ); if( ! $todo->id ) { return $this->codeNotFound(); } $vars = json_decode($this->readInput(),true); unset( $vars['created_on'] ); // lazy record bug if($vars) { $todo->update( $vars ); return $todo->toArray(); } return $this->codeBadRequest(); } .... 12年4月22⽇日星期⽇日
  120. 120. Backbone.js 12年4月22⽇日星期⽇日
  121. 121. Todo = Backbone.Model.extend({ // Default attributes for the todo item. defaults: function() { return { title: "empty todo...", done: false // order: Todos.nextOrder(), }; }, // Toggle the `done` state of this todo item. toggle: function() { this.save({done: !this.get("done")}); }, clear: function() { this.destroy(); } }); 12年4月22⽇日星期⽇日
  122. 122. TodoList = Backbone.Collection.extend({ // Reference to this collection's model. model: Todo, url:"/=/todos", done: function() { return this.filter(function(todo){ return todo.get('done'); }); }, remaining: function() { return this.without.apply(this, this.done()); }, }); 12年4月22⽇日星期⽇日
  123. 123. 12年4月22⽇日星期⽇日
  124. 124. Hacking forks welcome! http://github.com/c9s/LazyRecord.git 12年4月22⽇日星期⽇日
  125. 125. Q &A ? 12年4月22⽇日星期⽇日

×