Overview

Classes

  • TinyDB
  • TinyDBCommand
  • TinyDBFactory
  • TinyDBModel

Exceptions

  • TinyDBException
  • Overview
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * @copyright Dong <ddliuhb@gmail.com>
   4:  * @licence MIT
   5:  */
   6: 
   7: class TinyDBException extends Exception{}
   8: 
   9: /**
  10:  * Connection management/PDO Wrapper
  11:  */
  12: class TinyDB
  13: {
  14:     protected $current;
  15:     protected $connections = array();
  16:     protected $configs = array();
  17:     public function __construct($dsn = null, $username = null, $password = null, $options = array()){
  18:         $this->current = 'default';
  19:         $this->addConnection($this->current, $dsn, $username, $password, $options);
  20:     }
  21:     
  22:     public function getConfig($name = null){
  23:         if(null === $name){
  24:             $name = $this->current;
  25:         }
  26:         return $this->configs[$name];
  27:     }
  28:     
  29:     public function getPDO($name = null){
  30:         if(null === $name){
  31:             $name = $this->current;
  32:         }
  33:         if(!isset($this->connections[$name])){
  34:             $config = $this->configs[$name];
  35:             $this->connections[$name] = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
  36:         }
  37:         
  38:         return $this->connections[$this->current];
  39:     }
  40:     
  41:     /**
  42:      * Add a new connection configuration
  43:      * 
  44:      * @param string $name
  45:      * @param string $dsn
  46:      * @param string $username
  47:      * @param string $password
  48:      * @param array $options
  49:      */
  50:     public function addConnection($name, $dsn, $username = null, $password = null, $options = array()){
  51:         //default driver options
  52:         static $defaultOptions = array(
  53:             PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  54:             //PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
  55:         );
  56:         if(isset($options['prefix'])){
  57:             $prefix = $options['prefix'];
  58:             unset($options['prefix']);
  59:         }
  60:         else{
  61:             $prefix = '';
  62:         }
  63:         $this->configs[$name] = array(
  64:             'dsn' => $dsn,
  65:             'username' => $username,
  66:             'password' => $password,
  67:             'prefix' => $prefix,
  68:             'options' =>  $options + $defaultOptions,
  69:         );
  70:         
  71:         return $this;
  72:     }
  73:     
  74:     /**
  75:      * Set current connection
  76:      * @param string $name
  77:      */
  78:     public function switchConnection($name = 'default'){
  79:         $this->current = $name;
  80:         
  81:         return $this;
  82:     }
  83: 
  84:     /**
  85:      * Create a model factory
  86:      * 
  87:      * @param string $model
  88:      * @return TinyDBFactory
  89:      */
  90:     public function factory($model){
  91:         return new TinyDBFactory($this, $model);
  92:     }
  93:     
  94:     /**
  95:      * Create command
  96:      * 
  97:      * @return TinyDBCommand
  98:      */
  99:     public function command(){
 100:         return new TinyDBCommand($this);
 101:     }
 102:     
 103:     /**
 104:      * @link http://www.php.net/manual/en/pdo.getattribute.php
 105:      */
 106:     public function getAttribute($attribute){
 107:         return $this->getPDO()->getAttribute($attribute);
 108:     }
 109:     
 110:     /**
 111:      * @link http://www.php.net/manual/en/pdo.setattribute.php
 112:      */
 113:     public function setAttribute($attribute, $value){
 114:         return $this->getPDO()->setAttribute($attribute, $value);
 115:     }
 116:     
 117:     /**
 118:      * @link http://www.php.net/manual/en/pdo.prepare.php
 119:      */
 120:     public function prepare(){
 121:         return call_user_func_array(array($this->getPDO(), 'prepare'), func_get_args());
 122:     }
 123:     
 124:     /**
 125:      * @link http://www.php.net/manual/en/pdo.query.php
 126:      */
 127:     public function query(){
 128:         return call_user_func_array(array($this->getPDO(), 'query'), func_get_args());
 129:     }
 130:     
 131:     /**
 132:      * @link http://www.php.net/manual/en/pdo.exec.php
 133:      */
 134:     public function exec(){
 135:         return call_user_func_array(array($this->getPDO(), 'exec'), func_get_args());
 136:     }
 137:     
 138:     /**
 139:      * @link http://www.php.net/manual/en/pdo.begintransaction.php
 140:      */
 141:     public function beginTransaction(){
 142:         return $this->getPDO()->beginTransaction();
 143:     }
 144:     
 145:     /**
 146:      * @link http://www.php.net/manual/en/pdo.rollback.php
 147:      */
 148:     public function rollBack(){
 149:         return $this->getPDO()->rollBack();
 150:     }
 151:     
 152:     /**
 153:      * @link http://www.php.net/manual/en/pdo.commit.php
 154:      */
 155:     public function commit(){
 156:         return $this->getPDO()->commit();
 157:     }
 158:     
 159:     /**
 160:      * @link http://www.php.net/manual/en/pdo.intransaction.php
 161:      */
 162:     public function inTransaction(){
 163:         return $this->getPDO()->inTransaction();
 164:     }
 165:     
 166:     /**
 167:      * @link http://www.php.net/manual/en/pdo.lastinsertid.php
 168:      */
 169:     public function lastInsertId(){
 170:         return call_user_func_array(array($this->getPDO(), 'lastInsertId'), func_get_args());
 171:     }
 172:     
 173:     /**
 174:      * @link http://www.php.net/manual/en/pdo.quote.php
 175:      */
 176:     public function quote(){
 177:         return call_user_func_array(array($this->getPDO(), 'quote'), func_get_args());
 178:     }
 179:     
 180:     public function errorCode(){
 181:         return call_user_func_array(array($this->getPDO(), 'errorCode'), func_get_args());
 182:     }
 183:     
 184:     public function errorInfo(){
 185:         return call_user_func_array(array($this->getPDO(), 'errorInfo'), func_get_args());
 186:     }
 187:     
 188:     /**
 189:      * Quote table name
 190:      * @param string $name
 191:      * @return string;
 192:      */
 193:     public function quoteTable($name){
 194:         return $this->quoteIdentifier($name);
 195:     }
 196:     
 197:     /**
 198:      * Quote column name
 199:      * @param string $name
 200:      * @return string;
 201:      */
 202:     public function quoteColumn($name){
 203:         return $this->quoteIdentifier($name);
 204:     }
 205:     
 206:     /**
 207:      * Quote table or column
 208:      * @param string $name
 209:      * @return string
 210:      */
 211:     public function quoteIdentifier($name){
 212:         $quote = null;
 213:         switch($this->getAttribute(PDO::ATTR_DRIVER_NAME)){
 214:             case 'pgsql':
 215:             case 'sqlsrv':
 216:             case 'dblib':
 217:             case 'mssql':
 218:             case 'sybase':
 219:                 $quote = '"';
 220:             case 'mysql':
 221:             case 'sqlite':
 222:             case 'sqlite2':
 223:             default:
 224:                 $quote = '`';
 225:         }
 226:         
 227:         $parts = explode('.', $name);
 228:         foreach($parts as $k => $part){
 229:             if($part !== '*'){
 230:                 $parts[$k] = $quote.$part.$quote;
 231:             }
 232:         }
 233:         
 234:         return implode('.', $parts);
 235:     }
 236:     
 237:     public function buildLimitOffset($sql, $limit, $offset = 0){
 238:         switch($this->getAttribute(PDO::ATTR_DRIVER_NAME)){
 239:             case 'sqlsrv':
 240:             case 'dblib':
 241:             case 'mssql':
 242:             case 'sybase':
 243:                 throw new TinyDBException('Limit/offset not implemented yet');
 244:             case 'pgsql':
 245:             case 'mysql':
 246:             case 'sqlite':
 247:             case 'sqlite2':
 248:             default:
 249:                 if($limit > 0){
 250:                     $sql .= "\n LIMIT ".$limit;
 251:                 }
 252:                 if($offset > 0){
 253:                     $sql .= " OFFSET ".$offset;
 254:                 }
 255:                 
 256:                 return $sql;
 257:         }
 258:     }
 259: 
 260:     public function getPrefix(){
 261:         return $this->configs[$this->current]['prefix'];
 262:     }
 263:     
 264:     public function fixPrefix($sql){
 265:         
 266:     }
 267: }
 268: 
 269: /**
 270:  * Model
 271:  */
 272: class TinyDBModel
 273: {
 274:     ### Model defination ###
 275:     
 276:     private $_table;
 277:     
 278:     private $_pk;
 279:     
 280:     private $_relations;
 281:     
 282:     ### end ###
 283:     
 284:     protected $_db;
 285:     
 286:     protected $_data = array();
 287:     
 288:     protected $_safe;
 289:     
 290:     protected $_dirty = array();
 291:     
 292:     protected $_isNew;
 293:     
 294:     /**
 295:      * Model configuration
 296:      * 
 297:      * Sub classes should extend this static method
 298:      * @return array
 299:      */
 300:     public static function config(){
 301:         return array(
 302:             //'table' => 'table',
 303:             'pk' => 'id',
 304:             //'relations' => array(),
 305:         );
 306:     }
 307:     
 308:     /**
 309:      * Get model configuration
 310:      * 
 311:      * @param string $key
 312:      * @return mixed
 313:      */
 314:     final public static function getConfig($key = null){
 315:         $config = static::config();
 316:         if(null === $key){
 317:             return $config;
 318:         }
 319:         elseif(isset($config[$key])){
 320:             return $config[$key];
 321:         }
 322:         else{
 323:             return null;
 324:         }
 325:     }
 326:     
 327:     /**
 328:      * Get model table
 329:      */
 330:     public function getTable(){
 331:         return $this->_table;
 332:     }
 333: 
 334:     /**
 335:      * Get model pk
 336:      */
 337:     public function getPK(){
 338:         return $this->_pk;
 339:     }
 340:         
 341:     /**
 342:      * Get model relations
 343:      */
 344:     public function getRelations(){
 345:         return $this->_relations;
 346:     }
 347:     
 348:     /**
 349:      * Translate class name to table name.
 350:      * example:
 351:      *  BlogPost => blog_post
 352:      *  Acme\BlogPost => blog_post
 353:      * @param string $class Class name
 354:      * @return string
 355:      */
 356:     static public function entityNameToDBName($class){
 357:         //namespace
 358:         if(false !== $pos = strrpos($class, '\\')){
 359:             $class = substr($class, $pos + 1);
 360:         }
 361:         
 362:         return strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $class));
 363:     }
 364:     
 365:     /**
 366:      * Model constructor
 367:      * 
 368:      * @param TinyDB $db
 369:      * @param array $data
 370:      * @boolean $isNew
 371:      */
 372:     public function __construct($db, $data = array(), $isNew = true){
 373:         $this->_db = $db;
 374:         
 375:         if($isNew){
 376:             $this->_dirty = $data;
 377:         }
 378:         else{
 379:             $this->_data = $data;
 380:         }
 381:         $this->_isNew = $isNew;
 382:         
 383:         //this requires PHP 5.3
 384:         $this->setAttributes(static::config());
 385:     }
 386:     
 387:     /**
 388:      * Set attribute
 389:      */
 390:     public function setAttribute($attribute, $value){
 391:         if($attribute === 'table'){
 392:             $this->_table = $value;
 393:         }
 394:         elseif($attribute === 'pk'){
 395:             $this->_pk = $value;
 396:         }
 397:         elseif($attribute === 'relations'){
 398:             $this->_relations = $value;
 399:         }
 400:         
 401:         return $this;
 402:     }
 403:     
 404:     /**
 405:      * Set attributes(table, pk...)
 406:      * @param array $attributes
 407:      */
 408:     public function setAttributes($attributes){
 409:         foreach($attributes as $k => $v){
 410:             $this->setAttribute($k, $v);
 411:         }
 412:         
 413:         return $this;
 414:     }
 415:     
 416:     public function isNew(){
 417:         return $this->_isNew;
 418:     }
 419:     
 420:     public function isDirty(){
 421:         return !empty($this->_dirty);
 422:     }
 423:     
 424:     /**
 425:      * Get raw data
 426:      * @param string $key
 427:      * @return mixed
 428:      */
 429:     public function getRaw($key = null){
 430:         if(null === $key){
 431:             return $this->_data;
 432:         }
 433:         else{
 434:             return isset($this->_data[$key])?$this->_data[$key]:null;
 435:         }
 436:     }
 437:     
 438:     /**
 439:      * Get data
 440:      * @param string $key
 441:      * @return mixed Find in dirty data first, return all data if key is not specified
 442:      */
 443:     public function get($key = null){
 444:         if(null === $key){
 445:             return array_merge($this->_data, $this->_dirty);
 446:         }
 447:         else{
 448:             if(isset($this->_dirty[$key])){
 449:                 return $this->_dirty[$key];
 450:             }
 451:             elseif(isset($this->_data[$key])){
 452:                 return $this->_data[$key];
 453:             }
 454:             else{
 455:                 return null;
 456:             }
 457:         }
 458:     }
 459:     
 460:     public function __get($key){
 461:         $relations = $this->getRelations();
 462:         if(isset($relations[$key])){
 463:             return $this->getWithRelation($key);
 464:         }
 465:         return $this->get($key);
 466:     }
 467:     
 468:     public function getWithRelation($name){
 469:         $relations = $this->getRelations();
 470:         if(isset($relations[$name])){
 471:             $relation = $relations[$name];
 472:             if($relation['relation'] === 'OTO'){
 473:                 return $this->getOneToOne($relation['target'], isset($relation['key'])?$relation['key']:null, isset($relation['target_key'])?$relation['target_key']:null);
 474:             }
 475:             elseif($relation['relation'] == 'OTM'){
 476:                 return $this->getOneToMany($relation['target'], isset($relation['key'])?$relation['key']:null, isset($relation['target_key'])?$relation['target_key']:null);
 477:             }
 478:             elseif($relation['relation'] == 'MTO'){
 479:                 return $this->getManyToOne($relation['target'], isset($relation['key'])?$relation['key']:null, isset($relation['target_key'])?$relation['target_key']:null);
 480:             }
 481:             elseif($relation['relation'] == 'MTM'){
 482:                 return $this->getManyToMany($relation['target'], $relation['through'], isset($relation['key'])?$relation['key']:null, isset($relation['target_key'])?$relation['target_key']:null);
 483:             }
 484:             else{
 485:                 throw new TinyDBException('Invalid relation "'.$relation['relation'].'"');
 486:             }
 487:         }
 488:         else{
 489:             return false;
 490:         }
 491:     }
 492:     
 493:     /**
 494:      * Has one
 495:      * 
 496:      * @param string $target target model or @table
 497:      * @param string $key
 498:      * @param string $target_key
 499:      * 
 500:      * @return TinyDBModel
 501:      */
 502:     public function getOneToOne($target, $key = null, $target_key = null){
 503:         $factory = $this->_db->factory($target);
 504:         if(null === $key){
 505:             $key = $this->getPK();
 506:         }
 507:         if(null === $target_key){
 508:             $target_key = $factory->getPK();
 509:         }
 510:         
 511:         return $factory->findOneBy($target_key, $this->get($key));
 512:     }
 513:     
 514:     /**
 515:      * Has many
 516:      * 
 517:      * @param string $target
 518:      * @param string $key
 519:      * @param string $target_key
 520:      * @return array
 521:      */
 522:     public function getOneToMany($target, $key = null, $target_key = null){
 523:         $factory = $this->_db->factory($target);
 524:         if(null === $key){
 525:             $key = $this->getPK();
 526:         }
 527:         if(null === $target_key){
 528:             $target_key = $key;
 529:         }
 530:         
 531:         return $factory->findManyBy($target_key, $this->get($key));
 532:     }
 533:     
 534:     /**
 535:      * Belongs to
 536:      * @param string $target
 537:      * @param string $key
 538:      * @param string $target_key
 539:      * 
 540:      * @return TinyDBModel
 541:      */
 542:     public function getManyToOne($target, $key = null, $target_key = null){
 543:         $factory = $this->_db->factory($target);
 544:         if(null === $target_key){
 545:             $target_key = $factory->getPK();
 546:         }
 547:         if(null === $key){
 548:             $key = $target_key;
 549:         }
 550:         
 551:         return $factory->findOneBy($target_key, $this->get($key));
 552:     }
 553:     
 554:     /**
 555:      * Many to many
 556:      * 
 557:      * @param string $target
 558:      * @param string $through
 559:      * @param string $key
 560:      * @param string $target_key
 561:      * @return array
 562:      */
 563:     public function getManyToMany($target, $through, $key = null, $target_key = null){
 564:         $factory = $this->_db->factory($target);
 565: 
 566:         if(null === $key){
 567:             $key = $this->getPK();
 568:         }
 569:         if(null === $target_key){
 570:             $target_key = $factory->getPK();
 571:         }
 572:         
 573:         $through = $this->parseThrough($through);
 574:         if(!$through[1]){
 575:             $through[1] = $key;
 576:         }
 577:         if(!$through[2]){
 578:             $through[2] = $target_key;
 579:         }
 580:         
 581:         $rows = $this->_db->command()
 582:             ->select('t.*')
 583:             ->from($factory->getTable().' t')
 584:             ->leftJoin($through[0].' m', 'm.'.$through[2].'=t.'.$target_key)
 585:             ->where('m.'.$through[1].'=:value', array(
 586:                 ':value' => $this->get($key)
 587:             ))
 588:         ->queryAll();
 589:         
 590:         if(false === $rows){
 591:             return false;
 592:         }
 593:         
 594:         return $factory->mapModels($rows);
 595:     }
 596:     
 597:     protected function parseThrough($through){
 598:         $through = explode(',', $through);
 599:         $table = trim($through[0]);
 600:         $key = isset($through[1])?trim($through[1]):null;
 601:         $target_key = isset($through[2])?trim($through[2]):null;
 602:         
 603:         return array($table, $key, $target_key);
 604:     }
 605:     
 606:     /**
 607:      * Set data
 608:      * @param string|array $key
 609:      * @param mixed $value
 610:      */
 611:     public function set($key, $value = null){
 612:         if(is_array($key)){
 613:             $this->_dirty = $key;
 614:         }
 615:         else{
 616:             $this->_dirty[$key] = $value;
 617:         }
 618:         
 619:         return $this;
 620:     }
 621:     
 622:     public function __set($key, $value){
 623:         $this->_dirty[$key] = $value;
 624:     }
 625:     
 626:     public function __isset($key){
 627:         return isset($this->_dirty[$key]) || isset($this->_data[$key]);
 628:     }
 629:     
 630:     public function __unset($key){
 631:         if(isset($this->_dirty[$key])){
 632:             unset($this->_dirty[$key]);
 633:         }
 634:     }
 635:     
 636:     protected function buildPKConditions(){
 637:         $pk = $this->getPK();
 638:         if(is_string($pk)){
 639:             $pks = array($pk);
 640:         }
 641:         else{
 642:             $pks = $pk;
 643:         }
 644:         $params = array();
 645:         foreach($pks as $k => $pk){
 646:             $pks[$k] = $pk.'=:pk'.$k;
 647:             $params[':pk'.$k] = $this->_data[$pk];
 648:         }
 649:         array_unshift($pks, 'AND');
 650:         
 651:         return array($pks, $params);
 652:     }
 653:     
 654:     /**
 655:      * Save modified data to db
 656:      * @return int|boolean
 657:      */
 658:     public function save(){
 659:         if($this->beforeSave()){
 660:             if($this->isNew()){
 661:                 $data = $this->_dirty;
 662:                 
 663:                 //insert
 664:                 if(false !== $rst = $this->_db->command()->insert($this->getTable(), $data))
 665:                 {
 666:                     if(is_string($this->getPK()) && $id = $this->_db->lastInsertId()){
 667:                         $data[$this->getPK()] = $id;
 668:                     }
 669:                     $this->_data = $data;
 670:                     $this->_dirty = array();
 671:                     $this->_isNew = false;
 672:                     $this->afterSave();
 673:                     return $rst;
 674:                 }
 675:             }
 676:             else{
 677:                 if($this->isDirty()){
 678:                     //update
 679:                     $pkConditions = $this->buildPKConditions();
 680:                     if(false !== $rst = $this->_db->command()->update($this->getTable(), $this->_dirty, $pkConditions[0], $pkConditions[1])){
 681:                         $this->_data = array_merge($this->_data, $this->_dirty);
 682:                         $this->_dirty = array();
 683:                         $this->afterSave();
 684:                         return $rst;
 685:                     }
 686:                 }
 687:             }
 688:         }
 689:         
 690:         return false;
 691:     }
 692:     
 693:     /**
 694:      * Delete model
 695:      * @return int|boolean
 696:      */
 697:     public function delete(){
 698:         if($this->beforeDelete()){
 699:             $pkConditions = $this->buildPKConditions();
 700:             if(false !== $rst = ($this->isNew() || $this->_db->command()->delete($this->getTable(), $pkConditions[0], $pkConditions[1]))){
 701:                 $this->_data = array();
 702:                 $this->_dirty = array();
 703:                 $this->afterDelete();
 704:                 
 705:                 return $rst;
 706:             }
 707:         }
 708:         
 709:         return false;
 710:     }
 711:     
 712:     protected function beforeSave(){
 713:         return true;
 714:     }
 715:     
 716:     protected function afterSave(){
 717:         return true;
 718:     }
 719:     
 720:     protected function beforeDelete(){
 721:         return true;
 722:     }
 723:     
 724:     protected function afterDelete(){
 725:         return true;
 726:     }
 727:     
 728: }
 729: 
 730: /**
 731:  * Model factory
 732:  */
 733: class TinyDBFactory
 734: {
 735:     protected $db;
 736:     protected $modelClass;
 737:     protected $table;
 738:     protected $pk;
 739:     protected $with;
 740:     
 741:     public function __construct($db, $model, $pk = null){
 742:         $this->db = $db;
 743:         if($model[0] === '@'){
 744:             $this->modelClass = 'TinyDBModel';
 745:             $this->table = substr($model, 1);
 746:         }
 747:         else{
 748:             $this->modelClass = $model;
 749:             if(null !== $model::getConfig('table')){
 750:                 $this->table = $model::getConfig('table');
 751:             }
 752:             else{
 753:                 $this->table = $model::entityNameToDBName($model);
 754:             }
 755:         }
 756:         if(null !== $pk){
 757:             $this->pk = $pk;
 758:         }
 759:         else{
 760:             $class = $this->modelClass;
 761:             $this->pk = $class::getConfig('pk');
 762:         }
 763:     }
 764:     
 765:     /**
 766:      * PK getter
 767:      */
 768:     public function getPK(){
 769:         return $this->pk;
 770:     }
 771:     
 772:     /**
 773:      * Table getter
 774:      */
 775:     public function getTable(){
 776:         return $this->table;
 777:     }
 778:     
 779:     /**
 780:      * @todo fetch related data together
 781:      */
 782:     public function with($with){
 783:         $this->with = $with;
 784:         
 785:         return $this;
 786:     }
 787:     
 788:     public function buildWith(){
 789:         $with = $this->with;
 790:         $columns = explode(',', $with);
 791:         foreach($columns as $column){
 792:             //if(strpos($column, '.')
 793:         }
 794:         $this->with = null;
 795:     }
 796:     
 797:     /**
 798:      * Map array to model
 799:      * 
 800:      * @param array $row
 801:      * @return TinyDBModel
 802:      */
 803:     public function map($row){
 804:         $class = $this->modelClass;
 805:         $model = new $class($this->db, $row, false);
 806:         $model->setAttribute('table', $this->table)->setAttribute('pk', $this->pk);
 807:         return $model;
 808:     }
 809:     
 810:     /**
 811:      * Map data rows to model array
 812:      * 
 813:      * @param array $rows
 814:      * @return array
 815:      */
 816:     public function mapModels($rows){
 817:         $rst = array();
 818:         foreach($rows as $row){
 819:             $rst[] = $this->map($row);
 820:         }
 821:         
 822:         return $rst;
 823:     }
 824:     
 825:     /**
 826:      * Create a fresh model from array
 827:      * 
 828:      * @param array $row
 829:      * @param TinyDBModel
 830:      */
 831:     public function create($row = array()){
 832:         $class = $this->modelClass;
 833:         $model = new $class($this->db, $row);
 834:         $model->setAttribute('table', $this->table)->setAttribute('pk', $this->pk);
 835:         
 836:         return $model;
 837:     }
 838:     
 839:     /**
 840:      * Build PK condition(multiple pk is also supported)
 841:      * @param string|array $pk
 842:      * @param mixed $_data
 843:      * @return array
 844:      */
 845:     protected function buildPKConditions($pk, $_data){
 846:         if(is_string($pk)){
 847:             $pks = array($pk);
 848:             if(!is_array($_data)){
 849:                 $_data = array($pk => $_data);
 850:             }
 851:         }
 852:         else{
 853:             $pks = $pk;
 854:         }
 855:         $params = array();
 856:         foreach($pks as $k => $pk){
 857:             $pks[$k] = $pk.'=:pk'.$k;
 858:             $params[':pk'.$k] = $_data[$pk];
 859:         }
 860:         array_unshift($pks, 'AND');
 861:         
 862:         return array($pks, $params);
 863:     }
 864:     
 865:     /**
 866:      * Count with conditions
 867:      * 
 868:      * @param string|array $conditions
 869:      * @param array $params
 870:      * @return integer
 871:      */
 872:     public function count($conditions = '', $params = array()){
 873:         return $this->db->command()->select('COUNT(*)')->from($this->table)->where($conditions, $params)->queryScalar();
 874:     }
 875:     
 876:     /**
 877:      * Count by key
 878:      * 
 879:      * @param string $key
 880:      * @param mixed $value
 881:      * @return integer
 882:      */
 883:     public function countBy($key, $value){
 884:         return $this->count($key.'=:key', array(':key' => $value));
 885:     }
 886:     
 887:     /**
 888:      * Find by PK
 889:      * @param int $id
 890:      */
 891:     public function find($pk){
 892:         return $this->findByPK($pk);
 893:     }
 894:     
 895:     /**
 896:      * Find all
 897:      */
 898:     public function findAll(){
 899:         $rows = $this->db->command()->select()->from($this->table)->queryAll();
 900:         if(false === $rows){
 901:             return $rows;
 902:         }
 903:         
 904:         return $this->mapModels($rows);
 905:     }
 906:     
 907:     /**
 908:      * Find one model with conditions
 909:      * 
 910:      * @param string|array $conditions
 911:      * @param array $params
 912:      * @return boolean|TinyDBModel
 913:      */
 914:     public function findOne($conditions, $params = array()){
 915:         $row = $this->db->command()->select()->from($this->table)->where($conditions, $params)->limit(1)->queryRow();
 916:         if(false === $row){
 917:             return false;
 918:         }
 919:         
 920:         return $this->map($row);
 921:     }
 922:         
 923:     /**
 924:      * Find one model by key
 925:      * 
 926:      * @param string $key
 927:      * @param mixed $value
 928:      * @return boolean|TinyDBModel
 929:      */
 930:     public function findOneBy($key, $value){
 931:         return $this->findOne($key.'=:key', array(':key' => $value));
 932:     }
 933:     
 934:     /**
 935:      * Find one model by primary key
 936:      * 
 937:      * @param string|array $pk
 938:      * @return boolean|TinyDBModel
 939:      */
 940:     public function findByPK($pk){
 941:         $pkConditions = $this->buildPKConditions($this->pk, $pk);
 942:         return $this->findOne($pkConditions[0], $pkConditions[1]);
 943:     }
 944:     
 945:     /**
 946:      * Find many models with conditions
 947:      * 
 948:      * @param string|array $conditions
 949:      * @param array $params
 950:      * @param string|array $orderBy
 951:      * @param int $limit
 952:      * @param int $offset
 953:      * @return boolean|array
 954:      */
 955:     public function findMany($conditions = '', $params = array(), $orderBy = null, $limit = null, $offset = null){
 956:         $cmd = $this->db->command()->select()->from($this->table)->where($conditions, $params);
 957:         if($orderBy){
 958:             $cmd->orderBy($orderBy);
 959:         }
 960:         $rows = $cmd->limit($limit, $offset)->queryAll();
 961:         if(false === $rows){
 962:             return false;
 963:         }
 964:         else{
 965:             return $this->mapModels($rows);
 966:         }
 967:     }
 968:     
 969:     /**
 970:      * Find many models with by key
 971:      * 
 972:      * @param string $key
 973:      * @param mixed $value
 974:      * @param string|array $orderBy
 975:      * @param int $limit
 976:      * @param int $offset
 977:      * @return boolean|array
 978:      */
 979:     public function findManyBy($key, $value,  $orderBy = null, $limit = null, $offset = null){
 980:         return $this->findMany($key.'=:key', array(':key' => $value), $orderBy, $limit, $offset);
 981:     }
 982:     
 983:     /**
 984:      * Update table with condition
 985:      * 
 986:      * @param array $values
 987:      * @param string|array $conditions
 988:      * @param array $params
 989:      * @return int|boolean
 990:      */
 991:     public function update($values, $conditions = '', $params = array()){
 992:         return $this->db->command()->update($this->table, $values, $conditions, $params);
 993:     }
 994:     
 995:     /**
 996:      * Update table by key
 997:      * 
 998:      * @param string $key
 999:      * @param mixed $value
1000:      * @param array $values
1001:      * @return int|boolean
1002:      */
1003:     public function updateBy($key, $value, $values){
1004:         return $this->update($values, $key.'=:key', array(':key' => $value));
1005:     }
1006:     
1007:     /**
1008:      * Update table by primary key
1009:      * @param string|array $pk
1010:      * @param array $values
1011:      * @return int|boolean
1012:      */
1013:     public function updateByPK($pk, $values){
1014:         $pkConditions = $this->buildPKConditions($this->pk, $pk);
1015:         return $this->update($values, $pkConditions[0], $pkConditions[1]);
1016:     }
1017:     
1018:     /**
1019:      * Insert
1020:      * 
1021:      * @param array $values
1022:      * @return int|boolean
1023:      */
1024:     public function insert($values){
1025:         return $this->db->command()->insert($this->table, $values);
1026:     }
1027:     
1028:     /**
1029:      * Delete with condition
1030:      * 
1031:      * @param string|array $conditioins
1032:      * @param array $params
1033:      * @return int|boolean
1034:      */
1035:     public function delete($conditions = '', $params = array()){
1036:         return $this->db->command()->delete($this->table, $conditions, $params);
1037:     }
1038:     
1039:     /**
1040:      * Delete by key
1041:      * 
1042:      * @param string $key
1043:      * @param mixed $value
1044:      * @return int|boolean
1045:      */
1046:     public function deleteBy($key, $value){
1047:         return $this->delete($key.'=:key', array(':key' => $value));
1048:     }
1049:     
1050:     /**
1051:      * Delete by primary key
1052:      * 
1053:      * @param string|array $pk
1054:      * @return int|boolean
1055:      */
1056:     public function deleteByPK($pk){
1057:         $pkConditions = $this->buildPKConditions($this->pk, $pk);
1058:         return $this->delete($pkConditions[0], $pkConditions[1]);
1059:     }
1060:     
1061:     /**
1062:      * Override __call magic method to provide many helper methods
1063:      * 
1064:      * @param string $name
1065:      * @param array $arguments
1066:      * @return mixed
1067:      */
1068:     public function __call($name, $arguments){
1069:         //findOneByXXX/findManyByXXX
1070:         if(preg_match('#^find(One|Many)By(.+)$#', $name, $matches)){
1071:             $one = $matches[1] === 'One';
1072:             $findByKey = TinyDBModel::entityNameToDBName($matches[2]);
1073:             array_unshift($arguments, $findByKey);
1074:             if($one){
1075:                 return call_user_func_array(array($this, 'findOneBy'), $arguments);
1076:             }
1077:             else{
1078:                 return call_user_func_array(array($this, 'findManyBy'), $arguments);
1079:             }
1080:         }
1081:         elseif(preg_match('#^(update|delete|count)By(.+)$#', $name, $matches)){
1082:             $action = $matches[1];
1083:             $actionByKey = TinyDBModel::entityNameToDBName($matches[2]);
1084:             array_unshift($arguments, $actionByKey);
1085:             return call_user_func_array(array($this, $action.'By'), $arguments); 
1086:         }
1087:         else{
1088:             throw new TinyDBException(sprintf('Helper method "%s" does not exist', $name));
1089:         }
1090:     }
1091: }
1092: 
1093: /**
1094:  * Database command/query builder
1095:  */
1096: class TinyDBCommand
1097: {
1098:     protected $db;
1099:     protected $statement;
1100:     protected $params = array();
1101:     protected $query = array();
1102:     protected $sql;
1103:     
1104:     /**
1105:      * Constructor
1106:      * 
1107:      * @param TinyDB $db Database connection
1108:      */
1109:     public function __construct($db){
1110:         $this->db = $db;
1111:     }
1112:     
1113:     protected function matchAlias($entry){
1114:         if(preg_match('#^(.*?)(?i:\s+as\s+|\s+)(.*)$#', $entry, $matches)){
1115:             return array(
1116:                 $matches[1],
1117:                 $matches[2],
1118:             );
1119:         }
1120:         else{
1121:             return false;
1122:         }
1123:     }
1124:     
1125:     protected function splitParts($parts){
1126:         return preg_split('#\s*,\s*#', trim($parts), -1, PREG_SPLIT_NO_EMPTY);
1127:     }
1128:     
1129:     /**
1130:      * Reset command
1131:      */
1132:     public function reset(){
1133:         $this->statement = null;
1134:         $this->params = array();
1135:         $this->query = array();
1136:         $this->sql = null;
1137:         
1138:         return $this;
1139:     }
1140:     
1141:     /**
1142:      * Prepare PDO statement
1143:      */
1144:     public function prepare(){
1145:         if(null === $this->statement){
1146:             $this->statement = $this->db->prepare($this->getSql());
1147:         }
1148:         
1149:         return $this;
1150:     }
1151:     
1152:     public function bindParam(){
1153:         $this->prepare();
1154:         call_user_func_array(array($this->statement, 'bindParam'), func_get_args);
1155:     }
1156:     
1157:     public function bindValue(){
1158:         $this->prepare();
1159:         call_user_func_array(array($this->statement, 'bindValue'), func_get_args());
1160:         
1161:         return $this;
1162:     }
1163:     
1164:     public function bindValues($values){
1165:         $this->prepare();
1166:         
1167:         foreach($values as $k=> $value){
1168:             $this->statement->bindValue($k, $value);
1169:         }
1170:         
1171:         return $this;
1172:     }
1173:     
1174:     public function mergeParams($params){
1175:         foreach($params as $k => $v){
1176:             $this->params[$k] = $v;
1177:         }
1178:     }
1179:     
1180:     /**
1181:      * SELECT statement
1182:      * example:
1183:      *  select('contact.*, user.email)
1184:      *  select(array('contact.*', 'user.email'))
1185:      * @param string|array $fields
1186:      */
1187:     public function select($fields = '*'){
1188:         if(is_string($fields) && strpos($fields, '(') !== false){
1189:             $this->query['select'] = $fields;
1190:         }
1191:         else{
1192:             if(!is_array($fields)){
1193:                 $fields = $this->splitParts($fields);
1194:             }
1195:             foreach($fields as $k => $field){
1196:                 if(false === strpos($field, '(')){
1197:                     if($alias = $this->matchAlias($field)){
1198:                         $fields[$k] = $this->db->quoteColumn($alias[0]).' AS '.$this->db->quoteColumn($alias[1]);
1199:                     }
1200:                     else{
1201:                         $fields[$k] = $this->db->quoteColumn($field);
1202:                     }
1203:                 }
1204:             }
1205:             $this->query['select'] = implode(', ', $fields);
1206:         }
1207:         
1208:         return $this;
1209:     }
1210:     
1211:     /**
1212:      * DISTINCT statement
1213:      */
1214:     public function distinct(){
1215:         $this->query['distinct'] = true;
1216:         
1217:         return $this;
1218:     }
1219:     
1220:     /**
1221:      * FROM statement
1222:      * example:
1223:      *  from('contact, user AS U')
1224:      *  from(array('contact', 'user AS U'))
1225:      * @param string|array $tables
1226:      */
1227:     public function from($tables){
1228:         if(is_string($tables)){
1229:             $tables = $this->splitParts($tables);
1230:         }
1231:         foreach($tables as $k => $table){
1232:             if($alias = $this->matchAlias($table)){
1233:                 $tables[$k] = $this->db->quoteTable($alias[0]).' AS '.$this->db->quoteTable($alias[1]);
1234:             }
1235:             else{
1236:                 $tables[$k] = $this->db->quoteTable($table);
1237:             }
1238:         }
1239:         
1240:         $this->query['from'] = implode(', ', $tables);
1241:         
1242:         return $this;
1243:     }
1244:     
1245:     /**
1246:      * Build condition SQL
1247:      * @param string|array $conditions
1248:      */
1249:     public function buildConditions($conditions){
1250:         if(!is_array($conditions)){
1251:             if(null === $conditions){
1252:                 return '';
1253:             }
1254:             return $conditions;
1255:         }
1256:         elseif($conditions === array()){
1257:             return '';
1258:         }
1259:         
1260:         $n = count($conditions);
1261:         $operator = strtoupper($conditions[0]);
1262:         if($operator === 'AND' || $operator === 'OR'){
1263:             $result = array();
1264:             for($i = 1; $i < $n; $i++){
1265:                 $condition = $this->buildConditions($conditions[$i]);
1266:                 if('' !== $condition){
1267:                     $result[] = '('.$condition.')';
1268:                 }
1269:             }
1270:             if($result === array()){
1271:                 return '';
1272:             }
1273:             else{
1274:                 return implode(' '.$operator.' ', $result);
1275:             }
1276:         }
1277:         
1278:         if(!isset($conditions[1], $conditions[2])){
1279:             return '';
1280:         }
1281:         
1282:         $column = $this->db->quoteColumn($conditions[1]);
1283:         $values = $conditions[2];
1284:         if(!is_array($values)){
1285:             $values = array($values);
1286:         }
1287:         if($operator === 'IN' || $operator === 'NOT IN'){
1288:             if($values === array()){
1289:                 return $operator === 'IN'?'0':'';
1290:             }
1291:             foreach($values as $k => $value){
1292:                 $values[$k] = $this->db->quote($value);
1293:             }
1294:             
1295:             return $column.' '.$operator.' ('.implode(',', $values).')';
1296:         }
1297:         elseif($operator === 'LIKE' || $operator === 'NOT LIKE' || $operator === 'OR LIKE' || $operator === 'OR NOT LIKE'){
1298:             if($values === array()){
1299:                 return ($operator === 'LIKE' || $operator === 'OR LIKE')?'0':'';
1300:             }
1301:             if($operator === 'LIKE' || $operator === 'NOT LIKE'){
1302:                 $andor = 'AND';
1303:             }
1304:             else{
1305:                 $andor = 'OR';
1306:                 $operator = $operator === 'OR LIKE'?'LIKE':'NOT LIKE';
1307:             }
1308:             
1309:             $result = array();
1310:             foreach($values as $k => $value){
1311:                 $result[] = $column.' '.$operator.' '.$this->db->quote($value);
1312:             }
1313:             
1314:             return implode($andor, $result);
1315:         }
1316:         else{
1317:             throw new TinyDBException('Invalid operator "'.$operator.'"');
1318:         }
1319:     }
1320:     
1321:     /**
1322:      * WHERE statement
1323:      * @param string|array $conditions
1324:      * @param array $params
1325:      */
1326:     public function where($conditions, $params = array()){
1327:         $this->mergeParams($params);
1328:         $this->query['where'] = $this->buildConditions($conditions);
1329:         
1330:         return $this;
1331:     }
1332:     
1333:     /**
1334:      * ORDER BY statement
1335:      * example:
1336:      *  orderBy('id DESC')
1337:      *  orderBy('firstname, lastname DESC')
1338:      * @param string|array $fields
1339:      */
1340:     public function orderBy($fields){
1341:         if(!is_array($fields)){
1342:             $fields = $this->splitParts($fields);
1343:         }
1344:         
1345:         foreach($fields as $k => $field){
1346:             if(preg_match('#^(.*?)\s+(asc|desc)$#i', $field, $matches)){
1347:                 $fields[$k] = $this->db->quoteColumn($matches[1]).' '.$matches[2];
1348:             }
1349:             else{
1350:                 $fields[$k] = $this->db->quoteColumn($field);
1351:             }
1352:         }
1353:         
1354:         $this->query['order'] = implode(',', $fields);
1355:         
1356:         return $this;
1357:     }
1358:     
1359:     /**
1360:      * LIMIT statement
1361:      * @param int $limit
1362:      * @param int $offset
1363:      */
1364:     public function limit($limit, $offset = null){
1365:         $this->query['limit'] = $limit;
1366:         if(null !== $offset){
1367:             $this->offset($offset);
1368:         }
1369:         
1370:         return $this;
1371:     }
1372:     
1373:     /**
1374:      * OFFSET statement
1375:      * @param int $offset
1376:      */
1377:     public function offset($offset){
1378:         $this->query['offset'] = $offset;
1379:         
1380:         return $this;
1381:     }
1382:     
1383:     /**
1384:      * * JOIN statement
1385:      * @param string $type LEFT JOIN|RIGHT JOIN...
1386:      * @param string $table
1387:      * @param string|array $conditions
1388:      * @param array $params
1389:      */
1390:     protected function anyJoin($type, $table, $conditions = '', $params = array()){
1391:         $this->mergeParams($params);
1392:         if($alias = $this->matchAlias($table)){
1393:             $table = $this->db->quoteTable($alias[0]).' AS '.$this->db->quoteTable($alias[1]);
1394:         }
1395:         else{
1396:             $table = $this->db->quoteTable($table);
1397:         }
1398:         $conditions = $this->buildConditions($conditions);
1399:         if('' !== $conditions){
1400:             $conditions = ' ON '.$conditions;
1401:         }
1402:         
1403:         if(isset($this->query['join']) && is_string($this->query['join'])){
1404:             $this->query['join'] = array($this->query['join']);
1405:         }
1406:         
1407:         $this->query['join'][] = $type.' '.$table.' '.$conditions;
1408:         
1409:         return $this;
1410:     }
1411:     
1412:     /**
1413:      * JOIN statement
1414:      * 
1415:      * @param string $table
1416:      * @param string|array $conditions
1417:      * @param array $params
1418:      */
1419:     public function join($table, $conditions = '', $params = array()){
1420:         return $this->anyJoin('JOIN', $table, $conditions, $params);
1421:     }
1422:     
1423:     /**
1424:      * LEFT JOIN statement
1425:      * 
1426:      * @param string $table
1427:      * @param string|array $conditions
1428:      * @param array $params
1429:      */
1430:     public function leftJoin($table, $conditions = '', $params = array()){
1431:         return $this->anyJoin('LEFT JOIN', $table, $conditions, $params);
1432:     }
1433:     
1434:     /**
1435:      * RIGHT JOIN statement
1436:      * 
1437:      * @param string $table
1438:      * @param string|array $conditions
1439:      * @param array $params
1440:      */
1441:     public function rightJoin($table, $condtions = '', $params = array()){
1442:         return $this->anyJoin('RIGHT JOIN', $table, $conditions, $params);
1443:     }
1444:     
1445:     /**
1446:      * GROUP BY statement
1447:      * 
1448:      * @param string|array $fields
1449:      */
1450:     public function groupBy($fields){
1451:         if(!is_array($fields)){
1452:             $fields = $this->splitParts($fields);
1453:         }
1454:         
1455:         foreach($fields as $k => $field){
1456:             $fields[$k] = $this->db->quoteColumn($field);
1457:         }
1458:         
1459:         $this->query['group'] = implode(',', $fields);
1460:         
1461:         return $this;
1462:     }
1463:     
1464:     /**
1465:      * HAVING statement
1466:      * 
1467:      * @param string|array $conditions
1468:      * @param array $params
1469:      */
1470:     public function having($conditions, $params = array()){
1471:         $this->mergeParams($params);
1472:         $this->query['having'] = $this->buildConditions($conditions);
1473:         
1474:         return $this;
1475:     }
1476:     
1477:     /**
1478:      * UNION statement
1479:      * 
1480:      * @param string $sql
1481:      */
1482:     public function union($sql){
1483:         if(isset($this->query['union']) && is_string($this->query['union'])){
1484:             $this->query['union'][] = $this->query['union'];
1485:         }
1486:         
1487:         $this->query['union'][] = $sql;
1488:         
1489:         return $this;
1490:     }
1491:     
1492:     /**
1493:      * Build query SQL
1494:      * 
1495:      * @param array $query use $this->query if not specified
1496:      * @return string
1497:      */
1498:     public function buildQuery($query = null){
1499:         if(null === $query){
1500:             $query = $this->query;
1501:         }
1502:         
1503:         $sql = "SELECT ";
1504:         if(isset($query['distinct']) && $query['distinct']){
1505:             $sql .= 'DISTINCT ';
1506:         }
1507:         $sql .= isset($query['select'])?$query['select']:'*';
1508:         
1509:         if(!isset($query['from'])){
1510:             return false;
1511:         }
1512:         
1513:         $sql .= "\nFROM ".$query['from'];
1514:         if(isset($query['join'])){
1515:             $sql .= "\n".(is_array($query['join'])?implode("\n", $query['join']):$query['join']);
1516:         }
1517:         if(isset($query['where']) && $query['where'] !== ''){
1518:             $sql .= "\nWHERE ".$query['where'];
1519:         }
1520:         if(isset($query['group'])){
1521:             $sql .= "\nGROUP BY ".$query['group'];
1522:             if(isset($query['having'])){
1523:                 $sql .= "\nHAVING ".$query['having'];
1524:             }
1525:         }
1526:         if(isset($query['order'])){
1527:             $sql .= "\n ORDER BY ".$query['order'];
1528:         }
1529:         $limit = isset($query['limit'])?$query['limit']:0;
1530:         $offset = isset($query['offset'])?$query['offset']:0;
1531:         $sql = $this->db->buildLimitOffset($sql, $limit, $offset);
1532:         
1533:         if(isset($query['union'])){
1534:             $sql .= "\n".(is_array($query['union'])?implode("\n", $query['union']):$query['union']);
1535:         }
1536:         
1537:         return $sql;
1538:     }
1539:     
1540:     /**
1541:      * Set SQL for this command
1542:      * 
1543:      * @param string $sql
1544:      */
1545:     public function setSql($sql){
1546:         if('' !== $prefix = $this->db->getPrefix()){
1547:             $sql = preg_replace('#{{(.*?)}}#', $prefix.'\1', $sql);
1548:         }
1549:         $this->sql = $sql;
1550:         
1551:         return $this;
1552:     }
1553:     
1554:     /**
1555:      * Get SQL for this command
1556:      * 
1557:      * @return string
1558:      */
1559:     public function getSql(){
1560:         if(null === $this->sql){
1561:             if(!empty($this->query)){
1562:                 $this->setSql($this->buildQuery());
1563:             }
1564:             else{
1565:                 return false;
1566:             }
1567:         }
1568:         
1569:         return $this->sql;
1570:     }
1571:     
1572:     /**
1573:      * Prepare statement before query
1574:      * @param array $params
1575:      */
1576:     protected function beginQuery($params = array()){
1577:         $params = array_merge($this->params, $params);
1578:         $this->prepare();
1579:         if(false === $this->statement->execute($params)){
1580:             $info = $this->statement->errorInfo();
1581:             throw new TinyDBException(sprintf('Statement error #%s: %s', $info[0], $info[2]));
1582:         }
1583:     }
1584:     
1585:     /**
1586:      * Query
1587:      * Statement cursor should be closed after fetching data($statement->closeCursor)
1588:      * 
1589:      * @param array $params
1590:      * @return PDOStatement
1591:      */
1592:     public function query($params = array()){
1593:         $this->beginQuery($params);
1594:         return $this->statement;
1595:     }
1596:     
1597:     /**
1598:      * Get query result as array
1599:      * 
1600:      * @param array $params
1601:      */
1602:     public function queryAll($params = array()){
1603:         $this->beginQuery($params);
1604:         $rst = $this->statement->fetchAll(PDO::FETCH_ASSOC);
1605:         $this->statement->closeCursor();
1606:         return $rst;
1607:     }
1608:     
1609:     /**
1610:      * Get first row of result
1611:      * 
1612:      * @param array $params
1613:      */
1614:     public function queryRow($params = array()){
1615:         $this->beginQuery($params);
1616:         $rst = $this->statement->fetch(PDO::FETCH_ASSOC);
1617:         $this->statement->closeCursor();
1618:         return $rst;
1619:     }
1620:     
1621:     /**
1622:      * Get first column of result set
1623:      * 
1624:      * @param array $params
1625:      */
1626:     public function queryColumn($params = array()){
1627:         $this->beginQuery($params);
1628:         $rst = $this->statement->fetchAll(PDO::FETCH_COLUMN);
1629:         $this->statement->closeCursor();
1630:         return $rst;
1631:     }
1632:     
1633:     /**
1634:      * Get first column of first row of result set
1635:      * 
1636:      * @param array $params
1637:      */
1638:     public function queryScalar($params = array()){
1639:         $this->beginQuery($params);
1640:         $rst = $this->statement->fetchColumn();
1641:         $this->statement->closeCursor();
1642:         
1643:         return $rst;
1644:     }
1645:     
1646:     /**
1647:      * Execute statement and return rows affected
1648:      * 
1649:      * @param array $params
1650:      */
1651:     public function execute($params = array()){
1652:         $this->prepare();
1653:         if(false === $rst = $this->statement->execute($params)){
1654:             return false;
1655:         }
1656:         
1657:         return $this->statement->rowCount();
1658:     }
1659:     
1660:     /**
1661:      * Insert data
1662:      * 
1663:      * @param string $table
1664:      * @param array $values
1665:      * @return int Rows affected, false on error
1666:      */
1667:     public function insert($table, $values){
1668:         $keys = array();
1669:         $params = array();
1670:         $placeholders = array();
1671:         foreach($values as $k => $v){
1672:             $keys[] = $this->db->quoteColumn($k);
1673:             $params[':'.$k] = $v;
1674:             $placeholders[] = ':'.$k;
1675:         }
1676:         $sql = "INSERT INTO ".$this->db->quoteTable($table).' ('.implode(', ',$keys).') VALUES ('.implode(',', $placeholders).')';
1677:         return $this->setSql($sql)->execute($params);
1678:     }
1679:     
1680:     /**
1681:      * Update table
1682:      * 
1683:      * @param string $table
1684:      * @param string|array $values
1685:      * @param string|array $conditions
1686:      * @param array $params
1687:      * @return int Rows affected, false on error
1688:      */
1689:     public function update($table, $values, $conditions = '', $params = array()){
1690:         $updates = array();
1691:         if (!is_array($values)) {
1692:             $values = array($values);
1693:         }
1694:         foreach($values as $k => $value){
1695:             if (is_integer($k)) {
1696:                 $updates[] = $value;
1697:             } else {
1698:                 $updates[] = $this->db->quoteColumn($k).' = :'.$k;
1699:                 $params[':'.$k] = $value;
1700:             }
1701:         }
1702:         
1703:         $sql = "UPDATE ".$this->db->quoteTable($table).' SET '.implode(', ', $updates);
1704:         $conditions = $this->buildConditions($conditions);
1705:         if('' !== $conditions){
1706:             $sql .= ' WHERE '.$conditions;
1707:         }
1708:         
1709:         return $this->setSql($sql)->execute($params);
1710:     }
1711:     
1712:     /**
1713:      * Delete
1714:      * 
1715:      * @param string $table
1716:      * @param array $values
1717:      * @param string|array $conditions
1718:      * @param array $params
1719:      * @return int Rows affected, false on error
1720:      */
1721:     public function delete($table, $conditions = '', $params = array()){
1722:         $sql = 'DELETE FROM '.$this->db->quoteTable($table);
1723:         $conditions = $this->buildConditions($conditions);
1724:         if('' !== $conditions){
1725:             $sql .= ' WHERE '.$conditions;
1726:         }
1727:         
1728:         return $this->setSql($sql)->execute($params);
1729:     }
1730: }
1731: 
API documentation generated by ApiGen 2.8.0