File indexing completed on 2024-05-05 10:19:47

0001 <?php
0002 
0003 /**
0004  * Flooer Framework
0005  *
0006  * LICENSE: BSD License (2 Clause)
0007  *
0008  * @category    Flooer
0009  * @package     Flooer_Db
0010  * @subpackage  Table
0011  * @author      Akira Ohgaki <akiraohgaki@gmail.com>
0012  * @copyright   Akira Ohgaki
0013  * @license     https://opensource.org/licenses/BSD-2-Clause  BSD License (2 Clause)
0014  * @link        https://github.com/akiraohgaki/flooer
0015  */
0016 
0017 require_once 'Flooer/Db/Table/Row.php';
0018 require_once 'Flooer/Db/Table/Rowset.php';
0019 
0020 /**
0021  * Usage
0022  *
0023  * $table = new Flooer_Db_Table($db);
0024  * $table->setName('TableName');
0025  * $table->setPrimary('PrimaryKey');
0026  * $row = $table->RecordID;
0027  */
0028 
0029 /**
0030  * Table class of SQL database abstraction layer
0031  *
0032  * @category    Flooer
0033  * @package     Flooer_Db
0034  * @subpackage  Table
0035  * @author      Akira Ohgaki <akiraohgaki@gmail.com>
0036  */
0037 class Flooer_Db_Table
0038 {
0039 
0040     /**
0041      * Database connection object
0042      *
0043      * @var     Flooer_Db
0044      */
0045     protected $_db = null;
0046 
0047     /**
0048      * Configuration options
0049      *
0050      * @var     array
0051      */
0052     protected $_config = array(
0053         'name' => null,
0054         'prefix' => null,
0055         'columns' => '*',
0056         'primary' => 'id',
0057         'primaryInsert' => false,
0058         'insertIgnore' => false
0059     );
0060 
0061     /**
0062      * Row exists
0063      *
0064      * @var     array
0065      */
0066     protected $_rowExists = array();
0067 
0068     /**
0069      * Row object cache
0070      *
0071      * @var     array
0072      */
0073     protected $_rowCache = array();
0074 
0075     /**
0076      * Constructor
0077      *
0078      * @param   Flooer_Db &$db
0079      * @param   array $config
0080      * @return  void
0081      */
0082     public function __construct(Flooer_Db &$db, array $config = null)
0083     {
0084         $this->_db =& $db;
0085         if ($config) {
0086             $this->_config = $config + $this->_config;
0087         }
0088         if (!$this->_config['name']) {
0089             $this->_config['name'] = get_class($this);
0090         }
0091     }
0092 
0093     /**
0094      * Magic method to update/insert a row
0095      *
0096      * @param   string $key
0097      * @param   array|object $value
0098      * @return  void
0099      */
0100     public function __set($key, $value)
0101     {
0102         if (is_array($value) || is_object($value)) {
0103             // Update a row
0104             if ($this->__isset($key)) {
0105                 $this->_rowExists = array();
0106                 $this->_rowCache = array();
0107                 $columns = array();
0108                 $statementValues = array();
0109                 foreach ($value as $columnName => $columnValue) {
0110                     $columns[] = "$columnName = :$columnName";
0111                     $statementValues[":$columnName"] = $columnValue;
0112                 }
0113                 $columnsSet = implode(',', $columns);
0114                 $whereValue = $this->_db->quote($key);
0115                 $sql = "UPDATE {$this->_config['prefix']}{$this->_config['name']}"
0116                     . " SET $columnsSet"
0117                     . " WHERE {$this->_config['primary']} = $whereValue;";
0118                 $this->_db->addStatementLog($sql);
0119                 $statement = $this->_db->prepare($sql);
0120                 $statement->execute($statementValues);
0121                 $statement->closeCursor();
0122             }
0123             // Insert a new row
0124             else {
0125                 $this->_rowExists = array();
0126                 $this->_rowCache = array();
0127                 $columnNames = array();
0128                 $columnValues = array();
0129                 $statementValues = array();
0130                 if ($this->_config['primaryInsert']) {
0131                     $columnNames[] = $this->_config['primary'];
0132                     $columnValues[] = ":{$this->_config['primary']}";
0133                     $statementValues[":{$this->_config['primary']}"] = $key;
0134                 }
0135                 foreach ($value as $columnName => $columnValue) {
0136                     $columnNames[] = $columnName;
0137                     $columnValues[] = ":$columnName";
0138                     $statementValues[":$columnName"] = $columnValue;
0139                 }
0140                 $columnNamesSet = implode(',', $columnNames);
0141                 $columnValuesSet = implode(',', $columnValues);
0142                 $sql = "INSERT ";
0143                 if($this->_config['insertIgnore']) {
0144                     $sql .= " IGNORE ";
0145                 }
0146                 $sql .= " INTO {$this->_config['prefix']}{$this->_config['name']}"
0147                     . " ($columnNamesSet)"
0148                     . " VALUES ($columnValuesSet);";
0149                 $this->_db->addStatementLog($sql);
0150                 $statement = $this->_db->prepare($sql);
0151                 $statement->execute($statementValues);
0152                 if ((int)$statement->errorCode()) {
0153                     trigger_error(
0154                         "" . json_encode($statement->errorInfo()),
0155                         E_USER_ERROR
0156                     );
0157                 }
0158                 $statement->closeCursor();
0159             }
0160             return;
0161         }
0162         trigger_error(
0163             "Setting non-array or non-object property ($key) is not allowed",
0164             E_USER_NOTICE
0165         );
0166     }
0167 
0168     /**
0169      * Magic method to get a row object
0170      *
0171      * @param   string $key
0172      * @return  Flooer_Db_Table_Row|null
0173      */
0174     public function __get($key)
0175     {
0176         if ($this->__isset($key)) {
0177             if (empty($this->_rowCache[$key])) {
0178                 $whereValue = $this->_db->quote($key);
0179                 $sql = "SELECT {$this->_config['columns']}"
0180                     . " FROM {$this->_config['prefix']}{$this->_config['name']}"
0181                     . " WHERE {$this->_config['primary']} = $whereValue;";
0182                 $this->_db->addStatementLog($sql);
0183                 $statement = $this->_db->prepare($sql);
0184                 $statement->setFetchMode(
0185                     Flooer_Db::FETCH_CLASS,
0186                     'Flooer_Db_Table_Row'
0187                 );
0188                 $bool = $statement->execute();
0189                 $row = $statement->fetch();
0190                 $statement->closeCursor();
0191                 $this->_rowCache[$key] = null;
0192                 if ($bool && $row) {
0193                     $this->_rowCache[$key] = $row;
0194                 }
0195             }
0196             return $this->_rowCache[$key];
0197         }
0198         return null;
0199     }
0200 
0201     /**
0202      * Magic method to check a row
0203      *
0204      * @param   string $key
0205      * @return  bool
0206      */
0207     public function __isset($key)
0208     {
0209         if (isset($this->_rowExists[$key])) {
0210             return $this->_rowExists[$key];
0211         }
0212         $driver = $this->_db->getAttribute(Flooer_Db::ATTR_DRIVER_NAME);
0213         $whereValue = $this->_db->quote($key);
0214         $sql = "SELECT COUNT(*)"
0215             . " FROM {$this->_config['prefix']}{$this->_config['name']}"
0216             . " WHERE {$this->_config['primary']} = $whereValue;";
0217         if ($driver == 'sqlite' || $driver == 'mysql' || $driver == 'pgsql') {
0218             $sql = "SELECT 1"
0219                 . " FROM {$this->_config['prefix']}{$this->_config['name']}"
0220                 . " WHERE {$this->_config['primary']} = $whereValue"
0221                 . " LIMIT 1;";
0222         }
0223         else if ($driver == 'sqlsrv') {
0224             $sql = "SELECT TOP 1 1"
0225                 . " FROM {$this->_config['prefix']}{$this->_config['name']}"
0226                 . " WHERE {$this->_config['primary']} = $whereValue;";
0227         }
0228         $this->_db->addStatementLog($sql);
0229         $statement = $this->_db->prepare($sql);
0230         $bool = $statement->execute();
0231         $row = $statement->fetch(Flooer_Db::FETCH_NUM);
0232         $statement->closeCursor();
0233         if ($bool && $row) {
0234             if ($row[0]) {
0235                 $this->_rowExists[$key] = true;
0236                 return true;
0237             }
0238             $this->_rowExists[$key] = false;
0239         }
0240         return false;
0241     }
0242 
0243     /**
0244      * Magic method to delete a row
0245      *
0246      * @param   string $key
0247      * @return  void
0248      */
0249     public function __unset($key)
0250     {
0251         if ($this->__isset($key)) {
0252             $whereValue = $this->_db->quote($key);
0253             $sql = "DELETE FROM {$this->_config['prefix']}{$this->_config['name']}"
0254                 . " WHERE {$this->_config['primary']} = $whereValue;";
0255             $this->_db->addStatementLog($sql);
0256             $statement = $this->_db->prepare($sql);
0257             $bool = $statement->execute();
0258             $statement->closeCursor();
0259             if ($bool) {
0260                 $this->_rowExists[$key] = false;
0261                 unset($this->_rowCache[$key]);
0262             }
0263         }
0264     }
0265 
0266     /**
0267      * Fetch one row
0268      *
0269      * @param   string $statementOption
0270      * @param   array $values
0271      * @return  Flooer_Db_Table_Row|null
0272      */
0273     public function fetchRow($statementOption = '', array $values = null)
0274     {
0275         $sql = "SELECT {$this->_config['columns']}"
0276             . " FROM {$this->_config['prefix']}{$this->_config['name']}"
0277             . " $statementOption;";
0278         $this->_db->addStatementLog($sql);
0279         $statement = $this->_db->prepare($sql);
0280         $statement->setFetchMode(
0281             Flooer_Db::FETCH_CLASS,
0282             'Flooer_Db_Table_Row'
0283         );
0284         $bool = $statement->execute($values);
0285         $row = $statement->fetch();
0286         $statement->closeCursor();
0287         if ($bool && $row) {
0288             return $row;
0289         }
0290         return null;
0291     }
0292 
0293     /**
0294      * Fetch a rowset
0295      *
0296      * @param   string $statementOption
0297      * @param   array $values
0298      * @return  Flooer_Db_Table_Rowset|null
0299      */
0300     public function fetchRowset($statementOption = '', array $values = null)
0301     {
0302         $sql = "SELECT {$this->_config['columns']}"
0303             . " FROM {$this->_config['prefix']}{$this->_config['name']}"
0304             . " $statementOption;";
0305         $this->_db->addStatementLog($sql);
0306         $statement = $this->_db->prepare($sql);
0307         $statement->setFetchMode(
0308             Flooer_Db::FETCH_CLASS,
0309             'Flooer_Db_Table_Row'
0310         );
0311         $bool = $statement->execute($values);
0312         $rowsetArray = $statement->fetchAll();
0313         $statement->closeCursor();
0314         if ($bool && $rowsetArray) {
0315             $rowset = new Flooer_Db_Table_Rowset();
0316             foreach ($rowsetArray as $key => $value) {
0317                 $rowset->$key = $value;
0318             }
0319             return $rowset;
0320         }
0321         return null;
0322     }
0323 
0324     /**
0325      * Count a record size
0326      *
0327      * @param   string $statementOption
0328      * @param   array $values
0329      * @return  int|bool
0330      */
0331     public function count($statementOption = '', array $values = null)
0332     {
0333         $sql = "SELECT COUNT(*)"
0334             . " FROM {$this->_config['prefix']}{$this->_config['name']}"
0335             . " $statementOption;";
0336         $this->_db->addStatementLog($sql);
0337         $statement = $this->_db->prepare($sql);
0338         $bool = $statement->execute($values);
0339         $row = $statement->fetch(Flooer_Db::FETCH_NUM);
0340         $statement->closeCursor();
0341         if ($bool && $row) {
0342             return $row[0];
0343         }
0344         return false;
0345     }
0346 
0347     /**
0348      * Get a database connection object
0349      *
0350      * @return  Flooer_Db
0351      */
0352     public function getDb()
0353     {
0354         return $this->_db;
0355     }
0356 
0357     /**
0358      * Set a table name
0359      *
0360      * @param   string $name
0361      * @return  void
0362      */
0363     public function setName($name)
0364     {
0365         $this->_config['name'] = $name;
0366         $this->_rowExists = array();
0367         $this->_rowCache = array();
0368     }
0369 
0370     /**
0371      * Get a table name
0372      *
0373      * @return  string
0374      */
0375     public function getName()
0376     {
0377         return $this->_config['name'];
0378     }
0379 
0380     /**
0381      * Set a table name prefix
0382      *
0383      * @param   string $prefix
0384      * @return  void
0385      */
0386     public function setPrefix($prefix)
0387     {
0388         $this->_config['prefix'] = $prefix;
0389         $this->_rowExists = array();
0390         $this->_rowCache = array();
0391     }
0392 
0393     /**
0394      * Get a table name prefix
0395      *
0396      * @return  string
0397      */
0398     public function getPrefix()
0399     {
0400         return $this->_config['prefix'];
0401     }
0402 
0403     /**
0404      * Set a table columns
0405      *
0406      * @param   string $columns
0407      * @return  void
0408      */
0409     public function setColumns($columns)
0410     {
0411         $this->_config['columns'] = $columns;
0412         $this->_rowCache = array();
0413     }
0414 
0415     /**
0416      * Get a table columns
0417      *
0418      * @return  string
0419      */
0420     public function getColumns()
0421     {
0422         return $this->_config['columns'];
0423     }
0424 
0425     /**
0426      * Set a primary key name
0427      *
0428      * @param   string $primary
0429      * @return  void
0430      */
0431     public function setPrimary($primary)
0432     {
0433         $this->_config['primary'] = $primary;
0434         $this->_rowExists = array();
0435         $this->_rowCache = array();
0436     }
0437 
0438     /**
0439      * Get a primary key name
0440      *
0441      * @return  string
0442      */
0443     public function getPrimary()
0444     {
0445         return $this->_config['primary'];
0446     }
0447 
0448     /**
0449      * Set an option for a primary key inserting
0450      *
0451      * @param   bool $bool
0452      * @return  void
0453      */
0454     public function setPrimaryInsert($bool)
0455     {
0456         $this->_config['primaryInsert'] = $bool;
0457     }
0458 
0459     /**
0460      * Get an option for a primary key inserting
0461      *
0462      * @return  bool
0463      */
0464     public function getPrimaryInsert()
0465     {
0466         return $this->_config['primaryInsert'];
0467     }
0468     
0469     /**
0470      * Set an option for a insert irgnore
0471      *
0472      * @param   bool $bool
0473      * @return  void
0474      */
0475     public function setInsertIgnore($bool)
0476     {
0477         $this->_config['insertIgnore'] = $bool;
0478     }
0479 
0480     /**
0481      * Get an option for a primary key inserting
0482      *
0483      * @return  bool
0484      */
0485     public function getInsertIgnore()
0486     {
0487         return $this->_config['insertIgnore'];
0488     }
0489 
0490 }