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