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
00038 class Varien_Data_Tree_Dbp extends Varien_Data_Tree
00039 {
00040
00041 const ID_FIELD = 'id';
00042 const PATH_FIELD = 'path';
00043 const ORDER_FIELD = 'order';
00044 const LEVEL_FIELD = 'level';
00045
00046
00047
00048
00049
00050
00051 protected $_conn;
00052
00053
00054
00055
00056
00057
00058 protected $_table;
00059
00060 protected $_loaded = false;
00061
00062
00063
00064
00065
00066
00067 protected $_select;
00068
00069
00070
00071
00072
00073
00074 protected $_idField;
00075 protected $_pathField;
00076 protected $_orderField;
00077 protected $_levelField;
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093 public function __construct($connection, $table, $fields)
00094 {
00095 parent::__construct();
00096
00097 if (!$connection) {
00098 throw new Exception('Wrong "$connection" parametr');
00099 }
00100
00101 $this->_conn = $connection;
00102 $this->_table = $table;
00103
00104 if (!isset($fields[self::ID_FIELD]) ||
00105 !isset($fields[self::PATH_FIELD]) ||
00106 !isset($fields[self::LEVEL_FIELD]) ||
00107 !isset($fields[self::ORDER_FIELD])) {
00108
00109 throw new Exception('"$fields" tree configuratin array');
00110 }
00111
00112 $this->_idField = $fields[self::ID_FIELD];
00113 $this->_pathField = $fields[self::PATH_FIELD];
00114 $this->_orderField = $fields[self::ORDER_FIELD];
00115 $this->_levelField = $fields[self::LEVEL_FIELD];
00116
00117 $this->_select = $this->_conn->select();
00118 $this->_select->from($this->_table);
00119 }
00120
00121 public function getDbSelect()
00122 {
00123 return $this->_select;
00124 }
00125
00126 public function setDbSelect($select)
00127 {
00128 $this->_select = $select;
00129 }
00130
00131
00132
00133
00134
00135
00136
00137 public function load($parentNode=null, $recursionLevel = 0)
00138 {
00139 if (!$this->_loaded) {
00140 $startLevel = 1;
00141 $parentPath = '';
00142
00143 if ($parentNode instanceof Varien_Data_Tree_Node) {
00144 $parentPath = $parentNode->getData($this->_pathField);
00145 $startLevel = $parentNode->getData($this->_levelField);
00146 } elseif (is_numeric($parentNode)) {
00147 $parentNode = null;
00148 $select = $this->_conn->select();
00149 $select->from($this->_table, array($this->_pathField, $this->_levelField))->where("{$this->_idField} = ?", $parentNode);
00150 $parent = $this->_conn->fetchRow($select);
00151 $startLevel = $parent[$this->_levelField];
00152 $parentPath = $parent[$this->_pathField];
00153 } elseif (is_string($parentNode)) {
00154 $parentNode = null;
00155 $parentPath = $parentNode;
00156 $startLevel = count(explode($parentPath))-1;
00157 }
00158
00159 $select = clone $this->_select;
00160 $select->order($this->_table.'.'.$this->_orderField . ' ASC');
00161
00162 if ($parentPath) {
00163 $condition = $this->_conn->quoteInto("$this->_table.$this->_pathField like ?", "$parentPath/%");
00164 $select->where($condition);
00165 }
00166 if ($recursionLevel != 0) {
00167 $select->where("$this->_levelField <= ?", $startLevel + $recursionLevel);
00168 }
00169
00170 $arrNodes = $this->_conn->fetchAll($select);
00171
00172 $childrenItems = array();
00173
00174 foreach ($arrNodes as $nodeInfo) {
00175 $pathToParent = explode('/', $nodeInfo[$this->_pathField]);
00176 array_pop($pathToParent);
00177 $pathToParent = implode('/', $pathToParent);
00178 $childrenItems[$pathToParent][] = $nodeInfo;
00179 }
00180
00181 $this->addChildNodes($childrenItems, $parentPath, $parentNode);
00182
00183 $this->_loaded = true;
00184 }
00185
00186 return $this;
00187 }
00188
00189 public function addChildNodes($children, $path, $parentNode, $level = 0)
00190 {
00191 if (isset($children[$path])) {
00192 foreach ($children[$path] as $child) {
00193 $nodeId = isset($child[$this->_idField])?$child[$this->_idField]:false;
00194 if ($parentNode && $nodeId && $node = $parentNode->getChildren()->searchById($nodeId)) {
00195 $node->addData($child);
00196 } else {
00197 $node = new Varien_Data_Tree_Node($child, $this->_idField, $this, $parentNode);
00198 }
00199
00200
00201 $node->setLevel($node->getData($this->_levelField));
00202 $node->setPathId($node->getData($this->_pathField));
00203 $this->addNode($node, $parentNode);
00204
00205
00206 if ($path) {
00207 $childrenPath = explode('/', $path);
00208 } else {
00209 $childrenPath = array();
00210 }
00211 $childrenPath[] = $node->getId();
00212 $childrenPath = implode('/', $childrenPath);
00213
00214 $this->addChildNodes($children, $childrenPath, $node, $level+1);
00215 }
00216 }
00217 }
00218
00219
00220
00221
00222
00223
00224
00225 public function loadNode($nodeId)
00226 {
00227 $select = clone $this->_select;
00228 if (is_numeric($nodeId))
00229 $condition = $this->_conn->quoteInto("$this->_table.$this->_idField=?", $nodeId);
00230 else
00231 $condition = $this->_conn->quoteInto("$this->_table.$this->_pathField=?", $nodeId);
00232
00233 $select->where($condition);
00234
00235 $node = new Varien_Data_Tree_Node($this->_conn->fetchRow($select), $this->_idField, $this);
00236 $this->addNode($node);
00237 return $node;
00238 }
00239
00240 public function getChildren($node, $recursive = true, $result = array()) {
00241 if (is_numeric($node)) {
00242 $node = $this->getNodeById($node);
00243 }
00244 if (!$node)
00245 return $result;
00246
00247 foreach ($node->getChildren() as $child) {
00248 if ($recursive) {
00249 if ($child->getChildren()) {
00250 $result = $this->getChildren($child, $recursive, $result);
00251 }
00252 }
00253 $result[] = $child->getId();
00254 }
00255 return $result;
00256 }
00257
00258
00259
00260
00261
00262
00263
00264
00265 public function move($node, $newParent, $prevNode = null)
00266 {
00267 $position = 1;
00268
00269 $oldPath = $node->getData($this->_pathField);
00270 $newPath = $newParent->getData($this->_pathField);
00271
00272 $newPath = $newPath . '/' . $node->getId();
00273 $oldPathLength = strlen($oldPath);
00274
00275 $newLevel = $newParent->getLevel()+1;
00276 $levelDisposition = $newLevel-$node->getLevel();
00277
00278 $data = array(
00279 $this->_levelField => new Zend_Db_Expr("{$this->_levelField} + '{$levelDisposition}'"),
00280 $this->_pathField => new Zend_Db_Expr("CONCAT('$newPath', RIGHT($this->_pathField, LENGTH($this->_pathField) - {$oldPathLength}))")
00281 );
00282 $condition = $this->_conn->quoteInto("$this->_pathField REGEXP ?", "^$oldPath(/|$)");
00283
00284 $this->_conn->beginTransaction();
00285
00286 $reorderData = array($this->_orderField => new Zend_Db_Expr("$this->_orderField + 1"));
00287 try {
00288 if ($prevNode && $prevNode->getId()) {
00289 $reorderCondition = "{$this->_orderField} > {$prevNode->getData($this->_orderField)}";
00290 $position = $prevNode->getData($this->_orderField) + 1;
00291 } else {
00292 $reorderCondition = $this->_conn->quoteInto("{$this->_pathField} REGEXP ?", "^{$newParent->getData($this->_pathField)}/[0-9]+$");
00293 $select = $this->_conn->select()
00294 ->from($this->_table, new Zend_Db_Expr("MIN({$this->_orderField})"))
00295 ->where($reorderCondition);
00296
00297 $position = (int) $this->_conn->fetchOne($select);
00298 }
00299 $this->_conn->update($this->_table, $reorderData, $reorderCondition);
00300 $this->_conn->update($this->_table, $data, $condition);
00301 $this->_conn->update($this->_table, array($this->_orderField => $position, $this->_levelField=>$newLevel),
00302 $this->_conn->quoteInto("{$this->_idField} = ?", $node->getId())
00303 );
00304
00305 $this->_conn->commit();
00306 } catch (Exception $e){
00307 $this->_conn->rollBack();
00308 throw new Exception("Can't move tree node due to error: " . $e->getMessage());
00309 }
00310 }
00311
00312 public function loadEnsuredNodes($category, $rootNode)
00313 {
00314 $pathIds = $category->getPathIds();
00315 $rootNodeId = $rootNode->getId();
00316 $rootNodePath = $rootNode->getData($this->_pathField);
00317
00318 $select = clone $this->_select;
00319 $select->order($this->_table.'.'.$this->_orderField . ' ASC');
00320
00321 if ($pathIds) {
00322 $condition = $this->_conn->quoteInto("$this->_table.$this->_idField in (?)", $pathIds);
00323 $select->where($condition);
00324 }
00325
00326 $arrNodes = $this->_conn->fetchAll($select);
00327
00328 if ($arrNodes) {
00329 $childrenItems = array();
00330 foreach ($arrNodes as $nodeInfo) {
00331 $nodeId = $nodeInfo[$this->_idField];
00332 if ($nodeId<=$rootNodeId) {
00333 continue;
00334 }
00335
00336 $pathToParent = explode('/', $nodeInfo[$this->_pathField]);
00337 array_pop($pathToParent);
00338 $pathToParent = implode('/', $pathToParent);
00339 $childrenItems[$pathToParent][] = $nodeInfo;
00340 }
00341
00342 $this->_addChildNodes($childrenItems, $rootNodePath, $rootNode, true);
00343 }
00344 }
00345
00346 protected function _addChildNodes($children, $path, $parentNode, $withChildren=false, $level = 0)
00347 {
00348 if (isset($children[$path])) {
00349 foreach ($children[$path] as $child) {
00350 $nodeId = isset($child[$this->_idField])?$child[$this->_idField]:false;
00351 if ($parentNode && $nodeId && $node = $parentNode->getChildren()->searchById($nodeId)) {
00352 $node->addData($child);
00353 } else {
00354 $node = new Varien_Data_Tree_Node($child, $this->_idField, $this, $parentNode);
00355 $node->setLevel($node->getData($this->_levelField));
00356 $node->setPathId($node->getData($this->_pathField));
00357 $this->addNode($node, $parentNode);
00358 }
00359
00360 if ($withChildren) {
00361 $this->_loaded = false;
00362 $node->loadChildren(1);
00363 $this->_loaded = false;
00364 }
00365
00366 if ($path) {
00367 $childrenPath = explode('/', $path);
00368 } else {
00369 $childrenPath = array();
00370 }
00371 $childrenPath[] = $node->getId();
00372 $childrenPath = implode('/', $childrenPath);
00373
00374 $this->_addChildNodes($children, $childrenPath, $node, $withChildren, $level+1);
00375 }
00376 }
00377 }
00378
00379 }