dbnested.php
来自「视频监控网络部分的协议ddns,的模块的实现代码,请大家大胆指正.」· PHP 代码 · 共 969 行 · 第 1/3 页
PHP
969 行
<?php//// +----------------------------------------------------------------------+// | PHP Version 4 |// +----------------------------------------------------------------------+// | Copyright (c) 1997-2003 The PHP Group |// +----------------------------------------------------------------------+// | This source file is subject to version 2.02 of the PHP license, |// | that is bundled with this package in the file LICENSE, and is |// | available at through the world-wide-web at |// | http://www.php.net/license/2_02.txt. |// | If you did not receive a copy of the PHP license and are unable to |// | obtain it through the world-wide-web, please send a note to |// | license@php.net so we can mail you a copy immediately. |// +----------------------------------------------------------------------+// | Authors: |// +----------------------------------------------------------------------+//// $Id: DBnested.php,v 1.16 2003/03/04 18:57:18 cain Exp $require_once('Tree/Common.php');require_once('Tree/Error.php');/*** this class implements methods to work on a tree saved using the nested* tree model* explaination: http://research.calacademy.org/taf/proceedings/ballew/index.htm** @access public* @package Tree*/class Tree_Dynamic_DBnested extends Tree_Common// FIXXME should actually extend Tree_Common, to use the methods provided in there... but we need to connect// to the db here, so we extend optionsDB for now, may be use "aggregate" function to fix that{ var $debug = 0; var $options = array(// FIXXME to be implemented 'whereAddOn'=>'' // add on for the where clause, this string is simply added behind the WHERE in the select // so you better make sure its correct SQL :-), i.e. 'uid=3' // this is needed i.e. when you are saving many trees in one db-table ,'table' =>'' // // since the internal names are fixed, to be portable between different // DB tables with different column namings, we map the internal name to the real column name // using this array here, if it stays empty the internal names are used, which are: // id, left, right ,'columnNameMaps'=>array( //'id' => 'node_id', // use "node_id" as "id" 'left' => 'l' // since mysql at least doesnt support 'left' ... ,'right' => 'r' // ...as a column name we set default to the first letter only //'name' => 'nodeName' // ,'parentId' => 'parent' // parent id ) ,'order' => '' // needed for sorting the tree, currently only used in Memory_DBnested );// the defined methods here are proposals for the implementation,// they are named the same, as the methods in the "Memory" branch, if possible// it would be cool to keep the same naming and if the same parameters would be possible too// then it would be even better, so one could easily change from any kind of// tree-implementation to another, without changing the source code, only the setupXXX would need to be changed /** * * * @access public * @version 2002/03/02 * @author Wolfram Kriesing <wolfram@kriesing.de> * @param * @return */ function __construct( $dsn , $options=array() ) { Tree_Dynamic_DBnested( $dsn , $options ); } /** * * * @access public * @version 2002/03/02 * @author Wolfram Kriesing <wolfram@kriesing.de> * @param * @return */ function Tree_Dynamic_DBnested( $dsn , $options=array() ) { parent::Tree_OptionsDB( $dsn , $options ); // instanciate DB $this->table = $this->getOption('table'); } /** * add a new element to the tree * there are three ways to use this method * 1. * give only the $parentId and the $newValues will be inserted as the first child of this parent * i.e. // insert a new element under the parent with the ID=7 * $tree->add( array('name'=>'new element name') , 7 ); * 2. * give the $prevId ($parentId will be dismissed) and the new element * will be inserted in the tree after the element with the ID=$prevId * the parentId is not necessary because the prevId defines exactly where * the new element has to be place in the tree, and the parent is the same as * for the element with the ID=$prevId * i.e. // insert a new element after the element with the ID=5 * $tree->add( array('name'=>'new') , 0 , 5 ); * 3. * neither $parentId nor prevId is given, then the root element will be inserted * this requires that programmer is responsible to confirm this * this method does not (yet) check if there is already a root element saved !!! * * @access public * @author Wolfram Kriesing <wolfram@kriesing.de> * @param array $newValues this array contains the values that shall be inserted in the db-table * @param integer $parentId the id of the element which shall be the parent of the new element * @param integer $prevId the id of the element which shall preceed the one to be inserted * use either 'parentId' or 'prevId' * @return integer the ID of the element that had been inserted */ function add( $newValues , $parentId=0 , $prevId=0 ) { $lName = $this->_getColName('left'); $rName = $this->_getColName('right'); $prevVisited = 0; // check the DB-table if the columns which are given as keys // in the array $newValues do really exist, if not remove them from the array// FIXXME do the above described if( $parentId || $prevId ) // if no parent and no prevId is given the root shall be added { if( $prevId ) { $element = $this->getElement( $prevId ); $parentId = $element['parentId']; // we also need the parent id of the element, to write it in the db } else { $element = $this->getElement( $parentId ); } $newValues['parentId'] = $parentId; if( PEAR::isError($element) ) return $element; // get the "visited"-value where to add the new element behind // if $prevId is given, we need to use the right-value // if only the $parentId is given we need to use the left-value // look at it graphically, that made me understand it :-) // i.e. at: http://research.calacademy.org/taf/proceedings/ballew/sld034.htm $prevVisited = $prevId ? $element['right'] : $element['left'];// FIXXME start transaction here if( PEAR::isError($err=$this->_add( $prevVisited , 1 )) ) { // FIXXME rollback //$this->dbh->rollback(); return $err; } } // inserting _one_ new element in the tree $newData = array(); foreach( $newValues as $key=>$value ) // quote the values, as needed for the insert { $newData[$this->_getColName($key)] = $this->dbh->quote($value); } // set the proper right and left values $newData[$lName] = $prevVisited+1; $newData[$rName] = $prevVisited+2; // use sequences to create a new id in the db-table $nextId = $this->dbh->nextId($this->table); $query = sprintf( 'INSERT INTO %s (%s,%s) VALUES (%s,%s)', $this->table , $this->_getColName('id'), implode( "," , array_keys($newData) ) , $nextId, implode( "," , $newData ) ); if( DB::isError( $res = $this->dbh->query($query) ) ) {// rollback return $this->_throwError( $res->getMessage() , __LINE__ ); }// commit here return $nextId; } // end of function /** * this method only updates the left/right values of all the * elements that are affected by the insertion * be sure to set the parentId of the element(s) you insert * * @param int this parameter is not the ID!!! * it is the previous visit number, that means * if you are inserting a child, you need to use the left-value * of the parent * if you are inserting a "next" element, on the same level * you need to give the right value !! * @param int the number of elements you plan to insert * @return mixed either true on success or a Tree_Error on failure */ function _add( $prevVisited , $numberOfElements=1 ) { $lName = $this->_getColName('left'); $rName = $this->_getColName('right'); // update the elements which will be affected by the new insert $query = sprintf( 'UPDATE %s SET %s=%s+%s WHERE%s %s>%s', $this->table, $lName,$lName, $numberOfElements*2, $this->_getWhereAddOn(), $lName, $prevVisited ); if( DB::isError( $res = $this->dbh->query($query) ) ) {// FIXXME rollback return $this->_throwError( $res->getMessage() , __LINE__ ); } $query = sprintf( 'UPDATE %s SET %s=%s+%s WHERE%s %s>%s', $this->table, $rName,$rName, $numberOfElements*2, $this->_getWhereAddOn(), $rName, $prevVisited ); if( DB::isError( $res = $this->dbh->query($query) ) ) {// FIXXME rollback return $this->_throwError( $res->getMessage() , __LINE__ ); } return true; } /** * remove a tree element * this automatically remove all children and their children * if a node shall be removed that has children * * @access public * @author Wolfram Kriesing <wolfram@kriesing.de> * @param integer $id the id of the element to be removed * @return boolean returns either true or throws an error */ function remove( $id ) { $element = $this->getElement( $id ); if( PEAR::isError($element) ) return $element;// FIXXME start transaction //$this->dbh->autoCommit(false); $query = sprintf( 'DELETE FROM %s WHERE%s %s BETWEEN %s AND %s', $this->table, $this->_getWhereAddOn(), $this->_getColName('left'), $element['left'],$element['right']); if( DB::isError( $res = $this->dbh->query($query) ) ) {// FIXXME rollback //$this->dbh->rollback(); return $this->_throwError( $res->getMessage() , __LINE__ ); } if( PEAR::isError($err=$this->_remove( $element )) ) {// FIXXME rollback //$this->dbh->rollback(); return $err; } return true; } /** * removes a tree element, but only updates the left/right values * to make it seem as if the given element would not exist anymore * it doesnt remove the row(s) in the db itself! * * @see getElement() * @access private * @author Wolfram Kriesing <wolfram@kriesing.de> * @param array the entire element returned by "getElement" * @return boolean returns either true or throws an error */ function _remove( $element ) { $delta = $element['right'] - $element['left'] +1; $lName = $this->_getColName('left'); $rName = $this->_getColName('right'); // update the elements which will be affected by the remove $query = sprintf( "UPDATE %s SET %s=%s-$delta, %s=%s-$delta WHERE%s %s>%s", $this->table, $lName,$lName, $rName,$rName, $this->_getWhereAddOn(), $lName,$element['left'] ); if( DB::isError( $res = $this->dbh->query($query) ) ) { // the rollback shall be done by the method calling this one, since it is only private we can do that return $this->_throwError( $res->getMessage() , __LINE__ ); } $query = sprintf( "UPDATE %s SET %s=%s-$delta WHERE%s %s<%s AND %s>%s", $this->table, $rName,$rName, $this->_getWhereAddOn(), $lName,$element['left'], $rName,$element['right'] ); if( DB::isError( $res = $this->dbh->query($query) ) ) { // the rollback shall be done by the method calling this one, since it is only private return $this->_throwError( $res->getMessage() , __LINE__ ); }// FIXXME commit - should that not also be done in the method calling this one? like when an error occurs?
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?