DBsimple.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  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: Wolfram Kriesing <wolfram@kriesing.de> |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: DBsimple.php,v 1.11 2003/01/18 15:36:50 cain Exp $
  20. require_once('Tree/OptionsDB.php');
  21. require_once('Tree/Error.php');
  22. /**
  23. * the DB interface to the tree class
  24. *
  25. * @access public
  26. * @author Wolfram Kriesing <wolfram@kriesing.de>
  27. * @version 2001/06/27
  28. * @package Tree
  29. */
  30. class Tree_Memory_DBsimple extends Tree_OptionsDB
  31. // FIXXME should actually extend Tree_Common, to use the methods provided in there... but we need to connect
  32. // to the db here, so we extend optionsDB for now, may be use "aggregate" function to fix that
  33. {
  34. /**
  35. * @access public
  36. * @var array saves the options passed to the constructor
  37. */
  38. var $options = array( 'order' =>'', // which column to order by when reading the data from the DB, this sorts the data even inside every level
  39. 'whereAddOn'=>'', // add on for the where clause, this string is simply added behind the WHERE in the select
  40. // so you better make sure its correct SQL :-), i.e. 'uid=3'
  41. // this is needed i.e. when you are saving many trees for different user
  42. // in one table where each entry has a uid (user id)
  43. 'table' =>'', //
  44. // the column-name maps are used for the "as" in the select queries
  45. // so you can use any column name in the table and "map" it to the name that shall be used in the
  46. // internal array, that is built, see the examples (in comments)
  47. 'columnNameMaps'=>array(
  48. /* 'id' => 'tree_id', // use "tree_id" as "id"
  49. 'parentId' => 'parent_id',
  50. 'prevId' => 'previous_id',
  51. 'name' => 'nodeName'
  52. */
  53. ),
  54. );
  55. /**
  56. * @access public
  57. * @var string the table where to read the tree data from
  58. * can also be set using the DSN in the constructor
  59. */
  60. var $table;
  61. /**
  62. * @access private
  63. * @var object $dbh the handle to the DB-object
  64. */
  65. // var $dbh;
  66. /**
  67. * set up this object
  68. *
  69. * @version 2001/06/27
  70. * @access public
  71. * @author Wolfram Kriesing <wolfram@kriesing.de>
  72. * @param string $dsn this is a DSN of the for that PEAR::DB uses it
  73. * only that additionally you can add parameters like ...?table=test_table
  74. * to define the table it shall work on
  75. * @param array $options additional options you can set
  76. */
  77. function Tree_Memory_DBsimple( $dsn , $options=array() )
  78. {
  79. $this->Tree_OptionsDB( $dsn , $options ); // instanciate DB
  80. if( is_string($options) ) // just to be backward compatible, or to make the second paramter shorter
  81. {
  82. $this->setOption( 'order' , $options );
  83. }
  84. $this->table = $this->getOption('table');
  85. } // end of function
  86. /**
  87. * retreive all the navigation data from the db and call build to build the
  88. * tree in the array data and structure
  89. *
  90. * @version 2001/11/20
  91. * @access public
  92. * @author Wolfram Kriesing <wolfram@kriesing.de>
  93. * @return boolean true on success
  94. */
  95. function setup()
  96. {
  97. // TODO sort by prevId (parentId,prevId $addQuery) too if it exists in the table, or the root might be wrong
  98. // TODO since the prevId of the root should be 0
  99. //
  100. $whereAddOn = '';
  101. if( $this->options['whereAddOn'] )
  102. {
  103. $whereAddOn = 'WHERE '.$this->getOption('whereAddOn');
  104. }
  105. //
  106. $orderBy = '';
  107. if( $this->options['order'] )
  108. {
  109. $orderBy = ",".$this->options['order'];
  110. }
  111. $map = $this->getOption('columnNameMaps');
  112. if( isset($map['parentId']) )
  113. {
  114. $orderBy = $map['parentId'].$orderBy;
  115. }
  116. else
  117. {
  118. $orderBy = 'parentId'.$orderBy;
  119. }
  120. // build the query this way, that the root, which has no parent (parentId=0)
  121. // and no previous (prevId=0) is in first place (in case prevId is given)
  122. $query = sprintf( "SELECT * FROM %s %s ORDER BY %s",
  123. $this->table,
  124. $whereAddOn,
  125. $orderBy); //,prevId !!!!
  126. if( DB::isError( $res = $this->dbh->getAll( $query ) ) )
  127. {
  128. // FIXXME remove print use debug mode instead
  129. printf("ERROR - Tree::setup - %s - %s<br>",DB::errormessage($res),$query);
  130. return $this->_throwError($res->getMessage(),__LINE__);
  131. }
  132. // if the db-column names need to be mapped to different names
  133. // FIXXME somehow we should be able to do this in the query, but i dont know how to select
  134. // only those columns, use "as" on them and select the rest, without getting those columns again :-(
  135. if( $map )
  136. foreach( $res as $id=>$aResult ) // map each result
  137. {
  138. foreach( $map as $key=>$columnName )
  139. {
  140. $res[$id][$key] = $res[$id][$columnName];
  141. unset($res[$id][$columnName]);
  142. }
  143. }
  144. return $res;
  145. }
  146. /**
  147. * adds _one_ new element in the tree under the given parent
  148. * the values' keys given have to match the db-columns, because the
  149. * value gets inserted in the db directly
  150. * to add an entire node containing children and so on see 'addNode()'
  151. *
  152. * to ba compatible, to the DBnested u can also give the parent and previd as the second and third parameter
  153. *
  154. * @see addNode()
  155. * @version 2001/10/09
  156. * @access public
  157. * @author Wolfram Kriesing <wolfram@kriesing.de>
  158. * @param array $newValues this array contains the values that shall be inserted in the db-table
  159. * the key for each element is the name of the column
  160. * @return mixed either boolean false on failure or the id of the inserted row
  161. */
  162. function add( $newValues , $parentId=0 )
  163. {
  164. // FIXXME use $this->dbh->tableInfo to check which columns exist
  165. // so only data for which a column exist is inserted
  166. if( $parentId )
  167. $newValues['parentId'] = $parentId;
  168. $newData = array();
  169. foreach( $newValues as $key=>$value ) // quote the values, as needed for the insert
  170. {
  171. $newData[$this->_getColName($key)] = $this->dbh->quote($value);
  172. }
  173. // use sequences to create a new id in the db-table
  174. $nextId = $this->dbh->nextId($this->table);
  175. $query = sprintf("INSERT INTO %s (%s,%s) VALUES (%s,%s)",
  176. $this->table ,
  177. $this->_getColName('id'),
  178. implode( ',' , array_keys($newData) ) ,
  179. $nextId,
  180. implode( ',' , $newData ) );
  181. if( DB::isError( $res = $this->dbh->query( $query ) ) )
  182. {
  183. // TODO raise PEAR error
  184. printf("ERROR - Tree::add - %s - %s<br>",DB::errormessage($res),$query);
  185. return false;
  186. }
  187. return $nextId;
  188. } // end of function
  189. /**
  190. * removes the given node
  191. *
  192. * @version 2001/10/09
  193. * @access public
  194. * @author Wolfram Kriesing <wolfram@kriesing.de>
  195. * @param mixed $id the id of the node to be removed, or an array of id's to be removed
  196. * @return boolean true on success
  197. */
  198. function remove( $id )
  199. {
  200. // if the one to remove has children, get their id's to remove them too
  201. if( $this->hasChildren($id) )
  202. $id = $this->walk( array('_remove',$this) , $id , 'array' );
  203. $idColumnName = 'id';
  204. $map = $this->getOption('columnNameMaps');
  205. if( isset($map['id']) ) // if there are maps given
  206. {
  207. $idColumnName = $map['id'];
  208. }
  209. $whereClause = "WHERE $idColumnName=$id";
  210. if( is_array($id) )
  211. {
  212. $whereClause = "WHERE $idColumnName in (".implode( ',' , $id ).')';
  213. }
  214. $query = "DELETE FROM {$this->table} $whereClause";
  215. //print("<br>".$query);
  216. if( DB::isError( $res = $this->dbh->query( $query ) ) )
  217. {
  218. // TODO raise PEAR error
  219. printf("ERROR - Tree::remove - %s - %s<br>",DB::errormessage($res),$query);
  220. return false;
  221. }
  222. // TODO if remove succeeded set prevId of the following element properly
  223. return true;
  224. } // end of function
  225. /**
  226. * move an entry under a given parent or behind a given entry
  227. *
  228. * @version 2001/10/10
  229. * @access public
  230. * @author Wolfram Kriesing <wolfram@kriesing.de>
  231. * @param integer $idToMove the id of the element that shall be moved
  232. * @param integer $newParentId the id of the element which will be the new parent
  233. * @param integer $newPrevId if prevId is given the element with the id idToMove
  234. * shall be moved _behind_ the element with id=prevId
  235. * if it is 0 it will be put at the beginning
  236. * if no prevId is in the DB it can be 0 too and won't bother
  237. * since it is not written in the DB anyway
  238. * @return boolean true for success
  239. */
  240. function move( $idToMove , $newParentId , $newPrevId=0 )
  241. {
  242. $idColumnName = 'id';
  243. $parentIdColumnName = 'parentId';
  244. $map = $this->getOption('columnNameMaps');
  245. if( isset($map['id']) )
  246. $idColumnName = $map['id'];
  247. if( isset($map['parentId']) )
  248. $parentIdColumnName = $map['parentId'];
  249. // FIXXME todo: previous stuff
  250. // set the parent in the DB
  251. $query = "UPDATE $this->table SET $parentIdColumnName=$newParentId WHERE $idColumnName=$idToMove";
  252. //print($query);
  253. if( DB::isError( $res = $this->dbh->query( $query ) ) )
  254. {
  255. // TODO raise PEAR error
  256. printf("ERROR - Tree::move - %s - %s<br>",DB::errormessage($res),$query);
  257. return false;
  258. }
  259. // FIXXME update the prevId's of the elements where the element was moved away from and moved in
  260. return true;
  261. } // end of function
  262. /**
  263. * update an element in the DB
  264. *
  265. * @version 2002/01/17
  266. * @access public
  267. * @author Wolfram Kriesing <wolfram@kriesing.de>
  268. * @param array $newData all the new data, the key 'id' is used to
  269. * build the 'WHERE id=' clause and all the other
  270. * elements are the data to fill in the DB
  271. * @return boolean true for success
  272. */
  273. function update( $id , $newData )
  274. {
  275. // FIXXME check $this->dbh->tableInfo to see if all the columns that shall be updated
  276. // really exist, this will also extract nextId etc. if given before writing it in the DB
  277. // in case they dont exist in the DB
  278. $setData = array();
  279. foreach( $newData as $key=>$value ) // quote the values, as needed for the insert
  280. {
  281. $setData[] = $this->_getColName($key).'='.$this->dbh->quote($value);
  282. }
  283. $query = sprintf( 'UPDATE %s SET %s WHERE %s=%s',
  284. $this->table,
  285. implode( ',' , $setData ),
  286. $this->_getColName('id'),
  287. $id
  288. );
  289. if( DB::isError( $res=$this->dbh->query($query) ) )
  290. {
  291. // FIXXME raise PEAR error
  292. printf("ERROR - Tree::update - %s - %s<br>",DB::errormessage($res),$query);
  293. return false;
  294. }
  295. return true;
  296. } // end of function
  297. /**
  298. *
  299. *
  300. * @access private
  301. * @version 2002/03/02
  302. * @author Wolfram Kriesing <wolfram@kriesing.de>
  303. * @param
  304. * @return
  305. */
  306. function _throwError( $msg , $line , $mode=null )
  307. {
  308. return new Tree_Error( $msg , $line , __FILE__ , $mode , $this->db->last_query );
  309. }
  310. /**
  311. * prepare multiple results
  312. *
  313. * @see _prepareResult()
  314. * @access private
  315. * @version 2002/03/03
  316. * @author Wolfram Kriesing <wolfram@kriesing.de>
  317. * @param
  318. * @return
  319. */
  320. function _prepareResults( $results )
  321. {
  322. $newResults = array();
  323. foreach( $results as $aResult )
  324. $newResults[] = $this->_prepareResult($aResult);
  325. return $newResults;
  326. }
  327. /**
  328. * map back the index names to get what is expected
  329. *
  330. * @access private
  331. * @version 2002/03/03
  332. * @author Wolfram Kriesing <wolfram@kriesing.de>
  333. * @param
  334. * @return
  335. */
  336. function _prepareResult( $result )
  337. {
  338. $map = $this->getOption('columnNameMaps');
  339. if( $map )
  340. foreach( $map as $key=>$columnName )
  341. {
  342. $result[$key] = $result[$columnName];
  343. unset($result[$columnName]);
  344. }
  345. return $result;
  346. }
  347. /**
  348. * this method retreives the real column name, as used in the DB
  349. * since the internal names are fixed, to be portable between different
  350. * DB-column namings, we map the internal name to the real column name here
  351. *
  352. * @access private
  353. * @version 2002/03/02
  354. * @author Wolfram Kriesing <wolfram@kriesing.de>
  355. * @param
  356. * @return
  357. */
  358. function _getColName( $internalName )
  359. {
  360. if( $map = $this->getOption( 'columnNameMaps' ) )
  361. {
  362. if( isset($map[$internalName]) )
  363. return $map[$internalName];
  364. }
  365. return $internalName;
  366. }
  367. } // end of class
  368. ?>