root/trunk/lib/SGL/NestedSet.php

Revision 2425 (checked in by demian, 1 month ago)

replaced tabs with spaces

Line 
1 <?php
2 /* Reminder: always indent with 4 spaces (no tabs). */
3 // +---------------------------------------------------------------------------+
4 // | Copyright (c) 2006, Demian Turner                                         |
5 // | All rights reserved.                                                      |
6 // |                                                                           |
7 // | Redistribution and use in source and binary forms, with or without        |
8 // | modification, are permitted provided that the following conditions        |
9 // | are met:                                                                  |
10 // |                                                                           |
11 // | o Redistributions of source code must retain the above copyright          |
12 // |   notice, this list of conditions and the following disclaimer.           |
13 // | o Redistributions in binary form must reproduce the above copyright       |
14 // |   notice, this list of conditions and the following disclaimer in the     |
15 // |   documentation and/or other materials provided with the distribution.    |
16 // | o The names of the authors may not be used to endorse or promote          |
17 // |   products derived from this software without specific prior written      |
18 // |   permission.                                                             |
19 // |                                                                           |
20 // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       |
21 // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         |
22 // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR     |
23 // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT      |
24 // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,     |
25 // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT          |
26 // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     |
27 // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY     |
28 // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT       |
29 // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE     |
30 // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.      |
31 // |                                                                           |
32 // +---------------------------------------------------------------------------+
33 // | Seagull 0.6                                                               |
34 // +---------------------------------------------------------------------------+
35 // | NestedSet.php                                                             |
36 // +---------------------------------------------------------------------------+
37 // | Author:   Andy Crain <andy@newslogic.com>                                 |
38 // +---------------------------------------------------------------------------+
39 // $Id: NestedSet.php,v 1.10 2005/04/27 23:32:41 demian Exp $
40
41 /**
42  * A lightweight wrapper to PEAR DB_NestedSet that bypasses NestedSet for most
43  * methods, querying the specified nested set table directly via PEAR DB; more
44  * complex, write-heavy methods are left to DB_NestedSet.
45  *
46  * @package SGL
47  * @author  Andy Crain <andy@newslogic.com>
48  * @version $Revision: 1.10 $
49  */
50 class SGL_NestedSet
51 {
52    /**
53     * Array of images for addImages() to use to decorate results, relative to root.
54     *
55     * @var array
56     * @access private
57     */
58     var $_images = array(
59         'upArrow'       => 'images/16/move_up.gif',
60         'upArrowDead'   => 'images/16/move_up_dead.gif',
61         'downArrow'     => 'images/16/move_down.gif',
62         'downArrowDead' => 'images/16/move_down_dead.gif',
63         'folder'        => 'images/treeNav/foldericon.png',
64         'file'          => 'images/treeNav/file.png',
65         'blank'         => 'images/treeNav/blank.png',
66         't'             => 'images/treeNav/T.png',
67         'l'             => 'images/treeNav/L.png',
68         'i'             => 'images/treeNav/I.png');
69
70     var $_protectedFields = array('id', 'rootid', 'l', 'r', 'norder', 'level', 'parent');
71
72     var $params          = array('tableStructure' =>
73                                 array(
74                                     'id'     => 'id',
75                                     'rootid' => 'rootid',
76                                     'l'      => 'l',
77                                     'r'      => 'r',
78                                     'norder' => 'norder',
79                                     'level'  => 'level',
80                                     'parent' => 'parent'
81                                 ),
82                                 'tableName'      => '',
83                                 'lockTableName'  => '',
84                                 'sequenceName'   => '');
85
86     var $_aNodes = array();
87
88     function SGL_NestedSet($params)
89     {
90         SGL::logMessage(null, PEAR_LOG_DEBUG);
91
92         $this->dbh                = $this->_getDb();
93         $this->_params            = $params;
94         $this->_tableName         = $params['tableName'];
95         $this->_fieldsInternal    = array_flip($params['tableStructure']);
96         $this->_fieldListExternal = $this->_tableName . '.' . implode(",$this->_tableName.",$this->_fieldsInternal);
97     }
98
99     function &_getDb()
100     {
101         $locator = &SGL_ServiceLocator::singleton();
102         $dbh = $locator->get('DB');
103         if (!$dbh) {
104             $dbh = & SGL_DB::singleton();
105             $locator->register('DB', $dbh);
106         }
107         return $dbh;
108     }
109
110     function setImage($key, $value)
111     {
112         $this->_images[$key] = $value;
113     }
114
115
116     function setImages($imagesArray)
117     {
118         $this->_images = $imagesArray;
119     }
120
121     /**
122      * Fetches all root nodes (rootid = id) from the entire table/tree.
123      *
124      * @access public
125      * @param void
126      * @return array of node arrays
127      *
128      * @author  Andy Crain <andy@newslogic.com>
129      * @version 1.0
130      * @since   PHP 4.0
131      */
132     function getRoots($addSQL = array())
133     {
134         $cols     = $this->_addSQL($addSQL, 'cols');
135         $join     = $this->_addSQL($addSQL, 'join');
136         $groupBy  = $this->_addSQL($addSQL, 'groupBy');
137         $where    = $this->_addSQL($addSQL, 'where', 'AND');
138
139         //  Get root nodes (id = rootid).
140         $sql = "SELECT $this->_fieldListExternal $cols
141                 FROM $this->_tableName $join
142                 WHERE {$this->_tableName}.{$this->_fieldsInternal['id']} = {$this->_tableName}.{$this->_fieldsInternal['rootid']}
143                 $where $groupBy
144                 ORDER BY {$this->_tableName}.{$this->_fieldsInternal['norder']}";
145         $result =& $this->dbh->query($sql);
146         if (DB::isError($result)) {
147             SGL::raiseError('SQL problem', SGL_ERROR_DBFAILURE);
148             return;
149         }
150         $r = '';
151         while ($result->fetchInto($row, DB_FETCHMODE_ASSOC)){
152             $r[$row[$this->_fieldsInternal['id']]] = $row;
153         }
154         return $r;
155     }
156
157
158     /**
159      * Fetches all nodes from all roots, i.e., the entire table/tree.
160      *
161      * @access public
162      * @param void
163      * @return array of node arrays
164      *
165      * @author  Andy Crain <andy@newslogic.com>
166      * @version 1.0
167      * @since   PHP 4.0
168      */
169     function getTree($addSQL = array())
170     {
171         $cols     = $this->_addSQL($addSQL, 'cols');
172         $join     = $this->_addSQL($addSQL, 'join');
173         $groupBy  = $this->_addSQL($addSQL, 'groupBy');
174         $where    = $this->_addSQL($addSQL, 'where', 'AND');
175
176         $roots = $this->getRoots($addSQL);
177         //  Get all descendants of each node (rootid = $rootid).
178         $r = '';
179         if (is_array($roots) && count($roots)) {
180             foreach($roots as $id => $root) {
181                 $sql = "SELECT $this->_fieldListExternal $cols
182                         FROM $this->_tableName $join
183                         WHERE {$this->_tableName}.{$this->_fieldsInternal['rootid']} = {$root[$this->_fieldsInternal['rootid']]}
184                         $where $groupBy
185                         ORDER BY {$this->_tableName}.{$this->_fieldsInternal['l']}";
186                 $result =& $this->dbh->query($sql);
187                 if (DB::isError($result)) {
188                     SGL::raiseError('SQL problem', SGL_ERROR_DBFAILURE);
189                     return;
190                 }
191                 while ($result->fetchInto($row, DB_FETCHMODE_ASSOC)){
192                     $r[$row[$this->_fieldsInternal['id']]] = $row;
193                 }
194             }
195         }
196         return $r;
197     }
198
199     /**
200      * For any node whose id is supplied, fetches branch for that node (all
201      * nodes with same root_id), including that node, and those above and below it.
202      *
203      * @access public
204      * @param int $node_id
205      * @return array of node arrays
206      *
207      * @author  Andy Crain <andy@newslogic.com>
208      * @version 1.0
209      * @since   PHP 4.0
210      */
211     function getBranch($node_id, $addSQL = array())
212     {
213         $cols     = $this->_addSQL($addSQL, 'cols');
214         $join     = $this->_addSQL($addSQL, 'join');
215         $groupBy  = $this->_addSQL($addSQL, 'groupBy');
216         $where    = $this->_addSQL($addSQL, 'where', 'AND');
217
218         //  Get this node's rootnode, so we can then get nodes with matching rootid.
219         $node = $this->getNode($node_id);
220         $sql = "SELECT $this->_fieldListExternal $cols
221                 FROM $this->_tableName $join
222                 WHERE {$this->_tableName}.{$this->_fieldsInternal['rootid']} = {$node[$this->_fieldsInternal['rootid']]}
223                 $where $groupBy
224                 ORDER BY {$this->_tableName}.{$this->_fieldsInternal['l']}";
225         $result =& $this->dbh->query($sql);
226         if (DB::isError($result)) {
227             SGL::raiseError('SQL problem', SGL_ERROR_DBFAILURE);
228             return;
229         }
230         while ($result->fetchInto($row, DB_FETCHMODE_ASSOC)){
231             $r[$row[$this->_fieldsInternal['id']]] = $row;
232         }
233         return $r;
234     }
235
236     /**
237      * For any node, fetches all descendents (kids, grandkids, etc.)of that node (nodes with same
238      * rootid and with left ids bounded by this node's left and right id, and which aren't this node).
239      *
240      * @access public
241      * @param int $node_id
242      * @return array of node arrays
243      *
244      * @author  Andy Crain <andy@newslogic.com>
245      * @version 1.0
246      * @since   PHP 4.0
247      */
248     function getSubBranch($node_id, $addSQL = array())
249     {
250         $cols     = $this->_addSQL($addSQL, 'cols');
251         $join     = $this->_addSQL($addSQL, 'join');
252         $groupBy  = $this->_addSQL($addSQL, 'groupBy');
253         $where    = $this->_addSQL($addSQL, 'where', 'AND');
254
255         //  Get this node's rootnode, left and right nodes and use to fetch children.
256         $node = $this->getNode($node_id);
257         $sql = "SELECT $this->_fieldListExternal $cols
258                 FROM $this->_tableName $join
259                 WHERE {$this->_tableName}.{$this->_fieldsInternal['rootid']} = {$node[$this->_fieldsInternal['rootid']]}
260                 AND {$this->_tableName}.{$this->_fieldsInternal['l']} BETWEEN {$node[$this->_fieldsInternal['l']]} AND {$node[$this->_fieldsInternal['r']]}
261                 AND {$this->_tableName}.{$this->_fieldsInternal['id']} <> $node_id
262                 $where $groupBy
263                 ORDER BY {$this->_tableName}.{$this->_fieldsInternal['l']}";
264         $result =& $this->dbh->query($sql);
265         if (DB::isError($result)) {
266             SGL::raiseError('SQL problem', SGL_ERROR_DBFAILURE);
267             return;
268         }
269         $r = '';
270         while ($result->fetchInto($row, DB_FETCHMODE_ASSOC)){
271             $r[$row[$this->_fieldsInternal['id']]] = $row;
272         }
273         return $r;
274     }
275
276     function getParent($id)
277     {
278         $ns = $this->_getNestedSet();
279         return $ns->getParent($id);
280     }
281
282     /**
283      * For any node, fetches child nodes (nodes with same rootid, with level = parent
284      * level + 1, with l between parent's l and r).
285      *
286      * @access public
287      * @param int $node_id
288      * @return array of node arrays
289      *
290      * @author  Andy Crain <andy@newslogic.com>
291      * @version 1.0
292      * @since   PHP 4.0
293      */
294     function getChildren($node_id, $addSQL = array())
295     {
296         //  Get this node's rootnode, left and right nodes and use to fetch children.
297         $node = $this->getNode($node_id);
298         $cols     = $this->_addSQL($addSQL, 'cols');
299         $join     = $this->_addSQL($addSQL, 'join');
300         $groupBy  = $this->_addSQL($addSQL, 'groupBy');
301         $where    = $this->_addSQL($addSQL, 'where', 'AND');
302         $sql  = "SELECT $this->_fieldListExternal $cols
303                 FROM $this->_tableName $join
304                 WHERE {$this->_tableName}.{$this->_fieldsInternal['rootid']} = {$node[$this->_fieldsInternal['rootid']]}
305                 AND {$this->_tableName}.{$this->_fieldsInternal['l']} BETWEEN {$node[$this->_fieldsInternal['l']]} AND {$node[$this->_fieldsInternal['r']]}
306                 AND {$this->_tableName}.{$this->_fieldsInternal['level']} = {$node[$this->_fieldsInternal['level']]} + 1
307                 $where $groupBy
308                 ORDER BY {$this->_tableName}.{$this->_fieldsInternal['l']}";
309         $result =& $this->dbh->query($sql);
310         if (DB::isError($result)) {
311             SGL::raiseError('SQL problem', SGL_ERROR_DBFAILURE);
312             return;
313         }
314         $r = array();
315         while ($result->fetchInto($row, DB_FETCHMODE_ASSOC)) {
316             $r[$row[$this->_fieldsInternal['id']]] = $row;
317         }
318         return $r;
319     }
320
321     /**
322      * Borrowed from PEAR DB_NestedSet. Formats various sql clauses.
323      *
324      * @access private
325      * @param
326      * @return
327      *
328      * @author  Andy Crain <andy@newslogic.com>
329      * @version 1.0
330      * @since   PHP 4.0
331      */
332     function _addSQL($addSQL, $type, $prefix = false) {
333         if (!isset($addSQL[$type]) || $addSQL[$type] == '') {
334             return '';
335         }
336         switch ($type) {
337             case 'cols':
338                 return ', ' . $addSQL[$type];
339             case 'where':
340                 return $prefix . ' (' . $addSQL[$type] . ')';
341             default:
342                 return $addSQL[$type];
343         }
344     }
345
346
347     /**
348      * Returns the node indentified by supplied id, or false.
349      *
350      * @access private
351      * @param int $node_id
352      * @return array | false
353      *
354      * @author  Andy Crain <andy@newslogic.com>
355      * @version 1.0
356      * @since   PHP 4.0
357      */
358     function getNode($node_id)
359     {
360         if (!isset($this->_aNodes[$node_id])) {
361             $sql = "SELECT $this->_fieldListExternal
362                     FROM $this->_tableName
363                     WHERE {$this->_fieldsInternal['id']} = $node_id";
364             $result =& $this->dbh->query($sql);
365             if (DB::isError($result)) {
366                 SGL::raiseError('SQL problem', SGL_ERROR_DBFAILURE);
367                 return;
368             }
369             $result->fetchInto($this->_aNodes[$node_id], DB_FETCHMODE_ASSOC);
370             if (is_null($this->_aNodes[$node_id])) {
371                 $this->_aNodes[$node_id] = false;
372             }
373         }
374         return $this->_aNodes[$node_id];
375     }
376
377     /**
378      * Returns "breadcrumbs," a node's ancestral path through the tree: same rootid,
379      * and a level less than this node's, and a left node less than this node's, and
380      * a right node greater than this node's.
381      *
382      * @access public
383      * @param int $node_id
384      * @return array of node arrays
385      *
386      * @author  Andy Crain <andy@newslogic.com>
387      * @version 1.0
388      * @since   PHP 4.0
389      */
390     function getBreadcrumbs($node_id, $addSQL = array(), $includeCurrentNode = false)
391     {
392         $cols     = $this->_addSQL($addSQL, 'cols');
393         $join     = $this->_addSQL($addSQL, 'join');
394         $groupBy  = $this->_addSQL($addSQL, 'groupBy');
395         $where    = $this->_addSQL($addSQL, 'where', 'AND');
396
397         $node = $this->getNode($node_id);
398         if ($includeCurrentNode) {
399             $sql = "SELECT $this->_fieldListExternal $cols
400                     FROM $this->_tableName $join
401                     WHERE {$this->_tableName}.{$this->_fieldsInternal['level']} <= {$node[$this->_fieldsInternal['level']]}
402                     AND {$this->_tableName}.{$this->_fieldsInternal['l']} <= {$node[$this->_fieldsInternal['l']]}
403                     AND {$this->_tableName}.{$this->_fieldsInternal['r']} >= {$node[$this->_fieldsInternal['r']]}
404                     AND {$this->_tableName}.{$this->_fieldsInternal['rootid']} = {$node[$this->_fieldsInternal['rootid']]}
405                     $where $groupBy
406                     ORDER BY {$this->_tableName}.{$this->_fieldsInternal['l']}";
407         } else {
408             $sql = "SELECT $this->_fieldListExternal $cols
409                     FROM $this->_tableName $join
410                     WHERE {$this->_tableName}.{$this->_fieldsInternal['level']} < {$node[$this->_fieldsInternal['level']]}
411                     AND {$this->_tableName}.{$this->_fieldsInternal['l']} < {$node[$this->_fieldsInternal['l']]}
412                     AND {$this->_tableName}.{$this->_fieldsInternal['r']} > {$node[$this->_fieldsInternal['r']]}
413                     AND {$this->_tableName}.{$this->_fieldsInternal['rootid']} = {$node[$this->_fieldsInternal['rootid']]}
414                     $where $groupBy
415                     ORDER BY {$this->_fieldsInternal['l']}";
416         }
417         $result =& $this->dbh->query($sql);
418         if (DB::isError($result)) {
419             SGL::raiseError('SQL problem', SGL_ERROR_DBFAILURE);
420             return;
421         }
422         $r = array();
423         while ($result->fetchInto($row, DB_FETCHMODE_ASSOC)){
424             $r[$row[$this->_fieldsInternal['id']]] = $row;
425         }
426         return $r;
427     }
428
429     /**
430      * Takes a reference to a nodes array such as returned by getTree() et. al. and adds to
431      * each node array an array of images that can be used to build a MS Windows Explorer-like
432      * tree view, with images for file, folder, blank, and I, L, and T shapes, as well as up
433      * and down arrows that are grayed out when the node cannot be moved up or down (has no
434      * siblings above or below it).
435      *
436      * @access public
437      * @param array $aNodes
438      * @return void
439      *
440      * @author  Andy Crain <andy@newslogic.com>
441      * @version 1.0
442      * @since   PHP 4.0
443      */
444     function addImages(&$aNodes)
445     {
446         //  Build sorted array $nodeMap so we can easily test whether a node has
447         //  siblings (same parent, same level) above or below it (order-1, order+1).
448         if (is_array($aNodes) && count($aNodes)) {
449             if (!isset($nodeMap)) {
450                 $level  = $this->_fieldsInternal['level'];
451                 $parent = $this->_fieldsInternal['parent'];
452                 $order  = $this->_fieldsInternal['norder'];
453                 foreach($aNodes as $k => $n){
454                     $nodeMap[$n[$level]][$n[$parent]][$n[$order]] = $k;
455                 }
456             }
457             reset($aNodes);
458             while (list($id,$node) = each($aNodes)){
459                 //  If inside subtree, use I shape, else blank (for level 2 and above only,
460                 //  since rightmost 2 images are the L/T shape then folder/file).
461                 for ($i=2; $i < $node[$this->_fieldsInternal['level']]; $i++){
462                     if (isset($insideNest[$i]) && $insideNest[$i] == true) {
463                         $aNodes[$id]['images']['treePad'][] = $this->_images['i'];
464                     } else {
465                         $aNodes[$id]['images']['treePad'][] = $this->_images['blank'];
466                     }
467                 }
468                 //  Only nodes with parents get T and L shapes
469                 if ($node[$this->_fieldsInternal['l']] > 1){
470                     //  use L shape for terminal node, T shape for intermediate node
471                     if ($node[$this->_fieldsInternal['r']] == $aNodes[$node[$this->_fieldsInternal['parent']]][$this->_fieldsInternal['r']] - 1){//terminal node
472                         $aNodes[$id]['images']['treePad'][] = $this->_images['l'];
473                         $insideNest[$node[$this->_fieldsInternal['level']]] = false;
474                     } else {
475                         $aNodes[$id]['images']['treePad'][] = $this->_images['t'];
476                         $insideNest[$node[$this->_fieldsInternal['level']]] = true;
477                     }
478                 }
479                 //  Finally, add folder icon.
480                 $aNodes[$id]['images']['treePad'][] = $this->_images['folder'];
481
482                 //  Using $nodeMap, assign up/down arrows and the target node id above or below
483                 //  which to move the node.
484                 if (isset($nodeMap[$node[$this->_fieldsInternal['level']]][$node[$this->_fieldsInternal['parent']]][$node[$this->_fieldsInternal['norder']] - 1]) ) {
485                     $aNodes[$id]['images']['moveUpImg']    = $this->_images['upArrow'];
486                     $aNodes[$id]['images']['moveUpTarget'] = $nodeMap[$node[$this->_fieldsInternal['level']]][$node[$this->_fieldsInternal['parent']]][$node[$this->_fieldsInternal['norder']] - 1];
487                 } else {
488                     $aNodes[$id]['images']['moveUpImg']    = $this->_images['upArrowDead'];
489                     $aNodes[$id]['images']['moveUpTarget'] = 0;
490                 }
491                 if (isset($nodeMap[$node[$this->_fieldsInternal['level']]][$node[$this->_fieldsInternal['parent']]][$node[$this->_fieldsInternal['norder']] + 1]) ) {
492                     $aNodes[$id]['images']['moveDownImg']    = $this->_images['downArrow'];
493                     $aNodes[$id]['images']['moveDownTarget'] = $nodeMap[$node[$this->_fieldsInternal['level']]][$node[$this->_fieldsInternal['parent']]][$node[$this->_fieldsInternal['norder']] + 1];
494                 } else {
495                     $aNodes[$id]['images']['moveDownImg']    = $this->_images['downArrowDead'];
496                     $aNodes[$id]['images']['moveDownTarget'] = 0;
497                 }
498             }
499         }
500     }
501
502     /**
503      * Updates defined fields (any field defined in params['tableStructure'] that is not
504      * a NestedSet field such as l, r, rootid, norder, etc. (i.e. not in _protectedFields).
505      *
506      * @access public
507      * @param int $id
508      * @param array $values (field name => field value)
509      * @return DB_OK | PEAR Error
510      *
511      * @author  Andy Crain <andy@newslogic.com>
512      * @version 1.0
513      * @since   PHP 4.0
514      */
515     function updateNode($id, $values)
516     {
517         $this->_cleanMemoryCache($id);
518
519         $set = implode(', ', $this->prepareValues($values, true));
520         $sql = "UPDATE $this->_tableName
521                 SET $set
522                 WHERE {$this->_fieldsInternal['id']} = $id";
523         $result =& $this->dbh->query($sql);
524         if (DB::isError($result)) {
525             SGL::raiseError('SQL problem', SGL_ERROR_DBFAILURE);
526             return;
527         }
528         return $result;
529     }
530
531     /**
532      * Prepares $values array for insert or update into table. Called
533      * with $quoteValues=true to build quoted key=value strings for update sql.
534      * Called externally with $quoteValues=false to simply weed out of an
535      * array any fields that either are not in the table or that are but are internal.
536      *
537      * @access private
538      * @param
539      * @return object DB_NestedSet_DB
540      *
541      * @author  Andy Crain <andy@newslogic.com>
542      * @version 1.0
543      * @since   PHP 4.0
544      */
545     function prepareValues($values, $quoteValues = false)
546     {
547         $r = array();
548         if ($quoteValues) {
549             foreach ($values as $k => $value) {
550                 $internalField = array_search($k,$this->_fieldsInternal);
551                 if ($internalField && !in_array($internalField,$this->_protectedFields)) {
552                     $k     = $this->dbh->quoteIdentifier($k);
553                     $value = $this->dbh->quoteSmart($value);
554                     $r[] = "$k = $value";
555                 }
556             }
557         } else {
558             foreach ($values as $k => $value) {
559                 $internalField = array_search($k,$this->_fieldsInternal);
560                 if ($internalField && !in_array($internalField,$this->_protectedFields)) {
561                     $r[$k] = $value;
562                 }
563             }
564         }
565         return $r;
566     }
567
568     /**
569      * DB_NestedSet_DB factory. Called only by wrapper methods.
570      * Several methods (createRootNode() and moveTree()) when called from
571      * DB_NestedSet directly take constants as parameters, but since DB_NestedSet
572      * isn't instantiated at call time, we pass the constants' values instead.
573      * Values for $pos are: 'BE' (move node before target norder),
574      * 'AF' (move node after target norder), or 'SUB' (move node beneath, as a child
575      * to target). See PEAR DB_NestedSet docs for api info on wrapper methods.
576      *
577      * @access private
578      * @param
579      * @return object DB_NestedSet_DB
580      *
581      * @author  Andy Crain <andy@newslogic.com>
582      */
583     function _getNestedSet()
584     {
585         static $ns;
586         if (is_null($ns)) {
587             require_once 'DB/NestedSet.php';
588             //  create an instance of DB_NestedSet_DB
589             $ns = & DB_NestedSet::factory('DB', SGL_DB::getDsn(), $this->_params['tableStructure']);
590             if (is_a($ns, 'PEAR_Error')) {
591                 echo $ns->getCode() . ': ' . $ns->getMessage();
592             }
593             $ns->setAttr(array(
594                     'node_table' => $this->_tableName,
595                     'lock_table' => $this->_params['lockTableName'],
596                     'sequence_table' => $this->_params['sequenceName'])
597             );
598         }
599         return $ns;
600     }
601
602     /**
603      * The following are all just wrappers to DB_NestedSet, an instance of which is
604      * returned by _getNestedSet(). See PEAR/DB/NestedSet.php for API docs.
605      */
606
607     function createRootNode($values, $id = false, $first = false, $pos = 'AF')
608     {
609         $ns = $this->_getNestedSet();
610         return $ns->createRootNode($values, $id, $first, $pos);
611     }
612
613     function createSubNode($id, $values)
614     {
615         $this->_cleanMemoryCache($id);
616
617         $ns = $this->_getNestedSet();
618         return $ns->createSubNode($id, $values);
619     }
620
621     function moveTree($id, $targetid, $pos, $copy = false)
622     {
623         $this->_cleanMemoryCache();
624
625         $ns = $this->_getNestedSet();
626         return $ns->moveTree($id, $targetid, $pos, $copy);
627     }
628
629     function deleteNode($id)
630     {
631         $this->_cleanMemoryCache($id);
632
633         $ns = $this->_getNestedSet();
634         return $ns->deleteNode($id);
635     }
636
637     /**
638      * Clean nodes stored in memory
639      * @param integer node id [optional]
640      * @access private
641      */
642     function _cleanMemoryCache($id = null)
643     {
644         if (is_null($id)) {
645             //clear all nodes
646             $this->_aNodes = array();
647         } elseif (array_key_exists($id, $this->_aNodes)) {
648             //clear the given node
649             unset($this->_aNodes[$id]);
650         }
651     }
652 }
653 ?>
Note: See TracBrowser for help on using the browser.