00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 class Varien_Data_Tree_Db extends Varien_Data_Tree
00038 {
00039 const ID_FIELD = 'id';
00040 const PARENT_FIELD = 'parent';
00041 const LEVEL_FIELD = 'level';
00042 const ORDER_FIELD = 'order';
00043
00044
00045
00046
00047
00048
00049 protected $_conn;
00050
00051
00052
00053
00054
00055
00056 protected $_table;
00057
00058
00059
00060
00061
00062
00063 protected $_select;
00064
00065
00066
00067
00068
00069
00070 protected $_idField;
00071 protected $_parentField;
00072 protected $_levelField;
00073 protected $_orderField;
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089 public function __construct($connection, $table, $fields)
00090 {
00091 parent::__construct();
00092
00093 if (!$connection) {
00094 throw new Exception('Wrong "$connection" parametr');
00095 }
00096
00097 $this->_conn = $connection;
00098 $this->_table = $table;
00099
00100 if (!isset($fields[self::ID_FIELD]) ||
00101 !isset($fields[self::PARENT_FIELD]) ||
00102 !isset($fields[self::LEVEL_FIELD]) ||
00103 !isset($fields[self::ORDER_FIELD])) {
00104
00105 throw new Exception('"$fields" tree configuratin array');
00106 }
00107
00108 $this->_idField = $fields[self::ID_FIELD];
00109 $this->_parentField = $fields[self::PARENT_FIELD];
00110 $this->_levelField = $fields[self::LEVEL_FIELD];
00111 $this->_orderField = $fields[self::ORDER_FIELD];
00112
00113 $this->_select = $this->_conn->select();
00114 $this->_select->from($this->_table, array_values($fields));
00115 }
00116
00117 public function getDbSelect()
00118 {
00119 return $this->_select;
00120 }
00121
00122 public function setDbSelect($select)
00123 {
00124 $this->_select = $select;
00125 }
00126
00127
00128
00129
00130
00131
00132
00133
00134 public function load($parentNode=null, $recursionLevel=100)
00135 {
00136 if (is_null($parentNode)) {
00137 $this->_loadFullTree();
00138 return $this;
00139 }
00140 elseif ($parentNode instanceof Varien_Data_Tree_Node) {
00141 $parentId = $parentNode->getId();
00142 }
00143 elseif (is_numeric($parentNode)) {
00144 $parentId = $parentNode;
00145 $parentNode = null;
00146 }
00147 else {
00148 throw new Exception('root node id is not defined');
00149 }
00150
00151 $select = clone $this->_select;
00152 $select->order($this->_table.'.'.$this->_orderField . ' ASC');
00153 $condition = $this->_conn->quoteInto("$this->_table.$this->_parentField=?", $parentId);
00154 $select->where($condition);
00155 $arrNodes = $this->_conn->fetchAll($select);
00156 foreach ($arrNodes as $nodeInfo) {
00157 $node = new Varien_Data_Tree_Node($nodeInfo, $this->_idField, $this, $parentNode);
00158 $this->addNode($node, $parentNode);
00159
00160 if ($recursionLevel) {
00161 $node->loadChildren($recursionLevel-1);
00162 }
00163 }
00164 return $this;
00165 }
00166
00167 public function loadNode($nodeId)
00168 {
00169 $select = clone $this->_select;
00170 $condition = $this->_conn->quoteInto("$this->_table.$this->_idField=?", $nodeId);
00171 $select->where($condition);
00172 $node = new Varien_Data_Tree_Node($this->_conn->fetchRow($select), $this->_idField, $this);
00173 $this->addNode($node);
00174 return $node;
00175 }
00176
00177 public function appendChild($data=array(), $parentNode, $prevNode=null)
00178 {
00179 $orderSelect = $this->_conn->select();
00180 $orderSelect->from($this->_table, new Zend_Db_Expr('MAX('.$this->_conn->quoteIdentifier($this->_orderField).')'))
00181 ->where($this->_conn->quoteIdentifier($this->_parentField).'='.$parentNode->getId());
00182
00183 $order = $this->_conn->fetchOne($orderSelect);
00184 $data[$this->_parentField] = $parentNode->getId();
00185 $data[$this->_levelField] = $parentNode->getData($this->_levelField)+1;
00186 $data[$this->_orderField] = $order+1;
00187
00188 $this->_conn->insert($this->_table, $data);
00189 $data[$this->_idField] = $this->_conn->lastInsertId();
00190
00191 return parent::appendChild($data, $parentNode, $prevNode);
00192 }
00193
00194
00195
00196
00197
00198
00199
00200
00201 public function moveNodeTo($node, $parentNode, $prevNode=null)
00202 {
00203 $data = array();
00204 $data[$this->_parentField] = $parentNode->getId();
00205 $data[$this->_levelField] = $parentNode->getData($this->_levelField)+1;
00206
00207 if (is_null($prevNode) || is_null($prevNode->getData($this->_orderField))) {
00208 $data[$this->_orderField] = 1;
00209 }
00210 else {
00211 $data[$this->_orderField] = $prevNode->getData($this->_orderField)+1;
00212 }
00213 $condition = $this->_conn->quoteInto("$this->_idField=?", $node->getId());
00214
00215
00216 $dataReorderNew = array(
00217 $this->_orderField => new Zend_Db_Expr($this->_conn->quoteIdentifier($this->_orderField).'+1')
00218 );
00219 $conditionReorderNew = $this->_conn->quoteIdentifier($this->_parentField).'='.$parentNode->getId().
00220 ' AND '.$this->_conn->quoteIdentifier($this->_orderField).'>='. $data[$this->_orderField];
00221
00222
00223 $dataReorderOld = array(
00224 $this->_orderField => new Zend_Db_Expr($this->_conn->quoteIdentifier($this->_orderField).'-1')
00225 );
00226 $conditionReorderOld = $this->_conn->quoteIdentifier($this->_parentField).'='.$node->getData($this->_parentField).
00227 ' AND '.$this->_conn->quoteIdentifier($this->_orderField).'>'.$node->getData($this->_orderField);
00228
00229 $this->_conn->beginTransaction();
00230 try {
00231
00232 $this->_conn->update($this->_table, $dataReorderNew, $conditionReorderNew);
00233
00234 $this->_conn->update($this->_table, $data, $condition);
00235
00236 $this->_conn->update($this->_table, $dataReorderOld, $conditionReorderOld);
00237 $this->_updateChildLevels($node->getId(), $data[$this->_levelField]);
00238 $this->_conn->commit();
00239 }
00240 catch (Exception $e){
00241 $this->_conn->rollBack();
00242 throw new Exception('Can\'t move tree node');
00243 }
00244 }
00245
00246 protected function _updateChildLevels($parentId, $parentLevel)
00247 {
00248 $select = $this->_conn->select()
00249 ->from($this->_table, $this->_idField)
00250 ->where($this->_parentField.'=?', $parentId);
00251 $ids = $this->_conn->fetchCol($select);
00252
00253 if (!empty($ids)) {
00254 $this->_conn->update($this->_table,
00255 array($this->_levelField=>$parentLevel+1),
00256 $this->_conn->quoteInto($this->_idField.' IN (?)', $ids));
00257 foreach ($ids as $id) {
00258 $this->_updateChildLevels($id, $parentLevel+1);
00259 }
00260 }
00261 return $this;
00262 }
00263
00264 protected function _loadFullTree()
00265 {
00266 $select = clone $this->_select;
00267 $select->order($this->_table . '.' . $this->_levelField)
00268 ->order($this->_table.'.'.$this->_orderField);
00269
00270 $arrNodes = $this->_conn->fetchAll($select);
00271
00272 foreach ($arrNodes as $nodeInfo) {
00273 $node = new Varien_Data_Tree_Node($nodeInfo, $this->_idField, $this);
00274 $parentNode = $this->getNodeById($nodeInfo[$this->_parentField]);
00275 $this->addNode($node, $parentNode);
00276 }
00277
00278 return $this;
00279 }
00280
00281 public function removeNode($node)
00282 {
00283
00284 $dataReorderOld = array(
00285 $this->_orderField => new Zend_Db_Expr($this->_conn->quoteIdentifier($this->_orderField).'-1')
00286 );
00287 $conditionReorderOld = $this->_conn->quoteIdentifier($this->_parentField).'='.$node->getData($this->_parentField).
00288 ' AND '.$this->_conn->quoteIdentifier($this->_orderField).'>'.$node->getData($this->_orderField);
00289
00290 $this->_conn->beginTransaction();
00291 try {
00292 $condition = $this->_conn->quoteInto("$this->_idField=?", $node->getId());
00293 $this->_conn->delete($this->_table, $condition);
00294
00295 $this->_conn->update($this->_table, $dataReorderOld, $conditionReorderOld);
00296 $this->_conn->commit();
00297 }
00298 catch (Exception $e){
00299 $this->_conn->rollBack();
00300 throw new Exception('Can\'t remove tree node');
00301 }
00302 parent::removeNode($node);
00303 return $this;
00304 }
00305 }