Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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