diff --git a/lib/db.php b/lib/db.php index c7af506..97f5427 100644 --- a/lib/db.php +++ b/lib/db.php @@ -5,35 +5,35 @@ See PDO docs for details: http://www.php.net/manual/en/class.pdo.php Adds a few extensions for common batched operations. - + See related `extra/tagged-sql.php` for extra post-processing magic. */ class db extends PDO { - private $statement; - const USR_DEF_DB_ERR = '45000'; + private $statement; + const USR_DEF_DB_ERR = '45000'; const DB_NO_ERR = '00000'; /* Create (or reuse) an instance of a PDO database */ static function _instance($dsn, $user, $password, $config = null) { global $_db; if ($_db !== null) return $_db; // return cached - + if ($config === null) $config = array( PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8' ); - + try { - $_db = new db($dsn, $user, $password, $config); + $_db = new db($dsn, $user, $password, $config); } catch (Exception $e) { throw new Exception("Failed to connect to database.", 500, $e); } - + return $_db; } - + /* Returns an array of classes based on the given SQL and bound parameters (see PDO docs for details) */ - function select($sql, $bound_parameters = array()) { + function select($sql, $bound_parameters = array()) { $this->statement = $this->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY)); $this->statement->execute($bound_parameters); $resultset = $this->statement->fetchAll(PDO::FETCH_CLASS); @@ -47,53 +47,101 @@ function select_row($sql, $bound_parameters = array()) { elseif ($c !== 1) throw new Exception("Too many rows (".(count($r)).") returned from '$sql'", 500); return $r[0]; } - + + function select_objects($sql, $bound_parameters = array()) { + + $rows = $this->select($sql, $bound_parameters); + + $d = '.'; $t = ':'; // delimiters for depth and type + $o = array(); // output + + foreach ($rows as $r) { // each row in result set + + $row = array(); + $type = ''; + + foreach ($r as $key => $value) { // each column + + if (strpos($key, $t)) { + // extract type + $p = explode($t, $key); + $type = $p[1]; + $key = $p[0]; + } else { + $type = null; // no type provided + } + + // extract keys + $keys = strpos($key, $d) ? explode($d, $key) : array($key); + + $ptr = &$row; // working pointer + + // create objects as needed + foreach ($keys as $k) { + if (!isset($ptr[$k])) $ptr[$k] = array(); + $ptr = &$ptr[$k]; + } + + // adjust type + if (!empty($type)) settype($value, $type); + + // add column + if (empty($ptr)) $ptr = $value; + else $ptr[] = $value; + } + + $o[] = db::array_to_object($row); // add row to output as objects + } + + return $o; + } + /* Provide a wrapper for updates which is really just an alias for the query function. */ function update($sql, &$bound_parameters = array()) { $this->query($sql, $bound_parameters); if ($this->statement->rowCount() === 0) throw new Exception('Update failed: resource was not found.', 404); } - /* + /* General query. Use this when no specific results are expected. Example: attempting to delete a resource that may or may exist. - - $bound_parameters is an array of arrays with the 'value' member as the value to bind and the 'pdoType' as + + $bound_parameters is an array of arrays with the 'value' member as the value to bind and the 'pdoType' as that parameter's PDO datatype. */ function query($sql, &$bound_parameters = array()) { $this->statement = $this->prepare($sql); - if (!empty($bound_parameters)) { + if (!empty($bound_parameters)) { foreach ($bound_parameters as $key => &$param) { $v = $param['value']; $t = $param['pdoType']; if (!$this->statement->bindValue($key, $v, $t)) throw new Exception("Unable to bind '$v' to named parameter ':$key'.", 500); } } - + $this->statement->execute(); - $this->errors(); + $this->errors(); } - + /* Provide a wrapper for inserts which is really just an alias for the query function. */ function insert($sql, &$bound_parameters = array()) { $this->query($sql, $bound_parameters); if ($this->statement->rowCount() === 0) throw new Exception('Insert failed: no rows were inserted.', 500); } - + /* Provide a wrapper for deletes which is really just an alias for the query function. */ function delete($sql, &$bound_parameters = array()) { $this->query($sql, $bound_parameters); if ($this->statement->rowCount() === 0) throw new Exception('Delete failed: resource does not exist.', 404); } - + /* Return the number of rows affected by the last INSERT, DELETE, or UPDATE. */ function affected_rows() { return $this->statement->rowCount(); } - + /* Throw error info pertaining to the last operation. */ private function errors() { $e = $this->statement->errorInfo(); @@ -107,4 +155,7 @@ private function errors() { } else throw new Exception('Update failed for unknown reason.', 500); } -} \ No newline at end of file + + // convert an array to an object (recursively) + public static function array_to_object($o) { return is_array($o) ? (object) array_map(__METHOD__, $o) : $o; } +}