/[cvs]/nfo/php/libs/net.php.pear/Tree/Dynamic/DBnested.php
ViewVC logotype

Annotation of /nfo/php/libs/net.php.pear/Tree/Dynamic/DBnested.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (hide annotations)
Thu Feb 27 16:49:58 2003 UTC (21 years, 6 months ago) by joko
Branch: MAIN
+ initial commit, from PEAR

1 joko 1.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: |
17     // +----------------------------------------------------------------------+
18     //
19     // Id: DBnested.php,v 1.13 2003/01/18 15:35:47 cain Exp
20     // $Id: DBnested.php,v 1.13 2003/01/18 15:35:47 cain Exp $
21    
22     require_once('Tree/Common.php');
23     require_once('Tree/Error.php');
24    
25     /**
26     * this class implements methods to work on a tree saved using the nested
27     * tree model
28     * explaination: http://research.calacademy.org/taf/proceedings/ballew/index.htm
29     *
30     * @access public
31     * @package Tree
32     */
33     class Tree_Dynamic_DBnested extends Tree_Common
34     // FIXXME should actually extend Tree_Common, to use the methods provided in there... but we need to connect
35     // to the db here, so we extend optionsDB for now, may be use "aggregate" function to fix that
36     {
37    
38     var $debug = 0;
39    
40     var $options = array(
41     // FIXXME to be implemented
42     'whereAddOn'=>'' // add on for the where clause, this string is simply added behind the WHERE in the select
43     // so you better make sure its correct SQL :-), i.e. 'uid=3'
44     // this is needed i.e. when you are saving many trees in one db-table
45     ,'table' =>'' //
46     // since the internal names are fixed, to be portable between different
47     // DB tables with different column namings, we map the internal name to the real column name
48     // using this array here, if it stays empty the internal names are used, which are:
49     // id, left, right
50     ,'columnNameMaps'=>array(
51     //'id' => 'node_id', // use "node_id" as "id"
52     'left' => 'l' // since mysql at least doesnt support 'left' ...
53     ,'right' => 'r' // ...as a column name we set default to the first letter only
54     //'name' => 'nodeName' //
55     ,'parentId' => 'parent' // parent id
56     )
57     ,'order' => '' // needed for sorting the tree, currently only used in Memory_DBnested
58     );
59    
60     // the defined methods here are proposals for the implementation,
61     // they are named the same, as the methods in the "Memory" branch, if possible
62     // it would be cool to keep the same naming and if the same parameters would be possible too
63     // then it would be even better, so one could easily change from any kind of
64     // tree-implementation to another, without changing the source code, only the setupXXX would need to be changed
65    
66     /**
67     *
68     *
69     * @access public
70     * @version 2002/03/02
71     * @author Wolfram Kriesing <wolfram@kriesing.de>
72     * @param
73     * @return
74     */
75     function __construct( $dsn , $options=array() )
76     {
77     Tree_Dynamic_DBnested( $dsn , $options );
78     }
79    
80     /**
81     *
82     *
83     * @access public
84     * @version 2002/03/02
85     * @author Wolfram Kriesing <wolfram@kriesing.de>
86     * @param
87     * @return
88     */
89     function Tree_Dynamic_DBnested( $dsn , $options=array() )
90     {
91     parent::Tree_OptionsDB( $dsn , $options ); // instanciate DB
92     $this->table = $this->getOption('table');
93     }
94    
95     /**
96     * add a new element to the tree
97     * there are three ways to use this method
98     * 1.
99     * give only the $parentId and the $newValues will be inserted as the first child of this parent
100     * i.e. // insert a new element under the parent with the ID=7
101     * $tree->add( array('name'=>'new element name') , 7 );
102     * 2.
103     * give the $prevId ($parentId will be dismissed) and the new element
104     * will be inserted in the tree after the element with the ID=$prevId
105     * the parentId is not necessary because the prevId defines exactly where
106     * the new element has to be place in the tree, and the parent is the same as
107     * for the element with the ID=$prevId
108     * i.e. // insert a new element after the element with the ID=5
109     * $tree->add( array('name'=>'new') , 0 , 5 );
110     * 3.
111     * neither $parentId nor prevId is given, then the root element will be inserted
112     * this requires that programmer is responsible to confirm this
113     * this method does not (yet) check if there is already a root element saved !!!
114     *
115     * @access public
116     * @author Wolfram Kriesing <wolfram@kriesing.de>
117     * @param array $newValues this array contains the values that shall be inserted in the db-table
118     * @param integer $parentId the id of the element which shall be the parent of the new element
119     * @param integer $prevId the id of the element which shall preceed the one to be inserted
120     * use either 'parentId' or 'prevId'
121     * @return integer the ID of the element that had been inserted
122     */
123     function add( $newValues , $parentId=0 , $prevId=0 )
124     {
125     $lName = $this->_getColName('left');
126     $rName = $this->_getColName('right');
127     $prevVisited = 0;
128    
129     // check the DB-table if the columns which are given as keys
130     // in the array $newValues do really exist, if not remove them from the array
131     // FIXXME do the above described
132    
133     if( $parentId || $prevId ) // if no parent and no prevId is given the root shall be added
134     {
135     if( $prevId )
136     {
137     $element = $this->getElement( $prevId );
138     $parentId = $element['parentId']; // we also need the parent id of the element, to write it in the db
139     }
140     else
141     {
142     $element = $this->getElement( $parentId );
143     }
144     $newValues['parentId'] = $parentId;
145    
146     if( PEAR::isError($element) )
147     return $element;
148    
149     // get the "visited"-value where to add the new element behind
150     // if $prevId is given, we need to use the right-value
151     // if only the $parentId is given we need to use the left-value
152     // look at it graphically, that made me understand it :-)
153     // i.e. at: http://research.calacademy.org/taf/proceedings/ballew/sld034.htm
154     $prevVisited = $prevId ? $element['right'] : $element['left'];
155    
156     // FIXXME start transaction here
157    
158     if( PEAR::isError($err=$this->_add( $prevVisited , 1 )) )
159     {
160     // FIXXME rollback
161     //$this->dbh->rollback();
162     return $err;
163     }
164     }
165    
166     // inserting _one_ new element in the tree
167     $newData = array();
168     foreach( $newValues as $key=>$value ) // quote the values, as needed for the insert
169     {
170     $newData[$this->_getColName($key)] = $this->dbh->quote($value);
171     }
172    
173     // set the proper right and left values
174     $newData[$lName] = $prevVisited+1;
175     $newData[$rName] = $prevVisited+2;
176    
177     // use sequences to create a new id in the db-table
178     $nextId = $this->dbh->nextId($this->table);
179     $query = sprintf( 'INSERT INTO %s (%s,%s) VALUES (%s,%s)',
180     $this->table ,
181     $this->_getColName('id'),
182     implode( "," , array_keys($newData) ) ,
183     $nextId,
184     implode( "," , $newData )
185     );
186     if( DB::isError( $res = $this->dbh->query($query) ) )
187     {
188     // rollback
189     return $this->_throwError( $res->getMessage() , __LINE__ );
190     }
191     // commit here
192    
193     return $nextId;
194     } // end of function
195    
196     /**
197     * this method only updates the left/right values of all the
198     * elements that are affected by the insertion
199     * be sure to set the parentId of the element(s) you insert
200     *
201     * @param int this parameter is not the ID!!!
202     * it is the previous visit number, that means
203     * if you are inserting a child, you need to use the left-value
204     * of the parent
205     * if you are inserting a "next" element, on the same level
206     * you need to give the right value !!
207     * @param int the number of elements you plan to insert
208     * @return mixed either true on success or a Tree_Error on failure
209     */
210     function _add( $prevVisited , $numberOfElements=1 )
211     {
212     $lName = $this->_getColName('left');
213     $rName = $this->_getColName('right');
214    
215     // update the elements which will be affected by the new insert
216     $query = sprintf( 'UPDATE %s SET %s=%s+%s WHERE%s %s>%s',
217     $this->table,
218     $lName,$lName,
219     $numberOfElements*2,
220     $this->_getWhereAddOn(),
221     $lName,
222     $prevVisited );
223     if( DB::isError( $res = $this->dbh->query($query) ) )
224     {
225     // FIXXME rollback
226     return $this->_throwError( $res->getMessage() , __LINE__ );
227     }
228    
229     $query = sprintf( 'UPDATE %s SET %s=%s+%s WHERE%s %s>%s',
230     $this->table,
231     $rName,$rName,
232     $numberOfElements*2,
233     $this->_getWhereAddOn(),
234     $rName,
235     $prevVisited );
236     if( DB::isError( $res = $this->dbh->query($query) ) )
237     {
238     // FIXXME rollback
239     return $this->_throwError( $res->getMessage() , __LINE__ );
240     }
241     return true;
242     }
243    
244     /**
245     * remove a tree element
246     * this automatically remove all children and their children
247     * if a node shall be removed that has children
248     *
249     * @access public
250     * @author Wolfram Kriesing <wolfram@kriesing.de>
251     * @param integer $id the id of the element to be removed
252     * @return boolean returns either true or throws an error
253     */
254     function remove( $id )
255     {
256     $element = $this->getElement( $id );
257     if( PEAR::isError($element) )
258     return $element;
259    
260     // FIXXME start transaction
261     //$this->dbh->autoCommit(false);
262     $query = sprintf( 'DELETE FROM %s WHERE%s %s BETWEEN %s AND %s',
263     $this->table,
264     $this->_getWhereAddOn(),
265     $this->_getColName('left'),
266     $element['left'],$element['right']);
267     if( DB::isError( $res = $this->dbh->query($query) ) )
268     {
269     // FIXXME rollback
270     //$this->dbh->rollback();
271     return $this->_throwError( $res->getMessage() , __LINE__ );
272     }
273    
274     if( PEAR::isError($err=$this->_remove( $element )) )
275     {
276     // FIXXME rollback
277     //$this->dbh->rollback();
278     return $err;
279     }
280     return true;
281     }
282    
283     /**
284     * removes a tree element, but only updates the left/right values
285     * to make it seem as if the given element would not exist anymore
286     * it doesnt remove the row(s) in the db itself!
287     *
288     * @see getElement()
289     * @access private
290     * @author Wolfram Kriesing <wolfram@kriesing.de>
291     * @param array the entire element returned by "getElement"
292     * @return boolean returns either true or throws an error
293     */
294     function _remove( $element )
295     {
296     $delta = $element['right'] - $element['left'] +1;
297     $lName = $this->_getColName('left');
298     $rName = $this->_getColName('right');
299    
300     // update the elements which will be affected by the remove
301     $query = sprintf( "UPDATE %s SET %s=%s-$delta, %s=%s-$delta WHERE%s %s>%s",
302     $this->table,
303     $lName,$lName,
304     $rName,$rName,
305     $this->_getWhereAddOn(),
306     $lName,$element['left'] );
307     if( DB::isError( $res = $this->dbh->query($query) ) )
308     {
309     // the rollback shall be done by the method calling this one, since it is only private we can do that
310     return $this->_throwError( $res->getMessage() , __LINE__ );
311     }
312    
313     $query = sprintf( "UPDATE %s SET %s=%s-$delta WHERE%s %s<%s AND %s>%s",
314     $this->table,
315     $rName,$rName,
316     $this->_getWhereAddOn(),
317     $lName,$element['left'],
318     $rName,$element['right'] );
319     if( DB::isError( $res = $this->dbh->query($query) ) )
320     {
321     // the rollback shall be done by the method calling this one, since it is only private
322     return $this->_throwError( $res->getMessage() , __LINE__ );
323     }
324     // FIXXME commit - should that not also be done in the method calling this one? like when an error occurs?
325     //$this->dbh->commit();
326     return true;
327     } // end of function
328    
329     /**
330     * move an entry under a given parent or behind a given entry.
331     * If a newPrevId is given the newParentId is dismissed!
332     * call it either like this:
333     * $tree->move( x , y )
334     * to move the element (or entire tree) with the id x
335     * under the element with the id y
336     * or
337     * $tree->move( x , 0 , y ); // ommit the second parameter by setting it to 0
338     * to move the element (or entire tree) with the id x
339     * behind the element with the id y
340     * or
341     * $tree->move( array(x1,x2,x3) , ...
342     * the first parameter can also be an array of elements that shall be moved
343     * the second and third para can be as described above
344     * If you are using the Memory_DBnested then this method would be invain,
345     * since Memory.php already does the looping through multiple elements, but if
346     * Dynamic_DBnested is used we need to do the looping here
347     *
348     * @version 2002/06/08
349     * @access public
350     * @author Wolfram Kriesing <wolfram@kriesing.de>
351     * @param integer the id(s) of the element(s) that shall be moved
352     * @param integer the id of the element which will be the new parent
353     * @param integer if prevId is given the element with the id idToMove
354     * shall be moved _behind_ the element with id=prevId
355     * if it is 0 it will be put at the beginning
356     * @return mixed true for success, Tree_Error on failure
357     */
358     function move( $idsToMove , $newParentId , $newPrevId=0 )
359     {
360     settype($idsToMove,'array');
361     $errors = array();
362     foreach( $idsToMove as $idToMove )
363     {
364     $ret = $this->_move( $idToMove , $newParentId , $newPrevId );
365     if( PEAR::isError($ret) )
366     $errors[] = $ret;
367     }
368     // FIXXME the error in a nicer way, or even better let the throwError method do it!!!
369     if( sizeof($errors) )
370     {
371     return $this->_throwError(serialize($errors),__LINE__);
372     }
373     return true;
374     }
375    
376     /**
377     * this method moves one tree element
378     *
379     * @see move()
380     * @version 2002/04/29
381     * @access public
382     * @author Wolfram Kriesing <wolfram@kriesing.de>
383     * @param integer the id of the element that shall be moved
384     * @param integer the id of the element which will be the new parent
385     * @param integer if prevId is given the element with the id idToMove
386     * shall be moved _behind_ the element with id=prevId
387     * if it is 0 it will be put at the beginning
388     * @return mixed true for success, Tree_Error on failure
389     */
390     function _move( $idToMove , $newParentId , $newPrevId=0 )
391     {
392     // do some integrity checks first
393     if( $newPrevId )
394     {
395     if( $newPrevId == $idToMove ) // dont let people move an element behind itself, tell it succeeded, since it already is there :-)
396     {
397     return true;
398     }
399     if( PEAR::isError($newPrevious = $this->getElement( $newPrevId )) )
400     return $newPrevious;
401     $newParentId = $newPrevious['parentId'];
402     }
403     else
404     {
405     if( $newParentId == 0 )
406     {
407     return $this->_throwError( 'no parent id given' , __LINE__ );
408     }
409     if( $this->isChildOf( $idToMove , $newParentId ) ) // if the element shall be moved under one of its children, return false
410     {
411     return $this->_throwError( 'can not move an element under one of its children' , __LINE__ );
412     }
413     if( $newParentId == $idToMove ) // dont do anything to let an element be moved under itself, which is bullshit
414     {
415     return true;
416     }
417     if( PEAR::isError($newParent = $this->getElement( $newParentId )) ) // try to retreive the data of the parent element
418     {
419     return $newParent;
420     }
421     }
422    
423     if( PEAR::isError($element=$this->getElement($idToMove)) ) // get the data of the element itself
424     {
425     return $element;
426     }
427    
428     $numberOfElements = ($element['right'] - $element['left']+1)/2;
429     $prevVisited = $newPrevId ? $newPrevious['right'] : $newParent['left'];
430    
431     // FIXXME start transaction
432    
433     // add the left/right values in the new parent, to have the space to move the new values in
434     if( PEAR::isError($err=$this->_add( $prevVisited , $numberOfElements )) )
435     {
436     // FIXXME rollback
437     //$this->dbh->rollback();
438     return $err;
439     }
440    
441     // update the parentId of the element with $idToMove
442     if( PEAR::isError($err=$this->update( $idToMove , array('parentId'=>$newParentId) )) )
443     {
444     // FIXXME rollback
445     //$this->dbh->rollback();
446     return $err;
447     }
448    
449     // update the lefts and rights of those elements that shall be moved
450    
451     // first get the offset we need to add to the left/right values
452     // if $newPrevId is given we need to get the right value, otherwise the left
453     // since the left/right has changed, because we already updated it up there we need to
454     // get them again, we have to do that anyway, to have the proper new left/right values
455     if( $newPrevId )
456     {
457     if( PEAR::isError($temp = $this->getElement( $newPrevId )) )
458     {
459     // FIXXME rollback
460     //$this->dbh->rollback();
461     return $temp;
462     }
463     $calcWith = $temp['right'];
464     }
465     else
466     {
467     if( PEAR::isError($temp = $this->getElement( $newParentId )) )
468     {
469     // FIXXME rollback
470     //$this->dbh->rollback();
471     return $temp;
472     }
473     $calcWith = $temp['left'];
474     }
475    
476     // get the element that shall be moved again, since the left and right might have changed by the add-call
477     if( PEAR::isError($element=$this->getElement($idToMove)) )
478     {
479     return $element;
480     }
481    
482     $offset = $calcWith - $element['left']; // calc the offset that the element to move has to the spot where it should go
483     $offset++; // correct the offset by one, since it needs to go inbetween!
484    
485     $lName = $this->_getColName('left');
486     $rName = $this->_getColName('right');
487     $query = sprintf( "UPDATE %s SET %s=%s+$offset,%s=%s+$offset WHERE%s %s>%s AND %s<%s",
488     $this->table,
489     $rName,$rName,
490     $lName,$lName,
491     $this->_getWhereAddOn(),
492     $lName,$element['left']-1,
493     $rName,$element['right']+1 );
494     if( DB::isError( $res = $this->dbh->query($query) ) )
495     {
496     // FIXXME rollback
497     //$this->dbh->rollback();
498     return $this->_throwError( $res->getMessage() , __LINE__ );
499     }
500    
501     // remove the part of the tree where the element(s) was/were before
502     if( PEAR::isError($err=$this->_remove( $element )) )
503     {
504     // FIXXME rollback
505     //$this->dbh->rollback();
506     return $err;
507     }
508     // FIXXME commit all changes
509     //$this->dbh->commit();
510    
511     return true;
512     } // end of function
513    
514     /**
515     * update the tree element given by $id with the values in $newValues
516     *
517     * @access public
518     * @author Wolfram Kriesing <wolfram@kriesing.de>
519     * @param int the id of the element to update
520     * @param array the new values, the index is the col name
521     * @return mixed either true or an Tree_Error
522     */
523     function update( $id , $newValues )
524     {
525     // jsut to be sure nothing gets screwed up :-)
526     unset( $newValues[$this->_getColName('left')] );
527     unset( $newValues[$this->_getColName('right')] );
528     unset( $newValues[$this->_getColName('parentId')] );
529    
530     // updating _one_ element in the tree
531     $values = array();
532     foreach( $newValues as $key=>$value )
533     $values[] = $this->_getColName($key).'='.$this->dbh->quote($value);
534    
535    
536     $query = sprintf( 'UPDATE %s SET %s WHERE%s %s=%s',
537     $this->table,
538     implode(',',$values),
539     $this->_getWhereAddOn(),
540     $this->_getColName('id'),
541     $id);
542     if( DB::isError( $res=$this->dbh->query($query) ) ){
543     // FIXXME raise PEAR error
544     return $this->_throwError( $res->getMessage() , __LINE__ );
545     }
546    
547     return true;
548     } // end of function
549    
550     /**
551     * copy a subtree/node/... under a new parent or/and behind a given element
552     *
553     *
554     * @access public
555     * @author Wolfram Kriesing <wolfram@kriesing.de>
556     * @param
557     * @return
558     */
559     function copy( $id , $parentId=0 , $prevId=0 )
560     {
561     return $this->_throwError( 'copy-method is not implemented yet!' , __LINE__ );
562     // get element tree
563     // $this->addTree
564     } // end of function
565    
566    
567     /**
568     * get the root
569     *
570     * @access public
571     * @version 2002/03/02
572     * @author Wolfram Kriesing <wolfram@kriesing.de>
573     * @param
574     * @return mixed either the data of the root element or an Tree_Error
575     */
576     function getRoot()
577     {
578     $query = sprintf( 'SELECT * FROM %s WHERE%s %s=1',
579     $this->table,
580     $this->_getWhereAddOn(),
581     $this->_getColName('left'));
582     if( DB::isError( $res = $this->dbh->getRow($query) ) )
583     {
584     return $this->_throwError( $res->getMessage() , __LINE__ );
585     }
586     return $this->_prepareResult( $res );
587     } // end of function
588    
589     /**
590     *
591     *
592     * @access public
593     * @version 2002/03/02
594     * @author Wolfram Kriesing <wolfram@kriesing.de>
595     * @param
596     * @return mixed either the data of the requested element or an Tree_Error
597     */
598     function getElement( $id )
599     {
600     $query = sprintf( 'SELECT * FROM %s WHERE%s %s=%s',
601     $this->table,
602     $this->_getWhereAddOn(),
603     $this->_getColName('id'),
604     $id);
605     if( DB::isError( $res = $this->dbh->getRow($query) ) )
606     {
607     return $this->_throwError( $res->getMessage() , __LINE__ );
608     }
609     if( !$res )
610     return $this->_throwError( "Element with id $id does not exist!" , __LINE__ );
611    
612     return $this->_prepareResult( $res );
613     } // end of function
614    
615     /**
616     *
617     *
618     * @access public
619     * @version 2002/03/02
620     * @author Wolfram Kriesing <wolfram@kriesing.de>
621     * @param
622     * @return mixed either the data of the requested element or an Tree_Error
623     */
624     function getChild( $id )
625     {
626     // subqueries would be cool :-)
627     $curElement = $this->getElement( $id );
628     if( PEAR::isError($curElement) )
629     return $curElement;
630    
631     $query = sprintf( 'SELECT * FROM %s WHERE%s %s=%s',
632     $this->table,
633     $this->_getWhereAddOn(),
634     $this->_getColName('left'),
635     $curElement['left']+1 );
636     if( DB::isError( $res = $this->dbh->getRow($query) ) )
637     {
638     return $this->_throwError( $res->getMessage() , __LINE__ );
639     }
640     return $this->_prepareResult( $res );
641     }
642    
643     /**
644     * gets the path from the element with the given id down
645     * to the root
646     * the returned array is sorted to start at root
647     * for simply walking through and retreiving the path
648     *
649     * @access public
650     * @version 2002/03/02
651     * @author Wolfram Kriesing <wolfram@kriesing.de>
652     * @param
653     * @return mixed either the data of the requested elements or an Tree_Error
654     */
655     function getPath( $id )
656     {
657     // subqueries would be cool :-)
658     $curElement = $this->getElement( $id );
659    
660     $query = sprintf( 'SELECT * FROM %s WHERE%s %s<=%s AND %s>=%s ORDER BY %s',
661     $this->table,
662     $this->_getWhereAddOn(),
663     $this->_getColName('left'),
664     $curElement['left'],
665     $this->_getColName('right'),
666     $curElement['right'],
667     $this->_getColName('left') );
668     if( DB::isError( $res = $this->dbh->getAll($query) ) )
669     {
670     return $this->_throwError( $res->getMessage() , __LINE__ );
671     }
672     return $this->_prepareResults( $res );
673     }
674    
675     /**
676     * gets the element to the left, the left visit
677     *
678     * @access public
679     * @version 2002/03/07
680     * @author Wolfram Kriesing <wolfram@kriesing.de>
681     * @param
682     * @return mixed either the data of the requested element or an Tree_Error
683     */
684     function getLeft( $id )
685     {
686     $element = $this->getElement( $id );
687     if( PEAR::isError($element) )
688     return $element;
689    
690     $query = sprintf( 'SELECT * FROM %s WHERE%s (%s=%s OR %s=%s)',
691     $this->table,
692     $this->_getWhereAddOn(),
693     $this->_getColName('right'),$element['left']-1,
694     $this->_getColName('left'),$element['left']-1 );
695     if( DB::isError( $res = $this->dbh->getRow($query) ) )
696     {
697     return $this->_throwError( $res->getMessage() , __LINE__ );
698     }
699     return $this->_prepareResult( $res );
700     }
701    
702     /**
703     * gets the element to the right, the right visit
704     *
705     * @access public
706     * @version 2002/03/07
707     * @author Wolfram Kriesing <wolfram@kriesing.de>
708     * @param
709     * @return mixed either the data of the requested element or an Tree_Error
710     */
711     function getRight( $id )
712     {
713     $element = $this->getElement( $id );
714     if( PEAR::isError($element) )
715     return $element;
716    
717     $query = sprintf( 'SELECT * FROM %s WHERE%s (%s=%s OR %s=%s)',
718     $this->table,
719     $this->_getWhereAddOn(),
720     $this->_getColName('left'),$element['right']+1,
721     $this->_getColName('right'),$element['right']+1);
722     if( DB::isError( $res = $this->dbh->getRow($query) ) )
723     {
724     return $this->_throwError( $res->getMessage() , __LINE__ );
725     }
726     return $this->_prepareResult( $res );
727     }
728    
729     /**
730     * get the parent of the element with the given id
731     *
732     * @access public
733     * @version 2002/04/15
734     * @author Wolfram Kriesing <wolfram@kriesing.de>
735     * @param
736     * @return mixed the array with the data of the parent element
737     * or false, if there is no parent, if the element is the root
738     * or an Tree_Error
739     */
740     function getParent( $id )
741     {
742     $query = sprintf( 'SELECT p.* FROM %s p,%s e WHERE%s e.%s=p.%s AND e.%s=%s',
743     $this->table,$this->table,
744     $this->_getWhereAddOn( ' AND ' , 'p' ),
745     $this->_getColName('parentId'),
746     $this->_getColName('id'),
747     $this->_getColName('id'),
748     $id);
749     if( DB::isError( $res = $this->dbh->getRow($query) ) )
750     {
751     return $this->_throwError( $res->getMessage() , __LINE__ );
752     }
753     return $this->_prepareResult( $res );
754     }
755    
756     /**
757     * get the children of the given element
758     * or if the parameter is an array, it gets the children of all
759     * the elements given by their ids in the array
760     *
761     * @access public
762     * @version 2002/04/15
763     * @author Wolfram Kriesing <wolfram@kriesing.de>
764     * @param mixed (1) int the id of one element
765     * (2) array an array of ids for which
766     * the children will be returned
767     * @param integer the children of how many levels shall be returned
768     * @return mixed the array with the data of all children
769     * or false, if there are none
770     */
771     function getChildren( $ids , $levels=1 )
772     {
773     $res = array();
774     for( $i=1 ; $i<$levels+1 ; $i++ )
775     {
776     // if $ids is an array implode the values
777     $getIds = is_array($ids) ? implode(',',$ids) : $ids;
778    
779     $query = sprintf( 'SELECT c.* FROM %s c,%s e WHERE%s e.%s=c.%s AND e.%s IN (%s) '.
780     'ORDER BY c.%s',
781     $this->table,$this->table,
782     $this->_getWhereAddOn( ' AND ' , 'c' ),
783     $this->_getColName('id'),
784     $this->_getColName('parentId'),
785     $this->_getColName('id'),
786     $getIds,
787     // order by left, so we have it in the order as it is in the tree
788     // if no 'order'-option is given
789     $this->getOption('order') ? $this->getOption('order') : $this->_getColName('left')
790     );
791     if( DB::isError( $_res = $this->dbh->getAll($query) ) )
792     {
793     return $this->_throwError( $_res->getMessage() , __LINE__ );
794     }
795     $_res = $this->_prepareResults( $_res );
796    
797     // we use the id as the index, to make the use easier esp. for multiple return-values
798     $tempRes = array();
799     foreach( $_res as $aRes )
800     {
801     $tempRes[$aRes[$this->_getColName('id')]] = $aRes;
802     }
803     $_res = $tempRes;
804    
805     //
806     if( $levels>1 )
807     {
808     $ids = array();
809     foreach( $_res as $aRes )
810     $ids[] = $aRes[$this->_getColName('id')];
811     }
812     $res = array_merge($res,$_res);
813    
814     // quit the for-loop if there are no children in the current level
815     if( !sizeof($ids) )
816     break;
817     }
818     return $res;
819     }
820    
821     /**
822     * get the next element on the same level
823     * if there is none return false
824     *
825     * @access public
826     * @version 2002/04/15
827     * @author Wolfram Kriesing <wolfram@kriesing.de>
828     * @param
829     * @return mixed the array with the data of the next element
830     * or false, if there is no next
831     * or Tree_Error
832     */
833     function getNext( $id )
834     {
835     $query = sprintf( 'SELECT n.* FROM %s n,%s e WHERE%s e.%s=n.%s-1 AND e.%s=n.%s AND e.%s=%s',
836     $this->table,$this->table,
837     $this->_getWhereAddOn( ' AND ' , 'n' ),
838     $this->_getColName('right'),
839     $this->_getColName('left'),
840     $this->_getColName('parentId'),
841     $this->_getColName('parentId'),
842     $this->_getColName('id'),
843     $id);
844     if( DB::isError( $res = $this->dbh->getRow($query) ) )
845     {
846     return $this->_throwError( $res->getMessage() , __LINE__ );
847     }
848     if( !$res )
849     return false;
850     return $this->_prepareResult( $res );
851     }
852    
853     /**
854     * get the previous element on the same level
855     * if there is none return false
856     *
857     * @access public
858     * @version 2002/04/15
859     * @author Wolfram Kriesing <wolfram@kriesing.de>
860     * @param
861     * @return mixed the array with the data of the previous element
862     * or false, if there is no previous
863     * or a Tree_Error
864     */
865     function getPrevious( $id )
866     {
867     $query = sprintf( 'SELECT p.* FROM %s p,%s e WHERE%s e.%s=p.%s+1 AND e.%s=p.%s AND e.%s=%s',
868     $this->table,$this->table,
869     $this->_getWhereAddOn( ' AND ' , 'p' ),
870     $this->_getColName('left'),
871     $this->_getColName('right'),
872     $this->_getColName('parentId'),
873     $this->_getColName('parentId'),
874     $this->_getColName('id'),
875     $id);
876     if( DB::isError( $res = $this->dbh->getRow($query) ) )
877     {
878     return $this->_throwError( $res->getMessage() , __LINE__ );
879     }
880     if( !$res )
881     return false;
882     return $this->_prepareResult( $res );
883     }
884    
885     /**
886     * returns if $childId is a child of $id
887     *
888     * @abstract
889     * @version 2002/04/29
890     * @access public
891     * @author Wolfram Kriesing <wolfram@kriesing.de>
892     * @param int id of the element
893     * @param int id of the element to check if it is a child
894     * @return boolean true if it is a child
895     */
896     function isChildOf( $id , $childId )
897     {
898     // check simply if the left and right of the child are within the
899     // left and right of the parent, if so it definitly is a child :-)
900     $parent = $this->getElement( $id );
901     $child = $this->getElement( $childId );
902    
903     if( $parent['left'] < $child['left'] &&
904     $parent['right'] > $child['right'] )
905     {
906     return true;
907     }
908    
909     return false;
910     } // end of function
911    
912    
913    
914    
915    
916    
917     //
918     // PRIVATE METHODS
919     //
920    
921    
922     /**
923     *
924     *
925     * @access private
926     * @version 2002/04/20
927     * @author Wolfram Kriesing <wolfram@kriesing.de>
928     * @param string the current where clause
929     * @return
930     */
931     function _getWhereAddOn( $addAfter=' AND ' , $tableName='' )
932     {
933     if( $where=$this->getOption('whereAddOn') )
934     {
935     return ' '.($tableName?$tableName.'.':'')." $where$addAfter ";
936     }
937     return '';
938     }
939    
940    
941    
942    
943     // for compatibility to Memory methods
944     function getFirstRoot()
945     {
946     return $this->getRoot();
947     }
948     /**
949     * gets the tree under the given element in one array, sorted
950     * so you can go through the elements from begin to end and list them
951     * as they are in the tree, where every child (until the deepest) is retreived
952     *
953     * @see &_getNode()
954     * @access public
955     * @version 2001/12/17
956     * @author Wolfram Kriesing <wolfram@kriesing.de>
957     * @param integer $startId the id where to start walking
958     * @param integer $depth this number says how deep into the structure the elements shall be retreived
959     * @return array sorted as listed in the tree
960     */
961     function &getNode( $startId=0 , $depth=0 )
962     {
963     }
964    
965     }
966     ?>

MailToCvsAdmin">MailToCvsAdmin
ViewVC Help
Powered by ViewVC 1.1.26 RSS 2.0 feed