15bc00e11SAndreas Gohr<?php 25bc00e11SAndreas Gohr 35bc00e11SAndreas Gohrnamespace dokuwiki\plugin\struct\meta; 45bc00e11SAndreas Gohr 5*ce44c639SAndreas Gohruse dokuwiki\plugin\struct\types\Text; 6*ce44c639SAndreas Gohruse dokuwiki\Utf8\PhpString; 7*ce44c639SAndreas Gohr 85bc00e11SAndreas Gohr/** 95bc00e11SAndreas Gohr * This class builds a nested tree from a search result 105bc00e11SAndreas Gohr * 115bc00e11SAndreas Gohr * This is used to create the nested output in the AggregationList 125bc00e11SAndreas Gohr */ 135bc00e11SAndreas Gohrclass NestedResult 145bc00e11SAndreas Gohr{ 155bc00e11SAndreas Gohr 165bc00e11SAndreas Gohr /** @var NestedValue[] */ 175bc00e11SAndreas Gohr protected $nodes = []; 185bc00e11SAndreas Gohr 19*ce44c639SAndreas Gohr /** @var NestedValue[] */ 20*ce44c639SAndreas Gohr protected $indexNodes = []; 21*ce44c639SAndreas Gohr 225bc00e11SAndreas Gohr /** @var Value[][] */ 235bc00e11SAndreas Gohr protected $result; 245bc00e11SAndreas Gohr 255bc00e11SAndreas Gohr /** 265bc00e11SAndreas Gohr * @param Value[][] $result the original search result 275bc00e11SAndreas Gohr * @return void 285bc00e11SAndreas Gohr */ 295bc00e11SAndreas Gohr public function __construct($result) 305bc00e11SAndreas Gohr { 315bc00e11SAndreas Gohr $this->result = $result; 325bc00e11SAndreas Gohr } 335bc00e11SAndreas Gohr 345bc00e11SAndreas Gohr /** 355bc00e11SAndreas Gohr * Get the nested result 365bc00e11SAndreas Gohr * 375bc00e11SAndreas Gohr * @param int $nesting the nesting level to use 38*ce44c639SAndreas Gohr * @param int $index the number of characters to use for indexing 395bc00e11SAndreas Gohr * @return NestedValue the root node of the nested tree 405bc00e11SAndreas Gohr */ 41*ce44c639SAndreas Gohr public function getRoot($nesting, $index = 0) 42*ce44c639SAndreas Gohr { 435bc00e11SAndreas Gohr $this->nodes = []; 445bc00e11SAndreas Gohr $root = new NestedValue(null, -1); 455bc00e11SAndreas Gohr 465bc00e11SAndreas Gohr if (!$this->result) return $root; 47*ce44c639SAndreas Gohr 485bc00e11SAndreas Gohr foreach ($this->result as $row) { 495bc00e11SAndreas Gohr $this->nestBranch($root, $row, $nesting); 505bc00e11SAndreas Gohr } 515bc00e11SAndreas Gohr 52*ce44c639SAndreas Gohr $root = $this->createIndex($root, $index); 535bc00e11SAndreas Gohr return $root; 545bc00e11SAndreas Gohr } 555bc00e11SAndreas Gohr 565bc00e11SAndreas Gohr /** 57*ce44c639SAndreas Gohr * Add a top level index to the tree 58*ce44c639SAndreas Gohr * 59*ce44c639SAndreas Gohr * @param NestedValue $root Root node of the tree 60*ce44c639SAndreas Gohr * @param int $index Number of characters to use for indexing 61*ce44c639SAndreas Gohr * @return NestedValue new root node 62*ce44c639SAndreas Gohr */ 63*ce44c639SAndreas Gohr protected function createIndex(NestedValue $root, $index) 64*ce44c639SAndreas Gohr { 65*ce44c639SAndreas Gohr if (!$index) return $root; 66*ce44c639SAndreas Gohr $this->indexNodes = []; 67*ce44c639SAndreas Gohr 68*ce44c639SAndreas Gohr $children = $root->getChildren(); 69*ce44c639SAndreas Gohr $resultRows = $root->getResultRows(); 70*ce44c639SAndreas Gohr if ($children) { 71*ce44c639SAndreas Gohr // there are children, so we are a nested result 72*ce44c639SAndreas Gohr foreach ($children as $child) { 73*ce44c639SAndreas Gohr $indexValue = $child->getValueObject(); 74*ce44c639SAndreas Gohr $indexNode = $this->getIndexNode($indexValue, $index); 75*ce44c639SAndreas Gohr $indexNode->addChild($child); 76*ce44c639SAndreas Gohr $child->setDepth(1); // increase child's depth from 0 to 1 77*ce44c639SAndreas Gohr } 78*ce44c639SAndreas Gohr } elseif ($resultRows) { 79*ce44c639SAndreas Gohr // no children, so we are currently a flat result 80*ce44c639SAndreas Gohr foreach ($resultRows as $row) { 81*ce44c639SAndreas Gohr $indexValue = $row[0]; 82*ce44c639SAndreas Gohr $indexNode = $this->getIndexNode($indexValue, $index); 83*ce44c639SAndreas Gohr $indexNode->addResultRow($row); 84*ce44c639SAndreas Gohr } 85*ce44c639SAndreas Gohr } 86*ce44c639SAndreas Gohr 87*ce44c639SAndreas Gohr // now all results are added to index nodes - use them as children 88*ce44c639SAndreas Gohr $newRoot = new NestedValue(null, -1); 89*ce44c639SAndreas Gohr foreach ($this->indexNodes as $node) { 90*ce44c639SAndreas Gohr $newRoot->addChild($node); 91*ce44c639SAndreas Gohr } 92*ce44c639SAndreas Gohr return $newRoot; 93*ce44c639SAndreas Gohr } 94*ce44c639SAndreas Gohr 95*ce44c639SAndreas Gohr /** 965bc00e11SAndreas Gohr * Creates nested nodes for a given result row 975bc00e11SAndreas Gohr * 985bc00e11SAndreas Gohr * Splits up multi Values into separate nodes, when used in nesting 995bc00e11SAndreas Gohr * 1005bc00e11SAndreas Gohr * @param Value[] $row current result row to work on 1015bc00e11SAndreas Gohr * @param int $nesting number of wanted nesting levels 1025bc00e11SAndreas Gohr * @param int $depth current nesting depth (used in recursion) 1035bc00e11SAndreas Gohr */ 1045bc00e11SAndreas Gohr protected function nestBranch(NestedValue $parent, $row, $nesting, $depth = 0) 1055bc00e11SAndreas Gohr { 1065bc00e11SAndreas Gohr // nesting level reached, add row and return 1075bc00e11SAndreas Gohr if ($depth >= $nesting) { 1085bc00e11SAndreas Gohr $parent->addResultRow($row); 1095bc00e11SAndreas Gohr return; 1105bc00e11SAndreas Gohr } 1115bc00e11SAndreas Gohr 1125bc00e11SAndreas Gohr $valObj = array_shift($row); 1135bc00e11SAndreas Gohr if (!$valObj) return; // no more values to nest, usually shouldn't happen 1145bc00e11SAndreas Gohr 1155bc00e11SAndreas Gohr if ($valObj->getColumn()->isMulti()) { 1165bc00e11SAndreas Gohr // split up multi values into separate nodes 1175bc00e11SAndreas Gohr $values = $valObj->getValue(); 1185bc00e11SAndreas Gohr foreach ($values as $value) { 1195bc00e11SAndreas Gohr $newValue = new Value($valObj->getColumn(), $value); 1205bc00e11SAndreas Gohr $node = $this->getNodeForValue($newValue, $depth); 1215bc00e11SAndreas Gohr $parent->addChild($node); 1225bc00e11SAndreas Gohr $this->nestBranch($node, $row, $nesting, $depth + 1); 1235bc00e11SAndreas Gohr } 1245bc00e11SAndreas Gohr } else { 1255bc00e11SAndreas Gohr $node = $this->getNodeForValue($valObj, $depth); 1265bc00e11SAndreas Gohr $parent->addChild($node); 1275bc00e11SAndreas Gohr $this->nestBranch($node, $row, $nesting, $depth + 1); 1285bc00e11SAndreas Gohr 1295bc00e11SAndreas Gohr } 1305bc00e11SAndreas Gohr } 1315bc00e11SAndreas Gohr 1325bc00e11SAndreas Gohr /** 1335bc00e11SAndreas Gohr * Create or get existing Node from the tree 1345bc00e11SAndreas Gohr * 1355bc00e11SAndreas Gohr * @param Value $value 1365bc00e11SAndreas Gohr * @param int $depth 1375bc00e11SAndreas Gohr * @return NestedValue 1385bc00e11SAndreas Gohr */ 1395bc00e11SAndreas Gohr protected function getNodeForValue(Value $value, $depth) 1405bc00e11SAndreas Gohr { 1415bc00e11SAndreas Gohr $node = new NestedValue($value, $depth); 1425bc00e11SAndreas Gohr $key = (string)$node; 1435bc00e11SAndreas Gohr if (!isset($this->nodes[$key])) { 1445bc00e11SAndreas Gohr $this->nodes[$key] = $node; 1455bc00e11SAndreas Gohr } 1465bc00e11SAndreas Gohr return $this->nodes[$key]; 1475bc00e11SAndreas Gohr } 148*ce44c639SAndreas Gohr 149*ce44c639SAndreas Gohr /** 150*ce44c639SAndreas Gohr * Create or get an existing Node for indexing 151*ce44c639SAndreas Gohr * 152*ce44c639SAndreas Gohr * @param Value $value 153*ce44c639SAndreas Gohr * @param int $index 154*ce44c639SAndreas Gohr * @return NestedValue 155*ce44c639SAndreas Gohr */ 156*ce44c639SAndreas Gohr protected function getIndexNode(Value $value, $index) 157*ce44c639SAndreas Gohr { 158*ce44c639SAndreas Gohr $compare = $value->getCompareValue(); 159*ce44c639SAndreas Gohr if (is_array($compare)) $compare = $compare[0]; 160*ce44c639SAndreas Gohr $key = PhpString::strtoupper(PhpString::substr($compare, 0, $index)); 161*ce44c639SAndreas Gohr 162*ce44c639SAndreas Gohr if (!isset($this->indexNodes[$key])) { 163*ce44c639SAndreas Gohr $col = new Column( 164*ce44c639SAndreas Gohr 0, 165*ce44c639SAndreas Gohr new Text([], '%index%', false), 166*ce44c639SAndreas Gohr -1, 167*ce44c639SAndreas Gohr true, 168*ce44c639SAndreas Gohr $value->getColumn()->getTable() 169*ce44c639SAndreas Gohr ); 170*ce44c639SAndreas Gohr $this->indexNodes[$key] = new NestedValue(new Value($col, $key), 0); 171*ce44c639SAndreas Gohr } 172*ce44c639SAndreas Gohr 173*ce44c639SAndreas Gohr return $this->indexNodes[$key]; 174*ce44c639SAndreas Gohr } 1755bc00e11SAndreas Gohr} 1765bc00e11SAndreas Gohr 1775bc00e11SAndreas Gohr 178