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