1<?php 2 3namespace dokuwiki\plugin\struct\meta; 4 5use dokuwiki\Utf8\Sort; 6 7/** 8 * Object to create a tree of values 9 * 10 * You should not create these yourself, but use the NestedResult class instead 11 */ 12class NestedValue 13{ 14 /** @var Value */ 15 protected $value; 16 17 /** @var NestedValue[] */ 18 protected $children = []; 19 20 /** @var Value[][] */ 21 protected $resultRows = []; 22 23 /** @var int the nesting depth */ 24 protected $depth; 25 26 /** 27 * Create a nested version of the given value 28 * 29 * @param Value|null $value The value to store, null for root node 30 * @param int $depth The depth of this node (avoids collision where the same values are selected on multiple levels) 31 */ 32 public function __construct(?Value $value, $depth = 0) 33 { 34 $this->value = $value; 35 $this->depth = $depth; 36 } 37 38 /** 39 * @return int 40 */ 41 public function getDepth() 42 { 43 return $this->depth; 44 } 45 46 /** 47 * @param int $depth 48 */ 49 public function setDepth($depth) 50 { 51 $this->depth = $depth; 52 foreach ($this->children as $child) { 53 $child->setDepth($depth + 1); 54 } 55 } 56 57 /** 58 * Access the stored value 59 * 60 * @return Value|null the value stored in this node, null for root node 61 */ 62 public function getValueObject() 63 { 64 return $this->value; 65 } 66 67 /** 68 * Add a child node 69 * 70 * Nodes with the same key (__toString()) will be overwritten 71 * 72 * @param NestedValue $child 73 * @return void 74 */ 75 public function addChild(NestedValue $child) 76 { 77 $this->children[(string)$child] = $child; // ensures uniqueness 78 } 79 80 /** 81 * Get all child nodes 82 * 83 * @return NestedValue[] 84 */ 85 public function getChildren() 86 { 87 $children = $this->children; 88 usort($children, [$this, 'sortChildren']); 89 return $children; 90 } 91 92 /** 93 * Add a result row to this node 94 * 95 * Only unique rows will be stored, duplicates are detected by hashing the row values' toString result 96 * 97 * @param Value[] $row 98 * @return void 99 */ 100 public function addResultRow($row) 101 { 102 // only add unique rows 103 $ident = md5(array_reduce($row, function ($carry, $value) { 104 return $carry . $value; 105 }, '')); 106 107 $this->resultRows[$ident] = $row; 108 } 109 110 /** 111 * Get all result rows stored in this node 112 * 113 * @return Value[][] 114 */ 115 public function getResultRows() 116 { 117 return array_values($this->resultRows); 118 } 119 120 /** 121 * Get a unique key for this node 122 * 123 * @return string 124 */ 125 public function __toString() 126 { 127 if ($this->value === null) return ''; // root node 128 return $this->value->__toString() . '-' . $this->depth; 129 } 130 131 /** 132 * Custom comparator to sort the children of this node 133 * 134 * @param NestedValue $a 135 * @param NestedValue $b 136 * @return int 137 */ 138 public function sortChildren(NestedValue $a, NestedValue $b) 139 { 140 // note: the way NestedResults build the NestedValues, the value object should 141 // always contain a single value only. But since the associated column is still 142 // a multi-value column, getCompareValue() will still return an array. 143 // So here we treat all returns as array and join them with a dash (even though 144 // there should never be more than one value in there) 145 return Sort::strcmp( 146 join('-', (array)$a->getValueObject()->getCompareValue()), 147 join('-', (array)$b->getValueObject()->getCompareValue()) 148 ); 149 } 150 151 /** 152 * print the tree for debugging 153 * 154 * @return string 155 */ 156 public function dump() 157 { 158 $return = ''; 159 160 if ($this->value) { 161 $return .= str_pad('', $this->getDepth() * 4, ' '); 162 $return .= join(', ', (array)$this->value->getDisplayValue()); 163 $return .= "\n"; 164 } else { 165 $return .= "*\n"; 166 } 167 168 foreach ($this->getResultRows() as $row) { 169 $return .= str_pad('', $this->getDepth() * 4, ' '); 170 foreach ($row as $value) { 171 $return .= ' ' . join(', ', (array)$value->getDisplayValue()); 172 } 173 $return .= "\n"; 174 } 175 176 foreach ($this->getChildren() as $child) { 177 $return .= $child->dump(); 178 } 179 180 return $return; 181 } 182} 183