1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34:
35: class Db_NestedTree
36: {
37: 38: 39: 40:
41: protected $oSql = null;
42:
43: 44: 45: 46:
47: protected $label;
48:
49: 50: 51: 52:
53: protected $leftBound;
54:
55: 56: 57: 58:
59: protected $rightBound;
60:
61: 62: 63: 64:
65: protected $tableName;
66:
67: 68: 69: 70:
71: protected $level;
72:
73: 74: 75: 76:
77: protected $id;
78:
79: 80: 81: 82:
83: protected $current;
84:
85: const LEAFONLY = 1;
86: const NODEONLY = 2;
87:
88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98:
99: public function __construct($table,$label='nom',$left='left',$right='right',$level='level',$id='id')
100: {
101: $this->tableName = $table;
102: $this->leftBound = $left;
103: $this->rightBound = $right;
104: $this->level = $level;
105: $this->id = $id;
106: $this->label = $label;
107: $this->current = array();
108: }
109:
110: public function setDB(Zend_Db_Adapter_Abstract $db)
111: {
112: $this->oSql = $db;
113: }
114:
115: 116: 117: 118: 119: 120: 121:
122: public function setCurrent($id)
123: {
124: if(empty($this->oSql))
125: throw new RuntimeException("No db object set. Please use setDB() first");
126:
127: $prepare = $this->oSql->prepare('SELECT `'.$this->leftBound.'`,`'.$this->rightBound.'`,`'.$this->level.'`,`'.$this->label.'`
128: FROM '.$this->tableName.'
129: WHERE `'.$this->id.'` =:id');
130: $prepare->execute(array(':id'=>$id));
131: $current = $prepare->fetch(PDO::FETCH_ASSOC);
132: $this->current['left'] = intval($current[$this->leftBound]);
133: $this->current['right'] = intval($current[$this->rightBound]);
134: $this->current['level'] = intval($current[$this->level]);
135: return $current[$this->label];
136: }
137:
138: 139: 140: 141: 142: 143: 144: 145:
146: public function addRootElement($nom)
147: {
148: if(empty($this->oSql))
149: throw new RuntimeException("No db object set. Please use setDB() first");
150:
151:
152: $data = $this->oSql->query('SELECT `'.$this->rightBound.'` FROM '.$this->tableName.' WHERE `'.$this->leftBound.'` = 1')->fetch(PDO::FETCH_ASSOC);
153: if(!is_null($data[$this->rightBound]))
154: {
155: $leftB = $right+1;
156: $rightB = $right+2;
157: $level = $data[$this->level]+1;
158: }
159: else
160: {
161: $leftB = 1;
162: $rightB = 2;
163: $level = 0;
164: }
165: $prepare = $this->oSql->prepare('INSERT INTO '.$this->tableName.' (`'.$this->label.'`,`'.$this->leftBound.'`,`'.$this->rightBound.'`,`'.$this->level.'`)
166: VALUES(:nom,:left,:right,:level)');
167: $prepare->execute(array(':nom'=>$nom,':left'=>$leftB,':right'=>$rightB,':level'=>$level));
168:
169: return $this->oSql->last_id();
170: }
171:
172: 173: 174: 175: 176: 177:
178: public function getTree()
179: {
180: if(empty($this->oSql))
181: throw new RuntimeException("No db object set. Please use setDB() first");
182:
183: $data= $this->oSql->query('SELECT node.'.$this->label.', (COUNT(parent.'.$this->label.')-1) as depth
184: FROM '.$this->tableName.' as node,'.$this->tableName.' as parent
185: WHERE node.'.$this->leftBound.' BETWEEN parent.'.$this->leftBound.' AND parent.'.$this->rightBound.'
186: GROUP BY node.'.$this->label.'
187: ORDER BY node.'.$this->leftBound.',depth');
188: while($result = $data->fetch(PDO::FETCH_ASSOC))
189: $tree[] = $result;
190:
191: return $tree;
192: }
193:
194: 195: 196: 197: 198: 199:
200: public function getHTMLTree()
201: {
202: $tree = $this->getTree();
203:
204: $depth = 0;
205: $htmlTree = '';
206: $htmlTree .='<ul>';
207: foreach($tree as $value)
208: {
209: if($depth<$value['depth'])
210: {
211: $htmlTree .=str_repeat('<ul>',($value['depth']-$depth));
212: $depth = $value['depth'];
213: $htmlTree .= '<li>'.$value[$this->label].'</li>';
214: }
215: elseif($depth>$value['depth'])
216: {
217: $htmlTree .= str_repeat('</ul>',++$depth - $value['depth']);
218: $depth = $value['depth'];
219: $htmlTree .='<ul><li>'.$value[$this->label].'</li>';
220: }
221: else
222: $htmlTree .='<li>'.$value[$this->label].'</li>';
223: }
224: $htmlTree.='</ul>';
225: return $htmlTree;
226: }
227:
228: 229: 230: 231: 232: 233: 234:
235: public function countChilds($option=null)
236: {
237: if(empty($this->oSql))
238: throw new RuntimeException("No db object set. Please use setDB() first");
239:
240: $this->checkCurrent();
241:
242: if(!empty($option))
243: {
244: if(self::LEAFONLY == $option)
245: $option = ' AND (`'.$this->rightBound.'`-`'.$this->leftBound.'`)=1';
246: elseif(self::NODEONLY == $option)
247: $option = ' AND (`'.$this->rightBound.'`-`'.$this->leftBound.'`)>1';
248: else
249: $option = '';
250: }
251: else
252: $option = '';
253:
254: $child = $this->oSql->query('SELECT COUNT(*)
255: FROM '.$this->tableName.'
256: WHERE `'.$this->leftBound.'`>'.$this->current['left'].'
257: AND `'.$this->rightBound.'` < '.$this->current['right'].$option)
258: ->fetchColumn(0);
259: return $child;
260: }
261:
262: 263: 264: 265: 266: 267: 268: 269:
270: public function getChilds($direct=false,$option=null)
271: {
272: if(empty($this->oSql))
273: throw new RuntimeException("No db object set. Please use setDB() first");
274:
275: $this->checkCurrent();
276:
277: $childs = array();
278: if(!empty($option))
279: {
280: if(self::LEAFONLY == $option)
281: $option = ' AND (`'.$this->rightBound.'`-`'.$this->leftBound.'`)=1';
282: elseif(self::NODEONLY == $option)
283: $option = ' AND (`'.$this->rightBound.'`-`'.$this->leftBound.'`)>1';
284: else
285: $option = '';
286: }
287: else
288: $option = '';
289:
290: if($direct)
291: $option .= ' AND '.$this->level.' = '.($this->current['level']+1);
292:
293: $child = $this->oSql->query('SELECT '.$this->id.','.$this->label.'
294: FROM '.$this->tableName.'
295: WHERE `'.$this->leftBound.'`>'.$this->current['left'].'
296: AND `'.$this->rightBound.'` < '.$this->current['right'].$option);
297:
298: while($result = $child->fetch(PDO::FETCH_ASSOC))
299: $childs[] = $result;
300:
301: return $childs;
302: }
303:
304: 305: 306: 307: 308: 309: 310:
311: public function addChild($nom)
312: {
313: if(empty($this->oSql))
314: throw new RuntimeException("No db object set. Please use setDB() first");
315:
316: $this->checkCurrent();
317:
318:
319: $this->oSql->query('UPDATE '.$this->tableName.'
320: SET `'.$this->rightBound.'`=`'.$this->rightBound.'`+2
321: WHERE `'.$this->rightBound.'`>='.$this->current['right']);
322:
323: $this->oSql->query('UPDATE '.$this->tableName.'
324: SET `'.$this->leftBound.'`=`'.$this->leftBound.'`+2
325: WHERE `'.$this->leftBound.'`>='.$this->current['right']);
326:
327:
328: $prepare = $this->oSql->prepare('INSERT INTO '.$this->tableName.' (`'.$this->label.'`,`'.$this->leftBound.'`,`'.$this->rightBound.'`,`'.$this->level.'`)
329: VALUES(:nom,:left,:right,:level)');
330: $prepare->execute(array(':nom'=>$nom,':left'=>$this->current['right'],':right'=>$this->current['right']+1,':level'=>$this->current['level']+1));
331:
332: $this->current['right']+=2;
333: return $this->oSql->last_id();
334: }
335:
336: 337: 338: 339: 340: 341:
342: public function delete()
343: {
344: if(empty($this->oSql))
345: throw new RuntimeException("No db object set. Please use setDB() first");
346:
347: $this->checkCurrent();
348:
349: if(($this->current['right']-$this->current['left']==1))
350: {
351: $this->oSql->query('DELETE FROM '.$this->tableName.'
352: WHERE `'.$this->leftBound.'` = '.$this->current['left']);
353:
354: $this->oSql->query('UPDATE '.$this->tableName.'
355: SET `'.$this->leftBound.'`=`'.$this->leftBound.'`-2
356: WHERE `'.$this->leftBound.'`>='.$this->current['left']);
357:
358: $this->oSql->query('UPDATE '.$this->tableName.'
359: SET `'.$this->rightBound.'`=`'.$this->rightBound.'`-2
360: WHERE `'.$this->rightBound.'`>='.$this->current['left']);
361: return true;
362: }
363: else
364: {
365: return false;
366: }
367: }
368:
369: 370: 371: 372: 373:
374: public function countParents()
375: {
376: if(empty($this->oSql))
377: throw new RuntimeException("No db object set. Please use setDB() first");
378:
379: $this->checkCurrent();
380:
381: $parent = $this->oSql->query('SELECT COUNT(*)
382: FROM '.$this->tableName.'
383: WHERE `'.$this->leftBound.'`<'.$this->current['left'].'
384: AND `'.$this->rightBound.'` > '.$this->current['right'])
385: ->fetchColumn(0);
386: return $parent;
387: }
388:
389: 390: 391: 392: 393: 394:
395: public function getParents()
396: {
397: if(empty($this->oSql))
398: throw new RuntimeException("No db object set. Please use setDB() first");
399:
400: $this->checkCurrent();
401:
402: $parents = array();
403: $parent = $this->oSql->query('SELECT '.$this->id.','.$this->label.'
404: FROM '.$this->tableName.'
405: WHERE `'.$this->leftBound.'`<'.$this->current['left'].'
406: AND `'.$this->rightBound.'` > '.$this->current['right']);
407:
408: while($result = $parent->fetch(PDO::FETCH_ASSOC))
409: $parents[] = $result;
410:
411: return $parents;
412: }
413:
414: 415: 416: 417:
418: private function checkCurrent()
419: {
420: if(empty($this->current))
421: {
422: throw new Exception('Aucun élément courant n\'est défini');
423: }
424: }
425: }
426: ?>