PageRenderTime 54ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/main/class/class_sql_interval.php

http://github.com/FSB/Fire-Soft-Board-2
PHP | 370 lines | 222 code | 39 blank | 109 comment | 19 complexity | ee54bdc7dd1624747033daa5a07003bb MD5 | raw file
  1. <?php
  2. /**
  3. * Fire-Soft-Board version 2
  4. *
  5. * @package FSB2
  6. * @author Genova <genova@fire-soft-board.com>
  7. * @version $Id$
  8. * @license http://opensource.org/licenses/gpl-2.0.php GNU GPL 2
  9. */
  10. /**
  11. * Permet une gestion d'abres par representation intervallaire.
  12. *
  13. * Si vous souhaitez implementer une table gerant la representation intervallaire, vous devez disposer
  14. * des champs suivants :
  15. * (int) f_id :: ID de la feuille
  16. * (int) f_cat_id :: ID du parent le plus haut (categorie)
  17. * (int) f_level :: Niveau actuel dans l'arbre
  18. * (int) f_parent :: ID du parent
  19. * (int) f_left :: Borne gauche
  20. * (int) f_right Borne droite
  21. *
  22. * @link http://sqlpro.developpez.com/cours/arborescence/
  23. */
  24. class Sql_interval extends Fsb_model
  25. {
  26. /**
  27. * Interval de deplacement des feuilles temporaires
  28. */
  29. const MOVE = 1000000;
  30. /**
  31. * Ajoute un element dans l'arbre
  32. *
  33. * @param int $parent Element parent
  34. * @param array $data Informations a inserer dans la table
  35. * @param string $table Table concernee
  36. * @return int ID de l'element cree
  37. */
  38. public static function put($parent, $data, $table = 'forums')
  39. {
  40. // On recupere la bordure droite du parent
  41. $sql = 'SELECT f_cat_id, f_right, f_level
  42. FROM ' . SQL_PREFIX . $table . '
  43. WHERE f_id = ' . $parent;
  44. $result = Fsb::$db->query($sql);
  45. $parent_data = Fsb::$db->row($result);
  46. Fsb::$db->free($result);
  47. // Si aucun parent n'a ete trouve on place la feuille a droite
  48. if (!$parent_data)
  49. {
  50. $sql = 'SELECT MAX(f_right) AS max
  51. FROM ' . SQL_PREFIX . $table;
  52. $max = Fsb::$db->get($sql, 'max');
  53. $parent_data = array(
  54. 'f_right' => $max + 1,
  55. 'f_parent' => 0,
  56. 'f_level' => -1,
  57. 'f_cat_id' => 0,
  58. );
  59. }
  60. // On decale les bordures
  61. Fsb::$db->update($table, array(
  62. 'f_left' => array('f_left + 2', 'is_field' => true),
  63. ), 'WHERE f_left >= ' . $parent_data['f_right']);
  64. Fsb::$db->update($table, array(
  65. 'f_right' => array('f_right + 2', 'is_field' => true),
  66. ), 'WHERE f_right >= ' . $parent_data['f_right']);
  67. // Insertion du nouvel element
  68. Fsb::$db->insert($table, array_merge($data, array(
  69. 'f_parent' => $parent,
  70. 'f_left' => $parent_data['f_right'],
  71. 'f_right' => $parent_data['f_right'] + 1,
  72. 'f_level' => $parent_data['f_level'] + 1,
  73. 'f_cat_id' => $parent_data['f_cat_id'],
  74. )));
  75. $last_id = Fsb::$db->last_id();
  76. // S'il $parent vaut 0, on met a jour son ID de categorie
  77. if ($parent_data['f_cat_id'] == 0)
  78. {
  79. Fsb::$db->update($table, array(
  80. 'f_cat_id' => $last_id,
  81. ), 'WHERE f_id = ' . $last_id);
  82. }
  83. return ($last_id);
  84. }
  85. /**
  86. * Met a jour un element de l'interval
  87. *
  88. * @param int $f_id ID de la feuille courante
  89. * @param int $parent Parent de la feuille
  90. * @param array $data Donnees de la feuille
  91. * @param string $table Table concernee
  92. * @return bool True si le parent a ete modifie
  93. */
  94. public static function update($f_id, $parent, $data, $table = 'forums')
  95. {
  96. // On demare une transaction SQL
  97. Fsb::$db->transaction('begin');
  98. // Donnees du forum actuel
  99. $sql = 'SELECT f_cat_id, f_parent, f_left, f_right, f_level
  100. FROM ' . SQL_PREFIX . $table . '
  101. WHERE f_id = ' . $f_id;
  102. $current = Fsb::$db->request($sql);
  103. if ($parent != $current['f_parent'])
  104. {
  105. // On change le forum actuel d'ID, c'est parti pour tout redecaler comme il faut ..
  106. // On commence par recuperer les donnees du nouveau parent
  107. $sql = 'SELECT f_left, f_right, f_cat_id, f_level
  108. FROM ' . SQL_PREFIX . $table . '
  109. WHERE f_id = ' . $parent;
  110. $parent_data = Fsb::$db->request($sql);
  111. // L'interval est l'ecart entre les deux bornes des forums deplaces
  112. $interval = $current['f_right'] - $current['f_left'] + 1;
  113. // On decale les bornes de la feuille actuelle de Sql_interval::MOVE, afin de le placer en zone
  114. // temporaire et de ne pas entrer en conflit.
  115. Fsb::$db->update($table, array(
  116. 'f_left' => array('f_left + ' . Sql_interval::MOVE, 'is_field' => true),
  117. 'f_right' => array('f_right + ' . Sql_interval::MOVE, 'is_field' => true),
  118. ), 'WHERE f_left >= ' . $current['f_left'] . ' AND f_right <= ' . $current['f_right']);
  119. // Si on deplace le noeud vers la gauche ..
  120. if ($current['f_left'] > $parent_data['f_right'])
  121. {
  122. // On decale les feuilles situees entre la borne droite du parent - 1, et
  123. // la borne gauche de la feuille deplacee.
  124. Fsb::$db->update($table, array(
  125. 'f_left' => array('f_left + ' . $interval, 'is_field' => true),
  126. ), 'WHERE f_left > ' . ($parent_data['f_right'] - 1) . ' AND f_left < ' . $current['f_left']);
  127. Fsb::$db->update($table, array(
  128. 'f_right' => array('f_right + ' . $interval, 'is_field' => true),
  129. ), 'WHERE f_right > ' . ($parent_data['f_right'] - 1) . ' AND f_right < ' . $current['f_left']);
  130. $new_interval = Sql_interval::MOVE + (($current['f_left'] - $parent_data['f_right']));
  131. }
  132. // .. sinon deplacement vers la droite.
  133. else
  134. {
  135. // On decale les feuilles situees entre la borne droite du parent - 1, et
  136. // la borne gauche de la feuille deplacee.
  137. Fsb::$db->update($table, array(
  138. 'f_left' => array('f_left - ' . $interval, 'is_field' => true),
  139. ), 'WHERE f_left > ' . $current['f_right'] . ' AND f_left < ' . $parent_data['f_right']);
  140. Fsb::$db->update($table, array(
  141. 'f_right' => array('f_right - ' . $interval, 'is_field' => true),
  142. ), 'WHERE f_right > ' . $current['f_right'] . ' AND f_right < ' . $parent_data['f_right']);
  143. $new_interval = Sql_interval::MOVE - (($parent_data['f_right'] - 1 - $current['f_right']));
  144. }
  145. // On modifie l'interval et les donnees des feuilles deplacees
  146. Fsb::$db->update($table, array(
  147. 'f_left' => array('f_left - ' . $new_interval, 'is_field' => true),
  148. 'f_right' => array('f_right - ' . $new_interval, 'is_field' => true),
  149. 'f_level' => array('f_level - ' . ($current['f_level'] - $parent_data['f_level'] - 1), 'is_field' => true),
  150. 'f_cat_id' => $parent_data['f_cat_id'],
  151. ), 'WHERE f_left > ' . Sql_interval::MOVE);
  152. $data['f_parent'] = $parent;
  153. }
  154. // Mise a jour de la feuille
  155. Fsb::$db->update($table, $data, 'WHERE f_id = ' . $f_id);
  156. // On termine la transaction
  157. Fsb::$db->transaction('commit');
  158. return (($parent != $current['f_parent']) ? true : false);
  159. }
  160. /**
  161. * Supprime une feuille de l'arbre
  162. *
  163. * @param int $f_id ID de la feuille
  164. * @param string $table Forum concerne
  165. */
  166. public static function delete($f_id, $table = 'forums')
  167. {
  168. // Donnees de la feuille
  169. $sql = 'SELECT f_left, f_right
  170. FROM ' . SQL_PREFIX . $table . '
  171. WHERE f_id = ' . $f_id;
  172. if ($current = Fsb::$db->request($sql))
  173. {
  174. // Suppression de la feuille
  175. $sql = 'DELETE FROM ' . SQL_PREFIX . $table . '
  176. WHERE f_left >= ' . $current['f_left'] . ' AND f_right <= ' . $current['f_right'];
  177. Fsb::$db->query($sql);
  178. // On redecale les bornes correctement
  179. Fsb::$db->update($table, array(
  180. 'f_left' => array('f_left - ' . ($current['f_right'] - $current['f_left'] + 1), 'is_field' => true),
  181. ), 'WHERE f_left >= ' . $current['f_left']);
  182. Fsb::$db->update($table, array(
  183. 'f_right' => array('f_right - ' . ($current['f_right'] - $current['f_left'] + 1), 'is_field' => true),
  184. ), 'WHERE f_right >= ' . $current['f_right']);
  185. }
  186. }
  187. /**
  188. * Deplace une feuille
  189. *
  190. * @param int $f_id ID de la feuille
  191. * @param string $direction Direction du deplacement (left, right)
  192. * @param string $table Table concernee
  193. */
  194. public static function move($f_id, $direction, $table = 'forums')
  195. {
  196. if ($direction == 'left')
  197. {
  198. $current_side = 'f_left';
  199. $swap_side = 'f_right';
  200. $swap_operator = '+';
  201. $current_sign = -1;
  202. }
  203. else
  204. {
  205. $current_side = 'f_right';
  206. $swap_side = 'f_left';
  207. $swap_operator = '-';
  208. $current_sign = 1;
  209. }
  210. // Donnees de la feuille actuelle
  211. $sql = 'SELECT f_left, f_right
  212. FROM ' . SQL_PREFIX . $table . '
  213. WHERE f_id = ' . $f_id;
  214. $current = Fsb::$db->request($sql);
  215. if ($current)
  216. {
  217. // Donnees de la feuille avec laquelle on va faire un echange
  218. $sql = 'SELECT f_left, f_right
  219. FROM ' . SQL_PREFIX . $table . '
  220. WHERE ' . $swap_side . ' = ' . ($current[$current_side] - (-1 * $current_sign));
  221. $swap = Fsb::$db->request($sql);
  222. if ($swap)
  223. {
  224. // On decale la feuille actuelle en zone temporaire
  225. Fsb::$db->update($table, array(
  226. 'f_left' => array('f_left + ' . Sql_interval::MOVE, 'is_field' => true),
  227. 'f_right' => array('f_right + ' . Sql_interval::MOVE, 'is_field' => true),
  228. ), 'WHERE f_left >= ' . $current['f_left'] . ' AND f_right <= ' . $current['f_right']);
  229. // On decale la feuille d'echange
  230. Fsb::$db->update($table, array(
  231. 'f_left' => array('f_left ' . $swap_operator . ' ' . ($current['f_right'] - $current['f_left'] + 1), 'is_field' => true),
  232. 'f_right' => array('f_right ' . $swap_operator . ' ' . ($current['f_right'] - $current['f_left'] + 1), 'is_field' => true),
  233. ), 'WHERE f_left >= ' . $swap['f_left'] . ' AND f_right <= ' . $swap['f_right']);
  234. // On replace la feuille courante dans sa nouvelle position
  235. Fsb::$db->update($table, array(
  236. 'f_left' => array('f_left - ' . (Sql_interval::MOVE - (($swap['f_right'] - $swap['f_left'] + 1) * $current_sign)), 'is_field' => true),
  237. 'f_right' => array('f_right - ' . (Sql_interval::MOVE - (($swap['f_right'] - $swap['f_left'] + 1) * $current_sign)), 'is_field' => true),
  238. ), 'WHERE f_left > ' . Sql_interval::MOVE);
  239. }
  240. }
  241. }
  242. /**
  243. * Retourne la liste des enfants
  244. *
  245. * @param int $f_id ID de la feuille
  246. * @param bool $include Definit si on inclu le forum actuel dans le resultat
  247. * @param string $cache Mise en cache ?
  248. * @param string $table Table concernee
  249. * @return array
  250. */
  251. public static function get_childs($f_id, $include = true, $cache = null, $table = 'forums')
  252. {
  253. if (!is_array($f_id))
  254. {
  255. $f_id = array($f_id);
  256. }
  257. $childs = array();
  258. if ($include)
  259. {
  260. $childs = $f_id;
  261. }
  262. $sql = 'SELECT child.f_id
  263. FROM ' . SQL_PREFIX . $table . ' f
  264. INNER JOIN ' . SQL_PREFIX . $table . ' child
  265. ON f.f_left < child.f_left
  266. AND f.f_right > child.f_right
  267. WHERE f.f_id IN (' . implode(', ', $f_id) . ')';
  268. $result = Fsb::$db->query($sql, $cache);
  269. while ($row = Fsb::$db->row($result))
  270. {
  271. $childs[] = $row['f_id'];
  272. }
  273. Fsb::$db->free($result);
  274. return (array_unique($childs));
  275. }
  276. /**
  277. * Retourne la liste des parents
  278. *
  279. * @param int $f_id ID de la feuille
  280. * @param bool $include Definit si on inclu le forum actuel dans le resultat
  281. * @param string $cache Mise en cache ?
  282. * @param string $table Table concernee
  283. * @return array
  284. */
  285. public static function get_parents($f_id, $include = true, $cache = null, $table = 'forums')
  286. {
  287. if (!is_array($f_id))
  288. {
  289. $f_id = array($f_id);
  290. }
  291. $parents = array();
  292. if ($include)
  293. {
  294. $parents = $f_id;
  295. }
  296. $sql = 'SELECT child.f_id
  297. FROM ' . SQL_PREFIX . $table . ' f
  298. INNER JOIN ' . SQL_PREFIX . $table . ' child
  299. ON f.f_left > child.f_left
  300. AND f.f_right < child.f_right
  301. WHERE f.f_id IN (' . implode(', ', $f_id) . ')';
  302. $result = Fsb::$db->query($sql, $cache);
  303. while ($row = Fsb::$db->row($result))
  304. {
  305. $parents[] = $row['f_id'];
  306. }
  307. Fsb::$db->free($result);
  308. return (array_unique($parents));
  309. }
  310. /**
  311. * Affiche une representation de l'arbre pour le debug
  312. *
  313. * @param string $field Champ contenant le nom de la feuille actuelle
  314. * @param string $table Table concernee
  315. */
  316. public static function debug($field = 'f_name', $table = 'forums')
  317. {
  318. echo '<pre>';
  319. $sql = 'SELECT f_id, f_level, f_left, f_right, ' . $field . '
  320. FROM ' . SQL_PREFIX . $table . '
  321. ORDER BY f_left';
  322. $result = Fsb::$db->query($sql);
  323. while ($row = Fsb::$db->row($result))
  324. {
  325. echo $row['f_id'] . ' -' . str_repeat("\t", $row['f_level']) . ' [' . $row['f_left'] . '] ' . $row[$field] . ' [' . $row['f_right'] . "]\n";
  326. }
  327. Fsb::$db->free($result);
  328. echo '</pre>';
  329. }
  330. }
  331. /* EOF */