Data_Tree_Source.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <?php
  2. Lib::loadClass('Data_Source');
  3. /**
  4. * API:
  5. * get($field_name) - source field value
  6. * get_item($id) - get item by ID
  7. */
  8. class Data_Tree_Source extends Data_Source {
  9. protected $_parentIdField = '';
  10. protected $_nameField = '';
  11. protected $_sortField = '';
  12. public function isValidTree() {
  13. if (!empty($this->_parentIdField) && array_key_exists($this->_parentIdField, $this->_cols)
  14. && !empty($this->_nameField) && array_key_exists($this->_nameField, $this->_cols)) {
  15. return true;
  16. }
  17. return false;
  18. }
  19. public function getParentIdField() {
  20. return $this->_parentIdField;
  21. }
  22. public function setParentIdField($parentIdField) {
  23. if (array_key_exists($parentIdField, $this->_cols)) {
  24. $this->_parentIdField = $parentIdField;
  25. return true;
  26. }
  27. return false;
  28. }
  29. public function setSortField($sortFld) {
  30. if (array_key_exists($sortFld, $this->_cols)) {
  31. $this->_sortField = $sortFld;
  32. return true;
  33. }
  34. return false;
  35. }
  36. public function hasSortField() {
  37. return !empty($this->_sortField);
  38. }
  39. public function getNameField() {
  40. return $this->_nameField;
  41. }
  42. public function setNameField($nameField) {
  43. if (array_key_exists($nameField, $this->_cols)) {
  44. $this->_nameField = $nameField;
  45. return true;
  46. }
  47. return false;
  48. }
  49. private function _parseSqlWhere($params) {
  50. $sql_where = '';
  51. $sql_where_and = array();
  52. foreach ($params as $k => $v) {
  53. if ($k == '_rawSql') {
  54. foreach ($v as $rawSql) {
  55. $sql_where_and[] = $rawSql;
  56. }
  57. }
  58. else if (strlen($k) > 3 && substr($k, 0, 2) == 'f_') {
  59. $sql_where_and[] = "t.`" . substr($k, 2) . "` like '" . DB::_($v) . "'";
  60. }
  61. else if (strlen($k) > 4 && substr($k, 0, 3) == 'sf_') {
  62. $sqlFltr = $this->_parseSpecialFilter(substr($k, 3), $v);
  63. if (!empty($sqlFltr)) {
  64. $sql_where_and[] = $sqlFltr;
  65. }
  66. }
  67. }
  68. if (!empty($sql_where_and)) {
  69. $sql_where = implode(" and ", $sql_where_and);
  70. }
  71. if (!$sql_where) $sql_where = "1=1";
  72. return $sql_where;
  73. }
  74. public function getTreeNodes($parentID, $params = array()) {
  75. $DBG = ('1' == V::get('DBG_DS', '', $_REQUEST));
  76. $nodes = array();
  77. if (!$this->isValidTree()) return $nodes;
  78. $params['_rawSql'][] = "t.`{$this->_parentIdField}` = {$parentID}";
  79. //$sql_limit = V::get('limit', $this->_default_sql_limit, $params, 'int');
  80. //$sql_offset = V::get('limitstart', 0, $params, 'int');
  81. $sql_order_by = V::get('order_by', '', $params);
  82. if ($sql_order_by) {
  83. $sql_order_dir = V::get('order_dir', '', $params);
  84. // prevent from sorting by special columns
  85. if (!array_key_exists($sql_order_by, $this->_cols)) {
  86. $sql_order_by = null;
  87. $sql_order_dir = null;
  88. }
  89. }
  90. if ($sql_order_by) {
  91. $sql_order_by = "order by t.`{$sql_order_by}`";
  92. if ($sql_order_dir) {
  93. $sql_order_by = "{$sql_order_by} {$sql_order_dir}";
  94. }
  95. }
  96. $sql_cols = $this->_get_sql_cols();
  97. $sql_where = $this->_parseSqlWhere($params);
  98. $sql = "select {$sql_cols}
  99. from {$this->_tbl} as t
  100. where {$sql_where}
  101. {$sql_order_by}
  102. ";
  103. // limit {$sql_limit} offset {$sql_offset}
  104. if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">sql (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($sql);echo'</pre>';}
  105. $res = $this->_db->query($sql);
  106. while ($r = $this->_db->fetch($res)) {
  107. $node = new stdClass();
  108. $node->id = $r->ID;
  109. $node->parent_id = (int)$r->{$this->_parentIdField};
  110. $node->type = 'folder';
  111. $node->data = $r;
  112. $node->name = $r->{$this->_nameField};
  113. $nodes[$r->ID] = $node;
  114. }
  115. return $nodes;
  116. }
  117. public function getTreeNode($id, $params = array()) {
  118. $DBG = ('1' == V::get('DBG_DS', '', $_REQUEST));
  119. if (!$this->isValidTree()) return null;
  120. $params['_rawSql'][] = "t.`ID`={$id}";
  121. $sql_cols = $this->_get_sql_cols();
  122. $sql_where = $this->_parseSqlWhere($params);
  123. $sql = "select {$sql_cols}
  124. from {$this->_tbl} as t
  125. where {$sql_where}
  126. limit 1
  127. ";
  128. if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">sql (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($sql);echo'</pre>';}
  129. $res = $this->_db->query($sql);
  130. if ($r = $this->_db->fetch($res)) {
  131. $node = new stdClass();
  132. $node->id = $r->ID;
  133. $node->parent_id = (int)$r->{$this->_parentIdField};
  134. $node->type = 'folder';
  135. $node->data = $r;
  136. $node->name = $r->{$this->_nameField};
  137. return $node;
  138. }
  139. return null;
  140. }
  141. private function _isOnPathRec($id, $nodeId, $limit = 10) {
  142. //trigger_error('TODO: _isOnPathRec('.$id.', '.$nodeId.', '.$limit.')', E_USER_NOTICE);
  143. $sql = "select t.`ID`, t.`{$this->_parentIdField}`
  144. from `{$this->_tbl}` as t
  145. where t.`ID`={$nodeId}
  146. ";
  147. $res = $this->_db->query($sql);
  148. if ($r = $this->_db->fetch($res)) {
  149. $p_id = (int)$r->{$this->_parentIdField};
  150. if ($p_id > 0) {
  151. if ($p_id == $id) {
  152. //trigger_error('TODO: return true '.$id.' == '.$p_id.'', E_USER_NOTICE);
  153. return true;
  154. }
  155. if ($limit--) {
  156. return $this->_isOnPathRec($id, $p_id, $limit);
  157. }
  158. }
  159. }
  160. return false;
  161. }
  162. /**
  163. * Check if $id is on path between root to $nodeId
  164. */
  165. public function isOnPath($id, $nodeId) {
  166. //trigger_error('TODO: isOnPath('.$id.', '.$nodeId.')', E_USER_NOTICE);
  167. return $this->_isOnPathRec($id, $nodeId);
  168. }
  169. /**
  170. * Try to move $id under $p_id
  171. *
  172. * $p_id cant be under $id - check path
  173. */
  174. public function moveTreeNode($id, $p_id) {
  175. $DBG = ('1' == V::get('DBG_DS', '', $_REQUEST));
  176. if (!$this->isValidTree()) return false;
  177. if ($p_id == $id) return false;
  178. $node = $this->_db->get_by_id($this->_tbl, $id);
  179. if (!$node) return false;
  180. if ($p_id == (int)$node->P_ID) return false;
  181. if ($this->isOnPath($id, $p_id)) return false;
  182. $sqlObj = new stdClass();
  183. $sqlObj->ID = $id;
  184. $sqlObj->{$this->_parentIdField} = $p_id;
  185. $affected = $this->_db->UPDATE_OBJ($this->_tbl, $sqlObj);
  186. if ($this->_db->has_errors()) {
  187. return false;
  188. }
  189. return ($affected > 0);
  190. }
  191. /**
  192. * Change order - put $id before $p_id
  193. */
  194. public function moveTreeNodeSort($id, $destId, $destDir) {
  195. if ($id < 0 || $destId < 0 || $id == $destId) return false;
  196. if (!$this->_sortField) return false;
  197. if (!$this->isValidTree()) return false;
  198. $DBG = ('1' == V::get('DBG_DS', '', $_REQUEST));
  199. $node = $this->_db->get_by_id($this->_tbl, $id);
  200. $destItem = $this->_db->get_by_id($this->_tbl, $destId);
  201. if (!$node || !$destItem) return false;
  202. if ($node->{$this->_parentIdField} < 0 || $node->{$this->_parentIdField} != $destItem->{$this->_parentIdField}) return false;
  203. $rows = array();
  204. // $sql->order_by = "order by t.`SORT_PRIO` asc, t.`ID` asc";
  205. $sql = "select t.`ID`, t.`{$this->_parentIdField}`, t.`{$this->_sortField}`
  206. from `{$this->_tbl}` as t
  207. where t.`{$this->_parentIdField}`='{$node->{$this->_parentIdField}}'
  208. order by t.`{$this->_sortField}`
  209. ";
  210. if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">sql (dir:'.$destDir.') (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($sql);echo'</pre>';}
  211. $res = $this->_db->query($sql);
  212. while ($r = $this->_db->fetch($res)) {
  213. if ($r->ID == $node->ID) continue;
  214. $rows[$r->ID] = $r->{$this->_sortField};
  215. }
  216. if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">rows (dir:'.$destDir.') (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($rows);echo'</pre>';}
  217. $foundDestId = false;
  218. $prevSortPrio = 0;
  219. $changeArr = array();
  220. foreach ($rows as $id => $sortPrio) {
  221. if (!$foundDestId && $id == $destId) {
  222. $foundDestId = true;
  223. if ($destDir == 'before') {
  224. $prevSortPrio = $sortPrio;
  225. $changeArr[$node->ID] = $prevSortPrio;
  226. $prevSortPrio++;
  227. $changeArr[$id] = $prevSortPrio;
  228. }
  229. else if ($destDir == 'after') {
  230. $prevSortPrio = $sortPrio;
  231. $prevSortPrio++;
  232. $changeArr[$node->ID] = $prevSortPrio;
  233. }
  234. }
  235. else if ($foundDestId) {
  236. $prevSortPrio++;
  237. if ($prevSortPrio > $sortPrio) {
  238. $changeArr[$id] = $prevSortPrio;
  239. } else {
  240. $prevSortPrio = $sortPrio;
  241. }
  242. }
  243. }
  244. if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">changeArr (dir:'.$destDir.') (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($changeArr);echo'</pre>';}
  245. if (!empty($changeArr)) {
  246. foreach ($changeArr as $id => $sortPrio) {
  247. $sqlObj = new stdClass();
  248. $sqlObj->ID = $id;
  249. $sqlObj->{$this->_sortField} = $sortPrio;
  250. $affected = $this->_db->UPDATE_OBJ($this->_tbl, $sqlObj);
  251. }
  252. }
  253. return true;
  254. }
  255. public function moveTreeNodeSortBefore($id, $beforeId) {
  256. return $this->moveTreeNodeSort($id, $beforeId, 'after');
  257. }
  258. /**
  259. * Change order - put $id after $p_id
  260. */
  261. public function moveTreeNodeSortAfter($id, $afterId) {
  262. return $this->moveTreeNodeSort($id, $afterId, 'before');
  263. }
  264. }