3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
6 * Provides an object interface to a table row
10 * LICENSE: This source file is subject to version 3.0 of the PHP license
11 * that is available through the world-wide-web at the following URI:
12 * http://www.php.net/license/3_0.txt. If you did not receive a copy of
13 * the PHP License and are unable to obtain it through the web, please
14 * send a note to license@php.net so we can mail you a copy immediately.
18 * @author Stig Bakken <stig@php.net>
19 * @copyright 1997-2007 The PHP Group
20 * @license http://www.php.net/license/3_0.txt PHP License 3.0
21 * @version CVS: $Id: storage.php,v 1.24 2007/08/12 05:27:25 aharvey Exp $
22 * @link http://pear.php.net/package/DB
26 * Obtain the DB class so it can be extended from
28 require_once 'DB.php';
31 * Provides an object interface to a table row
33 * It lets you add, delete and change rows using objects rather than SQL
38 * @author Stig Bakken <stig@php.net>
39 * @copyright 1997-2007 The PHP Group
40 * @license http://www.php.net/license/3_0.txt PHP License 3.0
41 * @version Release: 1.7.14RC1
42 * @link http://pear.php.net/package/DB
44 class DB_storage extends PEAR
48 /** the name of the table (or view, if the backend database supports
49 updates in views) we hold data from */
52 /** which column(s) in the table contains primary keys, can be a
53 string for single-column primary keys, or an array of strings
54 for multiple-column primary keys */
55 var $_keycolumn = null;
57 /** DB connection handle used for all transactions */
60 /** an assoc with the names of database fields stored as properties
62 var $_properties = array();
64 /** an assoc with the names of the properties in this object that
65 have been changed since they were fetched from the database */
66 var $_changes = array();
68 /** flag that decides if data in this object can be changed.
69 objects that don't have their table's key column in their
70 property lists will be flagged as read-only. */
71 var $_readonly = false;
73 /** function or method that implements a validator for fields that
74 are set, this validator function returns true if the field is
75 valid, false if not */
76 var $_validator = null;
84 * @param $table string the name of the database table
86 * @param $keycolumn mixed string with name of key column, or array of
87 * strings if the table has a primary key of more than one column
89 * @param $dbh object database connection object
91 * @param $validator mixed function or method used to validate
92 * each new value, called with three parameters: the name of the
93 * field/column that is changing, a reference to the new value and
94 * a reference to this object
97 function DB_storage($table, $keycolumn, &$dbh, $validator = null)
99 $this->PEAR('DB_Error');
100 $this->_table = $table;
101 $this->_keycolumn = $keycolumn;
103 $this->_readonly = false;
104 $this->_validator = $validator;
111 * Utility method to build a "WHERE" clause to locate ourselves in
114 * XXX future improvement: use rowids?
118 function _makeWhere($keyval = null)
120 if (is_array($this->_keycolumn)) {
121 if ($keyval === null) {
122 for ($i = 0; $i < sizeof($this->_keycolumn); $i++) {
123 $keyval[] = $this->{$this->_keycolumn[$i]};
127 for ($i = 0; $i < sizeof($this->_keycolumn); $i++) {
129 $whereclause .= ' AND ';
131 $whereclause .= $this->_keycolumn[$i];
132 if (is_null($keyval[$i])) {
133 // there's not much point in having a NULL key,
134 // but we support it anyway
135 $whereclause .= ' IS NULL';
137 $whereclause .= ' = ' . $this->_dbh->quote($keyval[$i]);
141 if ($keyval === null) {
142 $keyval = @$this->{$this->_keycolumn};
144 $whereclause = $this->_keycolumn;
145 if (is_null($keyval)) {
146 // there's not much point in having a NULL key,
147 // but we support it anyway
148 $whereclause .= ' IS NULL';
150 $whereclause .= ' = ' . $this->_dbh->quote($keyval);
160 * Method used to initialize a DB_storage object from the
163 * @param $keyval mixed the key[s] of the row to fetch (string or array)
165 * @return int DB_OK on success, a DB error if not
167 function setup($keyval)
169 $whereclause = $this->_makeWhere($keyval);
170 $query = 'SELECT * FROM ' . $this->_table . ' WHERE ' . $whereclause;
171 $sth = $this->_dbh->query($query);
172 if (DB::isError($sth)) {
175 $row = $sth->fetchRow(DB_FETCHMODE_ASSOC);
176 if (DB::isError($row)) {
180 return $this->raiseError(null, DB_ERROR_NOT_FOUND, null, null,
183 foreach ($row as $key => $value) {
184 $this->_properties[$key] = true;
185 $this->$key = $value;
194 * Create a new (empty) row in the configured table for this
197 function insert($newpk)
199 if (is_array($this->_keycolumn)) {
200 $primarykey = $this->_keycolumn;
202 $primarykey = array($this->_keycolumn);
204 settype($newpk, "array");
205 for ($i = 0; $i < sizeof($primarykey); $i++) {
206 $pkvals[] = $this->_dbh->quote($newpk[$i]);
209 $sth = $this->_dbh->query("INSERT INTO $this->_table (" .
210 implode(",", $primarykey) . ") VALUES(" .
211 implode(",", $pkvals) . ")");
212 if (DB::isError($sth)) {
215 if (sizeof($newpk) == 1) {
218 $this->setup($newpk);
225 * Output a simple description of this DB_storage object.
226 * @return string object description
230 $info = strtolower(get_class($this));
232 $info .= $this->_table;
233 $info .= ", keycolumn=";
234 if (is_array($this->_keycolumn)) {
235 $info .= "(" . implode(",", $this->_keycolumn) . ")";
237 $info .= $this->_keycolumn;
240 if (is_object($this->_dbh)) {
241 $info .= $this->_dbh->toString();
246 if (sizeof($this->_properties)) {
247 $info .= " [loaded, key=";
248 $keyname = $this->_keycolumn;
249 if (is_array($keyname)) {
251 for ($i = 0; $i < sizeof($keyname); $i++) {
255 $info .= $this->$keyname[$i];
259 $info .= $this->$keyname;
263 if (sizeof($this->_changes)) {
264 $info .= " [modified]";
273 * Dump the contents of this object to "standard output".
277 foreach ($this->_properties as $prop => $foo) {
279 print htmlentities($this->$prop);
288 * Static method used to create new DB storage objects.
289 * @param $data assoc. array where the keys are the names
290 * of properties/columns
291 * @return object a new instance of DB_storage or a subclass of it
293 function &create($table, &$data)
295 $classname = strtolower(get_class($this));
296 $obj = new $classname($table);
297 foreach ($data as $name => $value) {
298 $obj->_properties[$name] = true;
299 $obj->$name = &$value;
305 // {{{ loadFromQuery()
308 * Loads data into this object from the given query. If this
309 * object already contains table data, changes will be saved and
310 * the object re-initialized first.
312 * @param $query SQL query
314 * @param $params parameter list in case you want to use
315 * prepare/execute mode
317 * @return int DB_OK on success, DB_WARNING_READ_ONLY if the
318 * returned object is read-only (because the object's specified
319 * key column was not found among the columns returned by $query),
320 * or another DB error code in case of errors.
322 // XXX commented out for now
324 function loadFromQuery($query, $params = null)
326 if (sizeof($this->_properties)) {
327 if (sizeof($this->_changes)) {
329 $this->_changes = array();
331 $this->_properties = array();
333 $rowdata = $this->_dbh->getRow($query, DB_FETCHMODE_ASSOC, $params);
334 if (DB::isError($rowdata)) {
338 $found_keycolumn = false;
339 while (list($key, $value) = each($rowdata)) {
340 if ($key == $this->_keycolumn) {
341 $found_keycolumn = true;
343 $this->_properties[$key] = true;
344 $this->$key = &$value;
345 unset($value); // have to unset, or all properties will
346 // refer to the same value
348 if (!$found_keycolumn) {
349 $this->_readonly = true;
350 return DB_WARNING_READ_ONLY;
360 * Modify an attriute value.
362 function set($property, $newvalue)
364 // only change if $property is known and object is not
366 if ($this->_readonly) {
367 return $this->raiseError(null, DB_WARNING_READ_ONLY, null,
368 null, null, null, true);
370 if (@isset($this->_properties[$property])) {
371 if (empty($this->_validator)) {
374 $valid = @call_user_func($this->_validator,
382 $this->$property = $newvalue;
383 if (empty($this->_changes[$property])) {
384 $this->_changes[$property] = 0;
386 $this->_changes[$property]++;
389 return $this->raiseError(null, DB_ERROR_INVALID, null,
390 null, "invalid field: $property",
395 return $this->raiseError(null, DB_ERROR_NOSUCHFIELD, null,
396 null, "unknown field: $property",
404 * Fetch an attribute value.
406 * @param string attribute name
408 * @return attribute contents, or null if the attribute name is
411 function &get($property)
413 // only return if $property is known
414 if (isset($this->_properties[$property])) {
415 return $this->$property;
425 * Destructor, calls DB_storage::store() if there are changes
426 * that are to be kept.
428 function _DB_storage()
430 if (sizeof($this->_changes)) {
433 $this->_properties = array();
434 $this->_changes = array();
435 $this->_table = null;
442 * Stores changes to this object in the database.
444 * @return DB_OK or a DB error
450 foreach ($this->_changes as $name => $foo) {
451 $params[] = &$this->$name;
452 $vars[] = $name . ' = ?';
455 $query = 'UPDATE ' . $this->_table . ' SET ' .
456 implode(', ', $vars) . ' WHERE ' .
458 $stmt = $this->_dbh->prepare($query);
459 $res = $this->_dbh->execute($stmt, $params);
460 if (DB::isError($res)) {
463 $this->_changes = array();
472 * Remove the row represented by this object from the database.
474 * @return mixed DB_OK or a DB error
478 if ($this->_readonly) {
479 return $this->raiseError(null, DB_WARNING_READ_ONLY, null,
480 null, null, null, true);
482 $query = 'DELETE FROM ' . $this->_table .' WHERE '.
484 $res = $this->_dbh->query($query);
485 if (DB::isError($res)) {
488 foreach ($this->_properties as $prop => $foo) {
491 $this->_properties = array();
492 $this->_changes = array();