Overview

Namespaces

  • App
    • Components
    • Interfaces
    • Lib
      • Files
      • Forms
        • Controls
      • Html
      • Repair
      • Statics
    • Model
    • Modules
      • BaseModule
        • Presenters
      • PartnersModule
        • Presenters
      • ProductModule
        • Presenters
      • SaleModule
        • Model
        • Presenters
      • SettingsModule
        • Model
        • Presenters
      • StoreModule
        • Model
        • Presenters
    • Presenters
    • Router
  • PHP

Classes

  • DateTime
  • SplFileInfo

Interfaces

  • ArrayAccess
  • Countable
  • Exception
  • Iterator
  • Traversable
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Download
  1: <?php
  2: 
  3: /**
  4:  * Project sberp
  5:  *
  6:  * @file Tree 
  7:  * @author Jaromír Polášek
  8:  * @version 0.6.5 ACL
  9:  * Encoding UTF-8
 10:  */
 11: 
 12: namespace App\Lib;
 13: 
 14: use Nette\Utils\Arrays,
 15:     Nette\Database\SqlLiteral,
 16:     App\Model\Database,
 17:     App\Lib\Statics\Cons,
 18:     App\Interfaces as I;
 19: 
 20: /**
 21:  * Třída pro zpracování stromových dat v databázi. Obsahuje metody pro získání stromu, seznamu položek stromu
 22:  * a přidání nebo odebrání uzlu. 
 23:  * 
 24:  * Zdroje:  
 25:  * @link http://www.zdrojak.cz/clanky/ukladame-hierarchicka-data-v-databazi-ii/
 26:  * @link http://interval.cz/clanky/metody-ukladani-stromovych-dat-v-relacnich-databazich/
 27:  */
 28: class Tree extends Table implements I\IData, I\ITree {
 29:     /* protected */
 30: 
 31:     /** @var Nette\Database\Table\ActiveRow 
 32:      * Hlavní uzel stromu, pokud existuje , jinak NULL */
 33:     protected $masterNode;
 34: 
 35:     /** @var App\model\Database
 36:      * Pomocná proměnná pro zavolání rebuildu */
 37:     protected $rebuildDatabase;
 38: 
 39:     /**
 40:      * Konstruktor předá parametry předkovy a zkontroluje integritu tabulky, pokud není test zakázán. 
 41:      * Pokud kontrola zjistí nesrovnalost, dojde k novém sestavení sestavení stromu.  
 42:      * @param String $tableName
 43:      * @param App\Model\Database $databases
 44:      * @param boolean $test (volitelné - test itegrity stromu)
 45:      * @param Nette\Database\Table\ActiveRow $masterNode (hlavní uzel. pokud není předán, třída si jej najde)
 46:      */
 47:     public function __construct($tableName, Database $databases, $test = true, $masterNode = NULL) {
 48:     parent::__construct($tableName, $databases);
 49:     $this->rebuildDatabase = $databases;
 50:     $this->masterNode = ($masterNode) ? $masterNode : $this->getMasterNode();
 51:     $this->setState(
 52:         !is_null($this->masterNode) &&
 53:         ((!$test) ? true : (new TreeIntegrity($tableName, $this->rebuildDatabase, false, $this->masterNode))->checkTreeIntegrity())
 54:     );
 55:     }
 56: 
 57:     /**
 58:      * Vrací setříděný strom bez hlavního uzlu
 59:      * @return Nette\Database\Selection
 60:      */
 61:     public function getData() {
 62:     return parent::getData()->where('NOT ' . Cons::COLUMN_ID, $this->masterNode->id)->order(Cons::COLUMN_LEFT);
 63:     }
 64: 
 65:     /**
 66:      * Vrací kopii setříděného stromu včetně hlavního uzlu
 67:      * @return Nette\Database\Selection
 68:      */
 69:     public function getCopy() {
 70:     return $this->data->createSelectionInstance();
 71:     }
 72: 
 73:     /**
 74:      * Vrací setříděný strom (čistější ale pomalejší verze, pracuje s kopií tabulky a vrací strom sestavený podle uzlů) 
 75:      * @return Nette\Database\Selection
 76:      */
 77:     public function getTree() {
 78:     return $this->getNodeChilds($this->masterNode->id);
 79:     }
 80: 
 81:     /**
 82:      * Vrací podstrom uzlu zadaného podle id, do hloubky $depth nebo jen poduzly. 
 83:      * Pracuje s kopii tabulky
 84:      * @param integer $id Id uzlu
 85:      * @param integer  $depth Hloubka zanoření
 86:      * @return Nette\Database\Table\Selection
 87:      */
 88:     public function getTreeNodes($id, $depth = null) {
 89:     $node = $this->data->get($id);
 90:     (!is_null($depth)) ? : $depth = $node[Cons::COLUMN_DEPTH] + 1;
 91:     return $this->getNodeChilds($id)->where(Cons::COLUMN_DEPTH . " ?", $depth);
 92:     }
 93: 
 94:     /**
 95:      * Vrátí počet potomků uzlu
 96:      * @param integer $id Id uzlu
 97:      * @return integer počet uzlů
 98:      */
 99:     public function getChildsCount($id) {
100:     $node = $this->data->get($id);
101:     return (int) ($node[Cons::COLUMN_RIGHT] - $node[Cons::COLUMN_LEFT] - 1) / 2;
102:     }
103: 
104:     /**
105:      * Přidá uzel a upraví hodnoty na pravo od něj a vytvoří cestu. Pokud není zadán rodič, je přidělen 
106:      * kořenový uzel.
107:      * @param array $data
108:      * @return integer afected row or exception 
109:      */
110:     public function insert($data) {
111:     $parent = NULL;
112:     if ($data[Cons::COLUMN_PARENT] !== NULL) {
113:         $id = (int) $data[Cons::COLUMN_PARENT];
114:         $parent = $this->findById($id);
115:     }
116:     if (!$parent instanceof \Nette\Database\Table\ActiveRow) {
117:         $data[Cons::COLUMN_PARENT] = $this->masterNode->id;
118:         $parent = $this->masterNode;
119:     }
120:     $this->rebuildTreeRight($parent, 2);
121:     $data[Cons::COLUMN_PATH] = ($parent[Cons::COLUMN_PATH] === NULL || $parent[Cons::COLUMN_PATH] === '') ?
122:         $data[Cons::COLUMN_NAME] : $parent[Cons::COLUMN_PATH] . ' > ' . $data[Cons::COLUMN_NAME];
123:     $data[Cons::COLUMN_LEFT] = $parent[Cons::COLUMN_LEFT] + 1;
124:     $data[Cons::COLUMN_RIGHT] = $parent[Cons::COLUMN_LEFT] + 2;
125:     $data[Cons::COLUMN_DEPTH] = $parent[Cons::COLUMN_DEPTH] + 1;
126:     return parent::insert($data);
127:     }
128: 
129:     /**
130:      * Odstraní uzel a upraví hodnoty na pravo od něj
131:      * @param integer $id Id uzlu
132:      * @return integer
133:      */
134:     public function delete($id) {
135:     $node = $this->data->get($id);
136:     if ($node[Cons::COLUMN_LEFT === $node[Cons::COLUMN_RIGHT] + 1]) {
137:         $this->rebuildTreeRight($node, -2);
138:         return parent::delete($id);
139:     } else {
140:         return 0;
141:     }
142:     }
143: 
144:     /**
145:      * Upraví uzel a opraví mu cestu. Nejprve je potřeba zapsat uzel a potom změnit cestu
146:      * @param integer $id
147:      * @param array $data
148:      * @return integer afected row or exception 
149:      */
150:     public function update($id, $data = null) {
151:     if ($data) {
152:         return (parent::update($id, $data)) ? parent::update($id, array(Cons::COLUMN_PATH => $this->getTreeNodePath($id))) : 0;
153:     } else {
154:         return parent::update($id, array(Cons::COLUMN_PATH => $this->getTreeNodePath($id)));
155:     }
156:     }
157: 
158:     /* protected */
159: 
160:     /**
161:      * Vraví cestu k uzlu zadaného dle id ve formátu: rodič > potomek
162:      * @param integer $id Id uzlu
163:      * @return string
164:      */
165:     protected function getTreeNodePath($id, $path = "") {
166:     $row = $this->data->get($id);
167:     $selection = $this->getCopy()
168:         ->where(Cons::COLUMN_LEFT . ' < ?', $row[Cons::COLUMN_LEFT])
169:         ->where(Cons::COLUMN_RIGHT . ' > ?', $row[Cons::COLUMN_RIGHT])
170:         ->where(Cons::COLUMN_DEPTH . ' > ?', 0)
171:         ->order(Cons::COLUMN_LEFT);
172:     Foreach ($selection as $key) {
173:         $path.=$key[Cons::COLUMN_NAME] . ' > ';
174:     }
175:     return $path.=$row[Cons::COLUMN_NAME];
176:     }
177: 
178:     /** Private * */
179: 
180:     /**
181:      * Upraví hodnoty napravo od zvoleného uzlu o zadanou značku
182:      * @param Nette\Database\Table\ActiveRow $node
183:      * @param integer $mark značka
184:      */
185:     private function rebuildTreeRight($node, $mark) {
186:     $this->copy
187:         ->where(Cons::COLUMN_LEFT . ' > ?', $node[Cons::COLUMN_LEFT])
188:         ->update(array(Cons::COLUMN_LEFT => new SqlLiteral(Cons::COLUMN_LEFT . ' + ' . $mark)))
189:     ;
190:     $this->copy
191:         ->where(Cons::COLUMN_RIGHT . ' >= ?', $node[Cons::COLUMN_LEFT])
192:         ->update(array(Cons::COLUMN_RIGHT => new SqlLiteral(Cons::COLUMN_RIGHT . ' + ' . $mark)))
193:     ;
194:     }
195: 
196:     /**
197:      * Vrátí část podstromu, pracuje s kopii tabulky
198:      * @param integer $id ID uzlu
199:      * @return Nette\Database\Table\Selection\
200:      */
201:     private function getNodeChilds($id) {
202:     $parent = $this->data->get($id);
203:     return $this->getCopy()
204:             ->where(Cons::COLUMN_LEFT . ' > ?', $parent[Cons::COLUMN_LEFT])
205:             ->where(Cons::COLUMN_RIGHT . ' < ?', $parent[Cons::COLUMN_RIGHT])
206:             ->order(Cons::COLUMN_LEFT);
207:     }
208: 
209:     /**
210:      * Vrací první nalezený uzel Stromu, který je bez rodiče. 
211:      * V případě, že neexistuje bude vytvořen
212:      * @return \Nette\Database\Table\ActiveRow  
213:      */
214:     protected function getMasterNode() {
215:     return ($this->getCopy()->where(Cons::COLUMN_PARENT, null)->fetch())? : parent::insert(array(Cons::COLUMN_NAME => Cons::ROOT));
216:     }
217: 
218: }
219: 
sberp API API documentation generated by ApiGen