File indexing completed on 2024-11-24 05:18:58
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 }