Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
 
3
// +----------------------------------------------------------------------+
4
// | PHP Version 4                                                        |
5
// +----------------------------------------------------------------------+
6
// | Copyright (c) 1997-2003 The PHP Group                                |
7
// +----------------------------------------------------------------------+
8
// | This source file is subject to version 2.02 of the PHP license,      |
9
// | that is bundled with this package in the file LICENSE, and is        |
10
// | available at through the world-wide-web at                           |
11
// | http://www.php.net/license/2_02.txt.                                 |
12
// | If you did not receive a copy of the PHP license and are unable to   |
13
// | obtain it through the world-wide-web, please send a note to          |
14
// | license@php.net so we can mail you a copy immediately.               |
15
// +----------------------------------------------------------------------+
16
// | Authors: Wolfram Kriesing <wolfram@kriesing.de>                      |
17
// |          Lorenzo Alberton <l.alberton@quipo.it>                      |
18
// +----------------------------------------------------------------------+
19
 
20
// $Id: MDBsimple.php,v 1.2.2.2 2009/03/12 17:19:48 dufuz Exp $
21
require_once 'Tree/OptionsMDB.php';
22
require_once 'Tree/Error.php';
23
 
24
/**
25
* the MDB interface to the tree class
26
*
27
* @access public
28
* @author Lorenzo Alberton <l.alberton@quipo.it>
29
* @package Tree
30
*/
31
class Tree_Memory_MDBsimple extends Tree_OptionsMDB
32
{
33
    // FIXXME should actually extend Tree_Common, to use the methods provided in there...
34
    // but we need to connect to the db here, so we extend optionsMDB for now, may be use
35
    // "aggregate" function to fix that
36
 
37
    /**
38
     * @access public
39
     * @var array saves the options passed to the constructor. Valid options:
40
     *      - order: which column to order by when reading the data from the DB,
41
     *               this sorts the data even inside every level
42
     *      - whereAddOn: add on for the where clause, this string is simply
43
     *                    added behind the WHERE in the select, so you better make
44
     *                    sure its correct SQL :-), i.e. 'uid=3'.
45
     *                    This is needed i.e. when you are saving many trees for different
46
     *                    user in one table where each entry has a uid (user id)
47
     *      - columnNameMaps: the column-name maps are used for the "as" in the select
48
     *                        queries so you can use any column name in the table and "map"
49
     *                        it to the name that shall be used in the internal array,
50
     *                        that is built, see the examples (in comments)
51
     *      - columnNameMaps: array for id/parentId/prevId/name table column names
52
     */
53
    var $options = array('order' => '',
54
                         'whereAddOn' => '',
55
                         'table' => '',
56
                         'columnNameMaps' => array(
57
                                   /* 'id'       => 'tree_id', // use "tree_id" as "id"
58
                                      'parentId' => 'parent_id',
59
                                      'prevId'   => 'previous_id',
60
                                      'name'     => 'nodeName'
61
                                   */
62
                        ),
63
                    );
64
 
65
    /**
66
     * @access public
67
     * @var string the table where to read the tree data from
68
     *             can also be set using the DSN in the constructor
69
     */
70
    var $table;
71
 
72
    /**
73
     * @access private
74
     * @var object $dbh    the handle to the DB-object
75
     */
76
    // var $dbh;
77
 
78
    // {{{ Tree_Memory_MDBsimple()
79
 
80
    /**
81
     * set up this object
82
     *
83
     * @access public
84
     * @param string $dsn this is a DSN of the for that PEAR::DB uses it
85
     *                    only that additionally you can add parameters like ...?table=test_table
86
     *                    to define the table it shall work on
87
     * @param array $options additional options you can set
88
     */
89
    function Tree_Memory_MDBsimple($dsn, $options = array())
90
    {
91
        $this->Tree_OptionsMDB($dsn, $options); // instanciate DB
92
        if (is_string($options)) {
93
            // just to be backward compatible, or to make the second paramter shorter
94
            $this->setOption('order', $options);
95
        }
96
 
97
        $this->table = $this->getOption('table');
98
    }
99
 
100
    // }}}
101
    // {{{ setup()
102
 
103
    /**
104
     * retrieve all the navigation data from the db and call build to build the
105
     * tree in the array data and structure
106
     *
107
     * @access public
108
     * @return boolean true on success
109
     */
110
    function setup()
111
    {
112
        // TODO sort by prevId (parentId, prevId $addQuery) too if it exists in the table,
113
        //      or the root might be wrong, since the prevId of the root should be 0
114
 
115
        $whereAddOn = '';
116
        if ($this->options['whereAddOn']) {
117
            $whereAddOn = 'WHERE ' . $this->getOption('whereAddOn');
118
        }
119
 
120
        $orderBy = '';
121
        if ($this->options['order']) {
122
            $orderBy = ',' . $this->options['order'];
123
        }
124
 
125
        $map = $this->getOption('columnNameMaps');
126
        if (isset($map['parentId'])) {
127
            $orderBy = $map['parentId'] . $orderBy;
128
        } else {
129
            $orderBy = 'parentId' . $orderBy;
130
        }
131
        // build the query this way, that the root, which has no parent (parentId=0)
132
        // and no previous (prevId=0) is in first place (in case prevId is given)
133
        $query = sprintf("SELECT * FROM %s %s ORDER BY %s",
134
                         $this->table,
135
                         $whereAddOn,
136
                         $orderBy); //,prevId !!!!
137
        if (MDB::isError($res = $this->dbh->getAll($query))) {
138
            // FIXXME remove print use debug mode instead
139
            printf("ERROR - Tree::setup - %s - %s<br />", MDB::errorMessage($res), $query);
140
            return $this->_throwError($res->getMessage(), __LINE__);
141
        }
142
        // if the db-column names need to be mapped to different names
143
        // FIXXME somehow we should be able to do this in the query, but I dont know
144
        // how to select only those columns, use "as" on them and select the rest,
145
        //without getting those columns again :-(
146
        if ($map) {
147
            foreach ($res as $id => $aResult) { // map each result
148
                foreach ($map as $key => $columnName) {
149
                    $res[$id][$key] = $res[$id][$columnName];
150
                    unset($res[$id][$columnName]);
151
                }
152
            }
153
        }
154
 
155
        return $res;
156
    }
157
 
158
    // }}}
159
    // {{{ add()
160
 
161
    /**
162
     * adds _one_ new element in the tree under the given parent
163
     * the values' keys given have to match the db-columns, because the
164
     * value gets inserted in the db directly
165
     * to add an entire node containing children and so on see 'addNode()'
166
     *
167
     * to be compatible with the MDBnested, u can also give the parent and previd
168
     * as the second and third parameter
169
     *
170
     * @see addNode
171
     * @access public
172
     * @param array $newValues this array contains the values that shall be inserted in the db-table
173
     *                         the key for each element is the name of the column
174
     * @return mixed either boolean false on failure or the id of the inserted row
175
     */
176
    function add($newValues, $parentId = 0)
177
    {
178
        // FIXXME use $this->dbh->tableInfo to check which columns exist
179
        // so only data for which a column exist is inserted
180
        if ($parentId) {
181
            $newValues['parentId'] = $parentId;
182
        }
183
        $newData = array();
184
        foreach ($newValues as $key => $value) {
185
            // quote the values, as needed for the insert
186
            $newData[$this->_getColName($key)] = $this->_quote($value);
187
        }
188
        // use sequences to create a new id in the db-table
189
        $nextId = $this->dbh->nextId($this->table);
190
        $query = sprintf("INSERT INTO %s (%s,%s) VALUES (%s,%s)",
191
                         $this->table ,
192
                         $this->_getColName('id'),
193
                         implode(',', array_keys($newData)) ,
194
                         $nextId,
195
                         implode(',', $newData));
196
        if (MDB::isError($res = $this->dbh->query($query))) {
197
            // TODO raise PEAR error
198
            printf("ERROR - Tree::add - %s - %s<br />", MDB::errorMessage($res), $query);
199
            return false;
200
        }
201
 
202
        return $nextId;
203
    }
204
 
205
    // }}}
206
    // {{{ remove()
207
 
208
    /**
209
     * removes the given node
210
     *
211
     * @access public
212
     * @param  mixed $id the id of the node to be removed, or an array of id's to be removed
213
     * @return boolean true on success
214
     */
215
    function remove($id)
216
    {
217
        // if the one to remove has children, get their id's to remove them too
218
        if ($this->hasChildren($id)) {
219
            $id = $this->walk(array('_remove', $this), $id, 'array');
220
        }
221
        $idColumnName = 'id';
222
        $map = $this->getOption('columnNameMaps');
223
        if (isset($map['id'])) { // if there are maps given
224
                $idColumnName = $map['id'];
225
        }
226
 
227
        $whereClause = "WHERE $idColumnName=$id";
228
        if (is_array($id)) {
229
            $whereClause = "WHERE $idColumnName in (" . implode(',', $id) . ')';
230
        }
231
 
232
        $query = "DELETE FROM {$this->table} $whereClause";
233
        // print('<br />'.$query);
234
        if (MDB::isError($res = $this->dbh->query($query))) {
235
            // TODO raise PEAR error
236
            printf("ERROR - Tree::remove - %s - %s<br>", MDB::errormessage($res), $query);
237
            return false;
238
        }
239
        // TODO if remove succeeded set prevId of the following element properly
240
        return true;
241
    }
242
 
243
    // }}}
244
    // {{{ move()
245
 
246
    /**
247
     * move an entry under a given parent or behind a given entry
248
     *
249
     * @version 2001/10/10
250
     * @access public
251
     * @author Wolfram Kriesing <wolfram@kriesing.de>
252
     * @param integer $idToMove the id of the element that shall be moved
253
     * @param integer $newParentId the id of the element which will be the new parent
254
     * @param integer $newPrevId if prevId is given the element with the id idToMove
255
     *                                        shall be moved _behind_ the element with id=prevId
256
     *                                        if it is 0 it will be put at the beginning
257
     *                                        if no prevId is in the DB it can be 0 too and won't bother
258
     *                                        since it is not written in the DB anyway
259
     * @return boolean true for success
260
     */
261
    function move($idToMove, $newParentId, $newPrevId = 0)
262
    {
263
        $idColumnName = 'id';
264
        $parentIdColumnName = 'parentId';
265
        $map = $this->getOption('columnNameMaps');
266
        if (isset($map['id'])) {
267
            $idColumnName = $map['id'];
268
        }
269
        if (isset($map['parentId'])) {
270
            $parentIdColumnName = $map['parentId'];
271
        }
272
        // FIXXME todo: previous stuff
273
        // set the parent in the DB
274
        $query = "UPDATE $this->table SET $parentIdColumnName=$newParentId WHERE $idColumnName=$idToMove";
275
        // print($query);
276
        if (MDB::isError($res = $this->dbh->query($query))) {
277
            // TODO raise PEAR error
278
            printf("ERROR - Tree::move - %s - %s<br>", MDB::errorMessage($res), $query);
279
            return false;
280
        }
281
        // FIXXME update the prevId's of the elements where the element was moved away from and moved in
282
        return true;
283
    }
284
 
285
    // }}}
286
    // {{{ update()
287
 
288
    /**
289
     * update an element in the DB
290
     *
291
     * @version 2002/01/17
292
     * @access public
293
     * @author Wolfram Kriesing <wolfram@kriesing.de>
294
     * @param array $newData all the new data, the key 'id' is used to
295
                             build the 'WHERE id=' clause and all the other
296
     *                       elements are the data to fill in the DB
297
     * @return boolean true for success
298
     */
299
    function update($id, $newData)
300
    {
301
        // FIXXME check $this->dbh->tableInfo to see if all the columns that shall be updated
302
        // really exist, this will also extract nextId etc. if given before writing it in the DB
303
        // in case they dont exist in the DB
304
        $setData = array();
305
        foreach ($newData as $key => $value) { // quote the values, as needed for the insert
306
            $setData[] = $this->_getColName($key) . '=' . $this->_quote($value);
307
        }
308
 
309
        $query = sprintf('UPDATE %s SET %s WHERE %s=%s',
310
                         $this->table,
311
                         implode(',', $setData),
312
                         $this->_getColName('id'),
313
                         $id
314
                );
315
        if (MDB::isError($res = $this->dbh->query($query))) {
316
            // FIXXME raise PEAR error
317
            printf("ERROR - Tree::update - %s - %s<br>", MDB::errormessage($res), $query);
318
            return false;
319
        }
320
 
321
        return true;
322
    }
323
 
324
    // }}}
325
    // {{{ _throwError()
326
 
327
    /**
328
     *
329
     * @access private
330
     * @version 2002/03/02
331
     * @author Wolfram Kriesing <wolfram@kriesing.de>
332
     */
333
    function _throwError($msg, $line, $mode = null)
334
    {
335
        return new Tree_Error($msg, $line, __FILE__, $mode, $this->dbh->last_query);
336
    }
337
 
338
    // }}}
339
    // {{{ _prepareResults()
340
 
341
    /**
342
     * prepare multiple results
343
     *
344
     * @see _prepareResult
345
     * @access private
346
     * @version 2002/03/03
347
     * @author Wolfram Kriesing <wolfram@kriesing.de>
348
     */
349
    function _prepareResults($results)
350
    {
351
        $newResults = array();
352
        foreach ($results as $aResult) {
353
            $newResults[] = $this->_prepareResult($aResult);
354
        }
355
        return $newResults;
356
    }
357
 
358
    // }}}
359
    // {{{ _prepareResult()
360
 
361
    /**
362
     * map back the index names to get what is expected
363
     *
364
     * @access private
365
     * @version 2002/03/03
366
     * @author Wolfram Kriesing <wolfram@kriesing.de>
367
     */
368
    function _prepareResult($result)
369
    {
370
        $map = $this->getOption('columnNameMaps');
371
 
372
        if ($map) {
373
            foreach ($map as $key => $columnName) {
374
                $result[$key] = $result[$columnName];
375
                unset($result[$columnName]);
376
            }
377
        }
378
        return $result;
379
    }
380
 
381
    // }}}
382
    // {{{ _getColName()
383
 
384
    /**
385
     * this method retreives the real column name, as used in the DB
386
     *    since the internal names are fixed, to be portable between different
387
     *    DB-column namings, we map the internal name to the real column name here
388
     *
389
     * @access private
390
     * @version 2002/03/02
391
     * @author Wolfram Kriesing <wolfram@kriesing.de>
392
     */
393
    function _getColName($internalName)
394
    {
395
        if ($map = $this->getOption('columnNameMaps')) {
396
            if (isset($map[$internalName])) {
397
                return $map[$internalName];
398
            }
399
        }
400
        return $internalName;
401
    }
402
 
403
    // }}}
404
    // {{{ _quoteArray()
405
 
406
    /**
407
     * quotes all the data in this array
408
     *
409
     * @param array $data Array to quote
410
     */
411
    function _quoteArray($data)
412
    {
413
        foreach ($data as $key => $val) {
414
            $data[$key] = $this->_quote($val);
415
        }
416
        return $data;
417
    }
418
 
419
    // }}}
420
    // {{{ _quote()
421
 
422
    /**
423
     * quotes all the data in this var (can be any type except object)
424
     * @param mixed $data data to quote
425
     */
426
    function _quote($data)
427
    {
428
        switch(gettype($data)) {
429
            case 'array':
430
                return $this->_quoteArray($data);
431
                break;
432
            case 'boolean':
433
                return $this->dbh->getBooleanValue($data);
434
                break;
435
            case 'double':
436
                return $this->dbh->getFloatValue($data);
437
                break;
438
            case 'integer':
439
                return $this->dbh->getIntegerValue($data);
440
                break;
441
            case 'string':  //if 'string' or 'unknown', quote as text
442
            default:
443
                return $this->dbh->getTextValue($data);
444
        }
445
    }
446
 
447
    // }}}
448
}