Subversion Repositories Applications.papyrus

Rev

Rev 1087 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
320 jpm 1
<?php
2
 
3
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
 
5
/**
6
 * Provides an object interface to a table row
7
 *
8
 * PHP versions 4 and 5
9
 *
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.
15
 *
16
 * @category   Database
17
 * @package    DB
18
 * @author     Stig Bakken <stig@php.net>
19
 * @copyright  1997-2005 The PHP Group
20
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
443 ddelon 21
 * @version    CVS: $Id: storage.php,v 1.2 2005-09-20 17:01:22 ddelon Exp $
320 jpm 22
 * @link       http://pear.php.net/package/DB
23
 */
24
 
25
/**
26
 * Obtain the DB class so it can be extended from
27
 */
28
require_once 'DB.php';
29
 
30
/**
31
 * Provides an object interface to a table row
32
 *
33
 * It lets you add, delete and change rows using objects rather than SQL
34
 * statements.
35
 *
36
 * @category   Database
37
 * @package    DB
38
 * @author     Stig Bakken <stig@php.net>
39
 * @copyright  1997-2005 The PHP Group
40
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
443 ddelon 41
 * @version    Release: @package_version@
320 jpm 42
 * @link       http://pear.php.net/package/DB
43
 */
44
class DB_storage extends PEAR
45
{
46
    // {{{ properties
47
 
48
    /** the name of the table (or view, if the backend database supports
49
        updates in views) we hold data from */
50
    var $_table = null;
51
 
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;
56
 
57
    /** DB connection handle used for all transactions */
58
    var $_dbh = null;
59
 
60
    /** an assoc with the names of database fields stored as properties
61
        in this object */
62
    var $_properties = array();
63
 
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();
67
 
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;
72
 
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;
77
 
78
    // }}}
79
    // {{{ constructor
80
 
81
    /**
82
     * Constructor
83
     *
84
     * @param $table string the name of the database table
85
     *
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
88
     *
89
     * @param $dbh object database connection object
90
     *
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
95
     *
96
     */
97
    function DB_storage($table, $keycolumn, &$dbh, $validator = null)
98
    {
99
        $this->PEAR('DB_Error');
100
        $this->_table = $table;
101
        $this->_keycolumn = $keycolumn;
102
        $this->_dbh = $dbh;
103
        $this->_readonly = false;
104
        $this->_validator = $validator;
105
    }
106
 
107
    // }}}
108
    // {{{ _makeWhere()
109
 
110
    /**
111
     * Utility method to build a "WHERE" clause to locate ourselves in
112
     * the table.
113
     *
114
     * XXX future improvement: use rowids?
115
     *
116
     * @access private
117
     */
118
    function _makeWhere($keyval = null)
119
    {
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]};
124
                }
125
            }
126
            $whereclause = '';
127
            for ($i = 0; $i < sizeof($this->_keycolumn); $i++) {
128
                if ($i > 0) {
129
                    $whereclause .= ' AND ';
130
                }
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';
136
                } else {
137
                    $whereclause .= ' = ' . $this->_dbh->quote($keyval[$i]);
138
                }
139
            }
140
        } else {
141
            if ($keyval === null) {
142
                $keyval = @$this->{$this->_keycolumn};
143
            }
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';
149
            } else {
150
                $whereclause .= ' = ' . $this->_dbh->quote($keyval);
151
            }
152
        }
153
        return $whereclause;
154
    }
155
 
156
    // }}}
157
    // {{{ setup()
158
 
159
    /**
160
     * Method used to initialize a DB_storage object from the
161
     * configured table.
162
     *
163
     * @param $keyval mixed the key[s] of the row to fetch (string or array)
164
     *
165
     * @return int DB_OK on success, a DB error if not
166
     */
167
    function setup($keyval)
168
    {
169
        $whereclause = $this->_makeWhere($keyval);
170
        $query = 'SELECT * FROM ' . $this->_table . ' WHERE ' . $whereclause;
171
        $sth = $this->_dbh->query($query);
172
        if (DB::isError($sth)) {
173
            return $sth;
174
        }
175
        $row = $sth->fetchRow(DB_FETCHMODE_ASSOC);
176
        if (DB::isError($row)) {
177
            return $row;
178
        }
179
        if (!$row) {
180
            return $this->raiseError(null, DB_ERROR_NOT_FOUND, null, null,
181
                                     $query, null, true);
182
        }
183
        foreach ($row as $key => $value) {
184
            $this->_properties[$key] = true;
185
            $this->$key = $value;
186
        }
187
        return DB_OK;
188
    }
189
 
190
    // }}}
191
    // {{{ insert()
192
 
193
    /**
194
     * Create a new (empty) row in the configured table for this
195
     * object.
196
     */
197
    function insert($newpk)
198
    {
199
        if (is_array($this->_keycolumn)) {
200
            $primarykey = $this->_keycolumn;
201
        } else {
202
            $primarykey = array($this->_keycolumn);
203
        }
204
        settype($newpk, "array");
205
        for ($i = 0; $i < sizeof($primarykey); $i++) {
206
            $pkvals[] = $this->_dbh->quote($newpk[$i]);
207
        }
208
 
209
        $sth = $this->_dbh->query("INSERT INTO $this->_table (" .
210
                                  implode(",", $primarykey) . ") VALUES(" .
211
                                  implode(",", $pkvals) . ")");
212
        if (DB::isError($sth)) {
213
            return $sth;
214
        }
215
        if (sizeof($newpk) == 1) {
216
            $newpk = $newpk[0];
217
        }
218
        $this->setup($newpk);
219
    }
220
 
221
    // }}}
222
    // {{{ toString()
223
 
224
    /**
225
     * Output a simple description of this DB_storage object.
226
     * @return string object description
227
     */
228
    function toString()
229
    {
230
        $info = strtolower(get_class($this));
231
        $info .= " (table=";
232
        $info .= $this->_table;
233
        $info .= ", keycolumn=";
234
        if (is_array($this->_keycolumn)) {
235
            $info .= "(" . implode(",", $this->_keycolumn) . ")";
236
        } else {
237
            $info .= $this->_keycolumn;
238
        }
239
        $info .= ", dbh=";
240
        if (is_object($this->_dbh)) {
241
            $info .= $this->_dbh->toString();
242
        } else {
243
            $info .= "null";
244
        }
245
        $info .= ")";
246
        if (sizeof($this->_properties)) {
247
            $info .= " [loaded, key=";
248
            $keyname = $this->_keycolumn;
249
            if (is_array($keyname)) {
250
                $info .= "(";
251
                for ($i = 0; $i < sizeof($keyname); $i++) {
252
                    if ($i > 0) {
253
                        $info .= ",";
254
                    }
255
                    $info .= $this->$keyname[$i];
256
                }
257
                $info .= ")";
258
            } else {
259
                $info .= $this->$keyname;
260
            }
261
            $info .= "]";
262
        }
263
        if (sizeof($this->_changes)) {
264
            $info .= " [modified]";
265
        }
266
        return $info;
267
    }
268
 
269
    // }}}
270
    // {{{ dump()
271
 
272
    /**
273
     * Dump the contents of this object to "standard output".
274
     */
275
    function dump()
276
    {
277
        foreach ($this->_properties as $prop => $foo) {
278
            print "$prop = ";
279
            print htmlentities($this->$prop);
280
            print "<br />\n";
281
        }
282
    }
283
 
284
    // }}}
285
    // {{{ &create()
286
 
287
    /**
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
292
     */
293
    function &create($table, &$data)
294
    {
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;
300
        }
301
        return $obj;
302
    }
303
 
304
    // }}}
305
    // {{{ loadFromQuery()
306
 
307
    /**
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.
311
     *
312
     * @param $query SQL query
313
     *
314
     * @param $params parameter list in case you want to use
315
     * prepare/execute mode
316
     *
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.
321
     */
322
// XXX commented out for now
323
/*
324
    function loadFromQuery($query, $params = null)
325
    {
326
        if (sizeof($this->_properties)) {
327
            if (sizeof($this->_changes)) {
328
                $this->store();
329
                $this->_changes = array();
330
            }
331
            $this->_properties = array();
332
        }
333
        $rowdata = $this->_dbh->getRow($query, DB_FETCHMODE_ASSOC, $params);
334
        if (DB::isError($rowdata)) {
335
            return $rowdata;
336
        }
337
        reset($rowdata);
338
        $found_keycolumn = false;
339
        while (list($key, $value) = each($rowdata)) {
340
            if ($key == $this->_keycolumn) {
341
                $found_keycolumn = true;
342
            }
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
347
        }
348
        if (!$found_keycolumn) {
349
            $this->_readonly = true;
350
            return DB_WARNING_READ_ONLY;
351
        }
352
        return DB_OK;
353
    }
354
 */
355
 
356
    // }}}
357
    // {{{ set()
358
 
359
    /**
360
     * Modify an attriute value.
361
     */
362
    function set($property, $newvalue)
363
    {
364
        // only change if $property is known and object is not
365
        // read-only
366
        if ($this->_readonly) {
367
            return $this->raiseError(null, DB_WARNING_READ_ONLY, null,
368
                                     null, null, null, true);
369
        }
370
        if (@isset($this->_properties[$property])) {
371
            if (empty($this->_validator)) {
372
                $valid = true;
373
            } else {
374
                $valid = @call_user_func($this->_validator,
375
                                         $this->_table,
376
                                         $property,
377
                                         $newvalue,
378
                                         $this->$property,
379
                                         $this);
380
            }
381
            if ($valid) {
382
                $this->$property = $newvalue;
383
                if (empty($this->_changes[$property])) {
384
                    $this->_changes[$property] = 0;
385
                } else {
386
                    $this->_changes[$property]++;
387
                }
388
            } else {
389
                return $this->raiseError(null, DB_ERROR_INVALID, null,
390
                                         null, "invalid field: $property",
391
                                         null, true);
392
            }
393
            return true;
394
        }
395
        return $this->raiseError(null, DB_ERROR_NOSUCHFIELD, null,
396
                                 null, "unknown field: $property",
397
                                 null, true);
398
    }
399
 
400
    // }}}
401
    // {{{ &get()
402
 
403
    /**
404
     * Fetch an attribute value.
405
     *
406
     * @param string attribute name
407
     *
408
     * @return attribute contents, or null if the attribute name is
409
     * unknown
410
     */
411
    function &get($property)
412
    {
413
        // only return if $property is known
414
        if (isset($this->_properties[$property])) {
415
            return $this->$property;
416
        }
417
        $tmp = null;
418
        return $tmp;
419
    }
420
 
421
    // }}}
422
    // {{{ _DB_storage()
423
 
424
    /**
425
     * Destructor, calls DB_storage::store() if there are changes
426
     * that are to be kept.
427
     */
428
    function _DB_storage()
429
    {
430
        if (sizeof($this->_changes)) {
431
            $this->store();
432
        }
433
        $this->_properties = array();
434
        $this->_changes = array();
435
        $this->_table = null;
436
    }
437
 
438
    // }}}
439
    // {{{ store()
440
 
441
    /**
442
     * Stores changes to this object in the database.
443
     *
444
     * @return DB_OK or a DB error
445
     */
446
    function store()
447
    {
448
        foreach ($this->_changes as $name => $foo) {
449
            $params[] = &$this->$name;
450
            $vars[] = $name . ' = ?';
451
        }
452
        if ($vars) {
453
            $query = 'UPDATE ' . $this->_table . ' SET ' .
454
                implode(', ', $vars) . ' WHERE ' .
455
                $this->_makeWhere();
456
            $stmt = $this->_dbh->prepare($query);
457
            $res = $this->_dbh->execute($stmt, $params);
458
            if (DB::isError($res)) {
459
                return $res;
460
            }
461
            $this->_changes = array();
462
        }
463
        return DB_OK;
464
    }
465
 
466
    // }}}
467
    // {{{ remove()
468
 
469
    /**
470
     * Remove the row represented by this object from the database.
471
     *
472
     * @return mixed DB_OK or a DB error
473
     */
474
    function remove()
475
    {
476
        if ($this->_readonly) {
477
            return $this->raiseError(null, DB_WARNING_READ_ONLY, null,
478
                                     null, null, null, true);
479
        }
480
        $query = 'DELETE FROM ' . $this->_table .' WHERE '.
481
            $this->_makeWhere();
482
        $res = $this->_dbh->query($query);
483
        if (DB::isError($res)) {
484
            return $res;
485
        }
486
        foreach ($this->_properties as $prop => $foo) {
487
            unset($this->$prop);
488
        }
489
        $this->_properties = array();
490
        $this->_changes = array();
491
        return DB_OK;
492
    }
493
 
494
    // }}}
495
}
496
 
497
/*
498
 * Local variables:
499
 * tab-width: 4
500
 * c-basic-offset: 4
501
 * End:
502
 */
503
 
504
?>