Merge branch '3.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / data / category / CategoryNodeTree.class.php
CommitLineData
796fdcf2
MS
1<?php
2namespace wcf\data\category;
3use wcf\system\category\CategoryHandler;
4use wcf\system\exception\SystemException;
5
6/**
7 * Represents a tree of category nodes.
8 *
9 * @author Matthias Schmidt
c839bd49 10 * @copyright 2001-2018 WoltLab GmbH
796fdcf2 11 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
e71525e4 12 * @package WoltLabSuite\Core\Data\Category
796fdcf2
MS
13 */
14class CategoryNodeTree implements \IteratorAggregate {
83aa1dc1 15 /**
b2aa772d 16 * list of ids of categories which will not be included in the node tree
83aa1dc1
MS
17 * @var integer[]
18 */
19 protected $excludedCategoryIDs = [];
20
21 /**
22 * if true, disabled categories are also included in the node tree
23 * @var boolean
24 */
25 protected $includeDisabledCategories = false;
26
dcf94cd3 27 /**
734662c9 28 * maximum depth considered when building the node tree
893aace3 29 * @var integer
dcf94cd3
AE
30 */
31 protected $maxDepth = -1;
32
796fdcf2
MS
33 /**
34 * name of the category node class
35 * @var string
36 */
734662c9 37 protected $nodeClassName = CategoryNode::class;
796fdcf2
MS
38
39 /**
40 * id of the parent category
41 * @var integer
42 */
43 protected $parentCategoryID = 0;
44
45 /**
46 * parent category node
734662c9 47 * @var CategoryNode
796fdcf2
MS
48 */
49 protected $parentNode = null;
50
734662c9
MS
51 /**
52 * name of the category object type
53 * @var string
54 */
55 protected $objectType = '';
56
796fdcf2
MS
57 /**
58 * Creates a new instance of CategoryNodeTree.
59 *
60 * @param string $objectType
61 * @param integer $parentCategoryID
62 * @param boolean $includeDisabledCategories
734662c9 63 * @param integer[] $excludedCategoryIDs
2b770bdd 64 * @throws SystemException
796fdcf2 65 */
734662c9 66 public function __construct($objectType, $parentCategoryID = 0, $includeDisabledCategories = false, array $excludedCategoryIDs = []) {
796fdcf2
MS
67 $this->objectType = $objectType;
68 $this->parentCategoryID = $parentCategoryID;
69 $this->includeDisabledCategories = $includeDisabledCategories;
70 $this->excludedCategoryIDs = $excludedCategoryIDs;
71
72 // validate category object type
73 if (CategoryHandler::getInstance()->getObjectTypeByName($this->objectType) === null) {
74 throw new SystemException("Unknown category object type '".$this->objectType."'");
75 }
76 }
77
dcf94cd3
AE
78 /**
79 * Sets the maximum depth considered when building the node tree, defaults
80 * to -1 which equals infinite.
81 *
82 * @param integer $maxDepth
83 */
84 public function setMaxDepth($maxDepth) {
85 $this->maxDepth = $maxDepth;
86 }
87
796fdcf2
MS
88 /**
89 * Builds the category node tree.
90 */
91 protected function buildTree() {
92 $this->parentNode = $this->getNode($this->parentCategoryID);
dcf94cd3 93 $this->buildTreeLevel($this->parentNode, $this->maxDepth);
796fdcf2
MS
94 }
95
96 /**
97 * Builds a certain level of the tree.
98 *
734662c9
MS
99 * @param CategoryNode $parentNode
100 * @param integer $depth
796fdcf2 101 */
dcf94cd3
AE
102 protected function buildTreeLevel(CategoryNode $parentNode, $depth = 0) {
103 if ($this->maxDepth != -1 && $depth < 0) {
104 return;
105 }
106
796fdcf2
MS
107 foreach ($this->getChildCategories($parentNode) as $childCategory) {
108 $childNode = $this->getNode($childCategory->categoryID);
109
110 if ($this->isIncluded($childNode)) {
111 $parentNode->addChild($childNode);
112
113 // build next level
11fa0d33 114 $this->buildTreeLevel($childNode, $depth - 1);
796fdcf2
MS
115 }
116 }
117 }
118
119 /**
120 * Returns the category with the given id.
121 *
122 * @param integer $categoryID
734662c9 123 * @return Category
796fdcf2
MS
124 */
125 protected function getCategory($categoryID) {
126 return CategoryHandler::getInstance()->getCategory($categoryID);
127 }
128
129 /**
130 * Returns the child categories of the given category node.
131 *
734662c9
MS
132 * @param CategoryNode $parentNode
133 * @return Category[]
796fdcf2
MS
134 */
135 protected function getChildCategories(CategoryNode $parentNode) {
136 return CategoryHandler::getInstance()->getChildCategories($parentNode->categoryID, $parentNode->objectTypeID);
137 }
138
139 /**
734662c9 140 * @inheritDoc
796fdcf2
MS
141 */
142 public function getIterator() {
143 if ($this->parentNode === null) {
144 $this->buildTree();
145 }
146
147 return new \RecursiveIteratorIterator($this->parentNode, \RecursiveIteratorIterator::SELF_FIRST);
148 }
149
150 /**
151 * Returns the category node for the category with the given id.
152 *
153 * @param integer $categoryID
734662c9 154 * @return CategoryNode
796fdcf2
MS
155 */
156 protected function getNode($categoryID) {
157 if (!$categoryID) {
734662c9 158 $category = new Category(null, [
796fdcf2
MS
159 'categoryID' => 0,
160 'objectTypeID' => CategoryHandler::getInstance()->getObjectTypeByName($this->objectType)->objectTypeID
734662c9 161 ]);
796fdcf2
MS
162 }
163 else {
164 $category = $this->getCategory($categoryID);
165 }
166
167 // decorate category if necessary
734662c9 168 $decoratorClassName = call_user_func([$this->nodeClassName, 'getBaseClass']);
157054c9 169 if ($decoratorClassName != Category::class) {
796fdcf2
MS
170 $category = new $decoratorClassName($category);
171 }
172
173 return new $this->nodeClassName($category);
174 }
175
176 /**
177 * Returns true if the given category node fulfils all relevant conditions
178 * to be included in this tree.
179 *
734662c9 180 * @param CategoryNode $categoryNode
796fdcf2
MS
181 * @return boolean
182 */
183 protected function isIncluded(CategoryNode $categoryNode) {
184 return (!$categoryNode->isDisabled || $this->includeDisabledCategories) && !in_array($categoryNode->categoryID, $this->excludedCategoryIDs);
185 }
186}