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 + -
显示快捷键?